如何将普通的 Git 存储库转换为裸存储库?

Posted

技术标签:

【中文标题】如何将普通的 Git 存储库转换为裸存储库?【英文标题】:How to convert a normal Git repository to a bare one? 【发布时间】:2011-01-13 01:58:03 【问题描述】:

如何将“普通”Git 存储库转换为裸存储库?

主要区别似乎是:

在普通的 Git 存储库中,存储库中有一个 .git 文件夹,其中包含所有相关数据和构成工作副本的所有其他文件

在裸 Git 存储库中,没有工作副本,并且文件夹(我们称之为 repo.git)包含实际的存储库数据

【问题讨论】:

大概这是一个更短的方法:mv repo/.git repo.git; rm -rf repo 是的,没错。当我写这个问题时,这只是我的历史记录,我在另一分钟执行了。 @eegg 使用&& 而不是; 以防mv 失败! git switch --orphan some_new_name 将删除除.git 文件夹之外的所有文件 【参考方案1】:

简而言之:将repo的内容替换为repo/.git的内容,然后告诉仓库它现在是一个裸仓库。

为此,请执行以下命令:

cd repo
mv .git ../repo.git # renaming just for clarity
cd ..
rm -fr repo
cd repo.git
git config --bool core.bare true

请注意,这与向新位置发送 git clone --bare 不同(见下文)。

【讨论】:

感谢core.bare 的提示。现在,在谷歌搜索此选项后,我可以确认:kernel.org/pub/software/scm/git-core/docs/git-config.html 谢谢!这对我来说似乎更干净: mv repo/.git repo.git && rm -rf repo && cd repo.git && git config --bool core.bare true 这比下面的答案 (git clone --bare /path/to/repo) 要复杂和脆弱得多。 rm 命令可能需要* \.[!.]* 而不是* 才能删除点文件和点目录。 @Ciantic:同样,正如我在上面的评论中已经解释的那样,我的答案是在 3 年前给出的,但 5 个月前有人将问题编辑成完全不同的东西。 此页面上的任何答案都将不再有意义。该命令序列是直接从问题中复制的,我只是添加了最后两行。【参考方案2】:

您的方法看起来可行;裸存储库的文件结构就是 .git 目录中的内容。但我不知道是否有任何文件实际上被改变了,所以如果失败了,你可以这样做

git clone --bare /path/to/repo

您可能需要在不同的目录中执行此操作以避免名称冲突,然后您可以将其移回您想要的位置。您可能需要更改配置文件以指向您的原始存储库所在的位置。

【讨论】:

错了,这个方法不是等价的。进行克隆不会保留配置选项,这对于正确操作(例如使用 git-p4)至关重要。此外,克隆会破坏遥控器,再次使用 git-p4 之类的东西,在克隆时会丢失 p4/master 分支,因此上述方法更可取。 但是您可以通过复制配置文件的各个部分来轻松传输配置选项。我仍然认为这种方法比手动复制和重命名文件更干净。 这在 subversion 迁移后是完美的,因为 git-svn 缺乏对 --bare 的支持。 避免名称冲突---git clone --bare /path/to/repo.git /path/to/newbarerepo.git 只要你在创建后在裸仓库上运行git update-server-info,它就可以工作。【参考方案3】:

我认为以下链接会有所帮助

GitFaq: How do I make existing non-bare repository bare?

$ mv repo/.git repo.git
$ git --git-dir=repo.git config core.bare true
$ rm -rf repo

【讨论】:

是的,这就是我搜索的内容,谢谢!但是,他们的第二个提案(git clone)有上面提到的 nosatalian 的缺点。 您建议的文档继续说,“一种更安全的方法是让 Git 为您处理所有内部设置,方法是这样做...... git clone --bare -l "【参考方案4】:

除非您特别想要或需要在文件系统上调整位,否则创建非裸存储库的裸版本确实非常简单(在此处的其他几篇文章中提到)。它是 git 核心功能的一部分:

git clone --bare existing_repo_path bare_repo_path

【讨论】:

4 个月后,遥远的最佳答案获得 0 票,这有点令人惊讶。不是很好但很危险的“公认答案”有 200 个! 一点也不奇怪——这个答案与最初的问题不符。它不会转换 repo,它会克隆一个 repo,在此过程中丢失信息(例如,远程分支)。 当然它引出了一个问题,为什么 Git 首先是有损的(是的,我知道git clone --mirror),但据我所知,clone的含义> 英文不是“除了这些随机的未指定部分之外的重复”? ...当然 Git 子命令的含义并不总是完全不言自明的,所以我想我做错了 Git 期望英文子命令跟随单词的英文含义。【参考方案5】:

也请考虑使用

git clone --mirror path_to_source_repository path_to_bare_repository

来自documentation:

设置源存储库的镜像。这意味着 --bare。与 --bare 相比,--mirror 不仅将源的本地分支映射到目标的本地分支,它还映射所有 refs(包括远程跟踪分支、注释等)并设置 refspec 配置,以便所有这些 refs被目标存储库中的 git 远程更新覆盖。

【讨论】:

似乎这是执行 OP 想要的最简洁、完整和安全的方式(而不是 clone)。我错过了什么吗? --mirror 是最近添加的吗? @CraigSilver:不,正如我在文档历史中看到的那样,--mirror 可以从 1.7 版本中获得,所以已经很长了。您可以查看here 看完这个答案,有些人可能想看看这个问题:***.com/q/3959924/11942268【参考方案6】:

我只是想推送到网络路径上的存储库,但 git 不会让我这样做,除非该存储库被标记为裸露。 我只需要更改它的配置:

git config --bool core.bare true

除非您想保持文件干净,否则无需摆弄文件。

【讨论】:

这很危险,当您还想在远程仓库的工作树上工作时。很可能,您迟早会因为您的远程工作树和索引与存储库不同步而恢复更改。如果您不确切知道自己在做什么,我不推荐此解决方案。 没错,你不应该在远程裸仓库的树上工作。【参考方案7】:

我已经阅读了答案并且我已经这样做了:

cd repos
mv .git repos.git
cd repos.git
git config --bool core.bare true # from another answer
cd ../
mv repos.git ../
cd ../
rm -rf repos/ # or delete using a file manager if you like

这会将repos/.git 的内容保留为裸露的repos.git

【讨论】:

【参考方案8】:

这是我认为最安全和最简单的方法。这里没有什么不是上面提到的。我只想看到一个显示安全的分步程序的答案。您从要使其裸露的存储库 (repo) 启动一个文件夹。我采用了上面隐含的约定,即裸存储库文件夹具有 .git 扩展名。

(1) Backup, just in case.
    (a) > mkdir backup
    (b) > cd backup
    (c) > git clone ../repo
(2) Make it bare, then move it
    (a) > cd ../repo
    (b) > git config --bool core.bare true
    (c) > mv .git ../repo.git
(3) Confirm the bare repository works (optional, since we have a backup)
    (a) > cd ..
    (b) > mkdir test
    (c) > cd test
    (d) > git clone ../repo.git
(4) Clean up
    (a) > rm -Rf repo
    (b) (optional) > rm -Rf backup/repo
    (c) (optional) > rm -Rf test/repo

【讨论】:

这并不安全,因为您使用 git clone 进行了备份。通过克隆进行备份会导致您丢失一些配置,这在某些情况下可能对存储库至关重要。 注明。我曾经关心的唯一配置是我所有本地存储库的全局配置。【参考方案9】:

简单阅读

Pro Git Book: 4.2 Git on the Server - Getting Git on a Server

归结为

$ git clone --bare my_project my_project.git
Cloning into bare repository 'my_project.git'...
done.

然后把my_project.git放到服务器上

主要是#42试图指出的答案。 Shurely 可以重新发明*** ;-)

【讨论】:

我看不到,这个答案增加了什么,在周围的任何其他答案(包括被否决的)中没有以任意长度讨论过。你能澄清一下吗? (顺便说一句:Pro Git Book 说“这大致相当于……”,这里也已经讨论了确切的粗略等价。)【参考方案10】:

这是一个小 BASH 函数,您可以在基于 UNIX 的系统上将其添加到您的 .bashrc 或 .profile 中。添加后,shell 要么重新启动,要么通过调用 source ~/.profilesource ~/.bashrc 重新加载文件。

function gitToBare() 
  if [ -d ".git" ]; then
    DIR="`pwd`"
    mv .git ..
    rm -fr *
    mv ../.git .
    mv .git/* .
    rmdir .git

    git config --bool core.bare true
    cd ..
    mv "$DIR" "$DIR.git"

    printf "[\x1b[32mSUCCESS\x1b[0m] Git repository converted to "
    printf "bare and renamed to\n  $DIR.git\n"
    cd "$DIR.git"
  else
    printf "[\x1b[31mFAILURE\x1b[0m] Cannot find a .git directory\n"
  fi

一旦在包含 .git 目录的目录中调用,它将进行适当的更改以转换存储库。如果调用时不存在 .git 目录,则会出现 FAILURE 消息,并且不会发生文件系统更改。

【讨论】:

【参考方案11】:

说删除文件和移动 .git 目录的方法并不干净,并且不使用“git”方法做一些应该很简单的事情。这是我发现的将普通 repo 转换为裸 repo 的最干净的方法。

首先将 /path/to/normal/repo 克隆到一个名为 repo.git 的裸仓库中

git clone --bare /path/to/normal/repo

接下来移除指向 /path/to/normal/repo 的原点

cd repo.git
git remote rm origin

最后你可以删除你原来的 repo。您可以在此时将 repo.git 重命名为 repo,但表示 git 存储库的标准约定是 something.git,所以我个人会这样保留。

完成所有这些后,您就可以克隆新的裸仓库(这实际上创建了一个普通仓库,也是您将其从裸仓库转换为普通仓库的方式)

当然,如果您有其他上游,您需要记下它们,并更新您的裸仓库以包含它。但同样,这一切都可以用 git 命令完成。请记住,手册页是您的朋友。

【讨论】:

您是否费心阅读其他答案?特别是@jonescb 和@nosatalian 对它的评论? “git”方法是这样的:“尽可能使用纯文本文件”。您应该开始调查.git 文件夹的内部结构。学习这些零件如何组合在一起具有很高的教育意义。【参考方案12】:

如果您的存储库具有很少的本地签出分支 /refs/heads/* 和很少的远程分支分支 remotes/origin/* 并且如果您想将其转换为所有分支都在 /refs/heads/ 中的 BARE 存储库*

您可以执行以下操作来保存历史记录。

    创建一个裸仓库 cd 到具有本地签出分支和远程分支的本地存储库 git push /path/to/bare/repo +refs/remotes/origin/:refs/heads/

【讨论】:

【参考方案13】:

这是来自gitglossary的裸仓库的定义:

裸存储库通常是一个适当命名的目录,带有 .git 后缀,它没有任何受修订控制的文件的本地签出副本。也就是说,通常存在于隐藏的 .git 子目录中的所有 Git 管理和控制文件都直接存在于 repository.git 目录中,并且没有其他文件存在和检出。公共存储库的发布者通常会提供裸存储库。

我来到这里是因为我在玩“本地存储库”,并且希望能够像远程存储库一样做任何我想做的事情。我只是在玩耍,试图了解 git。我假设这是任何想阅读此答案的人的情况。

我希望获得专家意见或一些具体的反例,但似乎(在翻阅了我找到的一些 git 源代码之后)只需转到文件 .git/config 并设置 core 属性 baretrue,git 会让你远程对存储库做任何你想做的事情。 IE。 .git/config 中应存在以下行:

[core]
    ...
    bare = true
...

(这大概是git config --bool core.bare true这个命令会做的事情,可能建议处理更复杂的情况)

我对这种说法的理由是,在 git 源代码中,似乎有两种不同的方法来测试一个 repo 是否是裸露的。一种是检查全局变量is_bare_repository_cfg。这是在执行的某些设置阶段设置的,反映了在.git/config 文件中找到的值。另一个是函数is_bare_repository()。下面是这个函数的定义:

int is_bare_repository(void)

    /* if core.bare is not 'false', let's see if there is a work tree */
    return is_bare_repository_cfg && !get_git_work_tree();
 

我没有时间也没有专业知识可以绝对自信地说出这一点,但据我所知,如果你在.git/config 中将bare 属性设置为true,这应该总是返回1 .剩下的功能大概是针对以下情况的:

    core.bare 未定义(即既不是真也不是假) 没有工作树(即 .git 子目录是主目录)

稍后我会尝试使用它,但这似乎表明设置 core.bare = true 等同于将 core.bare 从配置文件并正确设置目录。

无论如何,设置 core.bare = true 肯定会让你推到它,但我不确定项目文件的存在是否会导致其他一些操作出错。这很有趣,我认为推送到存储库并查看本地发生的情况很有启发性(即运行 git status 并理解结果)。

【讨论】:

【参考方案14】:

我使用以下脚本读取包含我所有 SVN 存储库列表的文本文件并将它们转换为 GIT,然后使用 git clone --bare 转换为裸 git 存储库

#!/bin/bash
file="list.txt"
while IFS= read -r repo_name
do
 printf '%s\n' "$repo_name"
 sudo git svn clone --shared --preserve-empty-dirs --authors-file=users.txt file:///programs/svn/$repo_name 
 sudo git clone --bare /programs/git/$repo_name $repo_name.git
 sudo chown -R www-data:www-data $repo_name.git
 sudo rm -rf $repo_name
done <"$file"

list.txt 的格式

repo1_name
repo2_name

users.txt 的格式为

(no author) = Prince Rogers &lt;prince.rogers.nelson@payesley.park.org&gt;

www-data 是 Apache Web 服务器用户,需要通过 HTTP 推送更改的权限

【讨论】:

【参考方案15】:

首先,backup 你现有的仓库:

(a)  mkdir backup

(b)  cd backup

(c)  git clone non_bare_repo

其次,运行以下命令:

git clone --bare -l non_bare_repo new_bare_repo

【讨论】:

中间克隆有什么用?【参考方案16】:

添加了 2: 写完答案后意识到如果后面跟着git add *,接受的答案可能会在我的电脑上产生相同的结果。

我的文件从我的工作文件夹中消失了(只有 .git 左边),它又好又紧凑:

git switch --orphan some_new_branch_name 

如果愿意,可以转换为裸:

git config --bool core.bare true

这样可以保留包括远程链接的配置:

$ git config --list
core.repositoryformatversion=0
core.filemode=true
core.bare=true
remote.origin.url=https://github.com/vmatare/thinkfan.git
remote.origin.fetch=+refs/*:refs/*
remote.origin.mirror=true

已添加: 在 cmets 中提到它不会删除“任何被 git 忽略的文件”,在这种情况下,它们需要额外手动删除(或者将 .git 子文件夹的存储库本身移到其他地方)。

注意事项:core.bare true之前的一些操作会导致错误:

$ git fetch --all
Fetching origin
fatal: Refusing to fetch into current branch refs/heads/devel of non-bare repository
error: Could not fetch origin

some_new_branch_name 之后没有在git branch 的输出中列出。为了进一步测试我做了git checkout master我得到了文件,在git branch的输出中没有some_new_branch_name,所以我认为新的orphan分支不会被添加到存储库,除非在那里完成一些工作(和/或提交执行)。

【讨论】:

这是完全不同的东西,与裸存储库或非裸存储库无关。孤立分支只是分支,不与其他分支共享历史。因此,如果您创建一个,Git 将向您显示一个现在为空的工作树。 @Boldewyn,是的,git status 的输出会显示不同的输出,但问题是“裸...没有工作副本”。运行 switch --orphan 会产生该结果。当我发现 QA 我正在寻找在不破坏存储库一致性的情况下删除工作副本文件的方法时,因此出于我的目的,我认为 --orphan 是最好的,它不会导致 cmets 中提到的许多***答案的缺点(例如,如果使用clone,配置会丢失)。会带来什么问题? 好吧,给你一个比较接近的类比。如果我的问题是“如何创建一个空文件夹?”你的回答是“看!如果您运行rm -fr *,您的当前文件夹将为空!会带来什么问题?”。裸存储库的行为不同,尤其是在推送到时。你的回购仍然是非裸露的。例如,Git 仍然会抱怨它在推送到存储库时无法更新存储库的工作副本。不要误会我的意思,孤儿分支是一个很棒的工具。只是为了一个完全不同的问题。 对裸回购的快速解释:saintsjd.com/2011/01/what-is-a-bare-git-repository。孤儿分支用例:shannoncrabill.com/blog/git-orphan-branches 是的,但switch --orphan 仍然是不必要的。您只需在 .git 文件夹中放置一个额外的分支,而无需提交。并且switch --orphan 无论如何都不会触及任何被 git 忽略的文件,因此您无法确定它是否正确删除了旧工作树文件夹的所有内容。听起来像是额外的工作,对我来说有额外的缺点。【参考方案17】:

用于执行上述所有操作的 Oneliner:

for i in `ls -A .`; do if [ $i != ".git" ]; then rm -rf $i; fi; done; mv .git/* .; rm -rf .git; git config --bool core.bare true

(如果发生故障并且您没有备份,请不要怪我:P)

【讨论】:

【参考方案18】:

哇,令人惊讶的是有多少人对此表示赞同,尤其是考虑到似乎没有一个人停下来问这个人为什么要做他正在做的事情。

裸和非裸 git 存储库之间的唯一区别是非裸版本具有工作副本。您需要一个裸仓库的主要原因是,如果您想将其提供给第三方,您实际上无法直接对其进行处理,因此在某些时候您将不得不克隆它立即回到常规工作副本版本。

话虽如此,要转换为裸仓库,您所要做的就是确保没有待处理的提交,然后:

rm -R * && mv .git/* . && rm -R .git

你去吧,裸回购。

【讨论】:

这不会使它足够裸露。试着推进去。你也需要git config core.bare true 我没有投反对票,但我只是想指出,这个答案的第一部分解释了为什么你想要一个裸仓库而不是一个非裸仓库是ok,虽然它没有包含足够的技术细节,并且可能稍微不准确。 但是,对于您答案的第二部分,虽然这些命令确实将您的 repo 设置为裸露的 Anthony is right,但您仍然需要设置 git config core.bare true,就像在 this answer 中一样。

以上是关于如何将普通的 Git 存储库转换为裸存储库?的主要内容,如果未能解决你的问题,请参考以下文章

如何在将一个巨大的存储库拆分为单独的存储库时将 SVN 转换为 GIT?

如何转换 git 存储库中的大量提交 [重复]

如何在 git 存储库中重写提交者名称? [复制]

如何将现有的非空目录转换为 Git 工作目录并将文件推送到远程存储库

将 Mercurial 存储库移动到现有 git 存储库的子目录中

将 SVN 主干中的每个目录转换为不同的 GIT 存储库