如何将一个裸的git仓库转换为普通仓库(就地转换)?

96

我有一个裸的 git 仓库,但需要通过 ssh 访问和浏览其内容(以类似文件管理器的用户体验方式)。

我认为我可以克隆它:

git clone -l <path_to_bare_repo> <new_normal_repo>

不过,我的代码库大小约为20GB,我没有足够的空间来复制它。有办法能够将裸代码库就地转换为带有工作副本的状态吗?


5
未经测试,但如果您将裸仓库的内容移动到一个名为.git的目录中,并将配置文件中的bare参数设置为false,则它应该表现得像一个普通的代码仓库,您可以通过 git checkout 命令获取文件。 - Noufal Ibrahim
根据你所说的“浏览其内容”的含义,你可能可以使用git showgit cat-file在裸仓库中完成你想要的一切。 - William Pursell
谢谢你的提示,很有用。不过我需要更像文件管理器的体验(已编辑问题)。 - nyi
如果您的文件系统支持硬链接,并且您在同一文件系统中进行克隆,那么使用“clone -l”不会占用更多的磁盘空间,因为它会将所有对象都硬链接起来。但是,正如其他人所指出的那样,您仍需要为检出留出空间。 - Neil Mayhew
1
如果你的仓库占用了20GB的磁盘空间,那么工作树需要多少空间呢?你真的有那么多空间吗? - ADTC
9个回答

140

注意:我在一个非常简单的1-commit仓库上进行了测试。请再次核对,阅读man页面,并且在遵循您在StackOverflow上找到的建议之前一定要备份。 (您有备份,对吗?)

将一个--bare仓库转换为非裸仓库的方法:

  1. 在存储库的顶层中创建一个.git文件夹。
  2. 将存储库管理文件(例如HEAD branches config description hooks info objects refs等)移动到您刚刚创建的.git中。
  3. 运行git config --local --bool core.bare false来将本地git仓库转换为非裸仓库。
  4. (通过Tamás Pap的评论)完成步骤#3后,您将看到您在分支master(或任何您的主分支)上,并且所有文件都已被删除并且删除已被暂存。这是正常的。只需手动检出master或执行git reset --hard,然后就完成了。
  5. (解决Royi报告的问题)编辑.git/config文件,在[remote“origin”]部分的url=<...>后添加一行fetch=+refs/heads/*:refs/remotes/origin/*。否则,git fetch将看不到origin/master和其他原点的分支。

这些步骤与将普通仓库转换为裸仓库的问题相反 - 特别注意此答案,它指出上述步骤(我推测是任意一种方向)与执行git-clone有所不同。不确定这是否对你有关联,但你在问题中提到了git clone


2
我已经做了所有的写作,但是当我推送文件时它们仍然没有显示出来。可能是什么问题(我也将 denyCurrentBranch 切换为 ignore 了)? - Royi
谢谢!有一天我来上班时发现我的主要代码库(我在其他地方有几个工作树)报告自己是“裸”的。所有其他工作树都没问题。不知道如何解决,但找到了这个答案,它非常有效。 - davidbak

21

我有一个稍微不同的情况:

解决方案:

  • 在该内容中克隆一个裸仓库,位于 .git 目录下:
    git clone --bare https://github.com/user/project .git
  • 将其标记为非裸仓库:
    git config --local --bool core.bare false
  • 重置索引(否则,它会认为所有内容都被删除了,因为 .git 裸仓库不包括文件 'index'。)
    git reset HEAD -- .
    这样就可以恢复 .git/index 了。
我已成功将一个裸仓库转换为非裸仓库,同时保留了之前获取的内容。
我多年来一直使用的完整脚本包括以下步骤:
cd /path/to/current/worktree

# That creates a .git directly at the right place
git clone --bare /url/of/repo .git

# restore the link between the local repo and its upstream remote repo
git config --local --bool core.bare false
git config --local remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
git fetch origin
git branch -u origin/master master

# reset the index (not the working tree)
git reset HEAD -- .

但我认为ADTC所提供的accepted solution(添加了helpful git reset step)更简单易懂。

值得注意的是,如果裸仓库是在具有不同行尾的机器上创建的,则即使执行了这些步骤,您仍可能会看到修改后的文件。我相信这是正常的;git 正在尝试修复换行符。不确定原因。 - Translunar
所以你基本上是将 GitHub 存储库克隆为 bare,切换到非 bare,手动将所有文件从“非存储库”放入并重置索引?与一开始就将存储库克隆为非 bare 有什么不同吗?非 Bare 克隆过程将无论如何检出所有文件。如果你真的想要,你只需用来自“非存储库”的文件替换已检出的文件即可。 - ADTC
@ADTC 目标是在工作树中(您知道是您的存储库)获得一个.git子文件夹,最初由归档(非git)制成。我无法检出非裸体存储库,因为我进行检出的文件夹不为空。在子文件夹中执行非裸体git clone --no-checkout将强制我将.git上移一级。执行裸克隆允许我直接创建所需的.git子文件夹。您可以在此处查看脚本:https://github.com/VonC/compileEverything/blob/3fb31b840b6d03e28125ffccb69122c07cd63837/git/update-repo-git.sh#L3-L21 - VonC
“我无法检出非裸仓库,因为我进行检出的文件夹不为空。”你的意思是说你有一个非 Git 工作树,其中包含尚未提交的更改,并且您打算在将其转换为启用 Git 的工作树后进行提交?是的,我猜可以这样做,对于这种用例它是有效的。个人而言,我会克隆到一个空文件夹并首先比较两者(使用外部工具)。但这只是我的想法。 - ADTC
是的,我的意思是由于限制,你才会遇到这种情况。对于没有限制的人来说,他们可以编写一个简单的脚本,如果Git未安装,则安装它,然后以正常方式克隆存储库。因此,基本上当系统限制阻止您采用直接的方式时,您正在使用一种解决方法。 - ADTC
显示剩余16条评论

20

原帖作者的问题是没有足够的空间以简单的方式完成某些事情。对于那些有足够空间的人,答案则要简单得多:

git clone foo.git foo

我想避免这种情况的原因是当存储库变得很大时,git clone有时会失败。我想对裸仓库进行rsync,然后再进行转换。 - Sridhar Sarnobat
@SridharSarnobat,如果您有足够的空间,请考虑执行接受答案中描述的任务:https://dev59.com/9Ggv5IYBdhLWcg3wY_02#10637882 -- 我在这里放置了这个一行答案,以便那些有足够空间的人可以轻松地完成任务。 - sarnold

12
为了简化和合并答案中的信息:
裸仓库与普通的.git文件夹不同有三点差异:
- 配置文件中设置了core.bare为true - 没有索引文件(index file)和工作树(working tree) - 没有为“origin”远程生成默认的refspec
因此,您可以将裸仓库简单地移动到一个新文件夹的.git子文件夹中。
mkdir clone
mv bare.git clone/.git

更改 core.bare:

cd clone
git config --local --bool core.bare false

添加一个默认的来源refspec,使得git fetchgit push选择与通常相同的默认值:

git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'

生成索引文件和工作树:

git checkout master

我建议使用git checkout而不是git reset来生成文件,以防将其意外地输入到错误的位置。


7

cd进入裸库,然后执行以下操作:

  1. 要么:
git config core.bare false
git reset --hard

2. 或
git clone X.git X

(将提供名为X的常规Git代码仓库)

这是最简单的解决方案,我可以确认在2019年它有效(第一种方法)。 - Sridhar Sarnobat
只有一个小提示我忘记了(适用于所有答案):当您第一次推送时,您需要编辑配置文件以设置远程URL。 - Sridhar Sarnobat

6
如果您的磁盘空间不足,通过将工作树转换为普通存储库来扩展工作树将是一个问题,但您可以在不进行转换的情况下浏览裸仓库的内容。使用git cat-file -p <commit-sha>命令查看任何提交所引用的树。使用git cat-file -p <blob-sha>命令查看由blob引用的文件的内容。使用git show <sha>:path命令,其中sha是提交或树,以查看路径下blob的内容。

1
你说得没错,但我需要更方便地浏览它(通过ssh在文件管理器中)。因此,我将不得不接受增加的磁盘空间。 - nyi
1
实际上,这是一个真正的问题(+1)。工作树通常会占用高达一半的磁盘空间;部分原因是git历史记录被积极压缩。 - jpaugh

3
如果你不介意在不同的工作树上工作,那么:
git worktree add ../repo2
cd ..
git status # now works fine

请注意,这不是一个克隆。

这很棒,不确定为什么它没有排名更靠前。 - Nickolai

0

另一个Windows 10案例

在我的情况下,我甚至不会在另一个路径中初始化任何带有工作树的新存储库,以便稍后克隆存储库,因为它不是存储库,而且我无法找到.git文件夹,因为我无法在运行后创建它。

git init

我的问题出在系统环境变量上,那里有一个与我的git相关的不同路径。 我只需要删除那个变量,然后就能运行git init了。

我会附上一张图片让你可以访问到系统环境变量
  1. 打开Windows开始菜单 < Windows键 >
  2. 输入system
  3. 点击系统控制面板 enter image description here
  4. About中找到相关设置下的高级系统设置
  5. 然后单击环境变量... enter image description here
  6. 删除任何与git相关的内容。

0

推送即部署

与其将裸远程转换为标准存储库,不如使用钩子目录中的post-receive脚本将存储库扩展到部署目录。

这里有一个很好的设置推送即部署的示例

为了方便参考,这是来自上面链接的脚本内容示例。它将仅将“master”分支的推送部署到与存储库父目录同级的名为“deploy”的目录中:

#!/usr/bin/env ruby
# post-receive

# 1. Read STDIN (Format: "from_commit to_commit branch_name")
from, to, branch = ARGF.read.split " "

# 2. Only deploy if master branch was pushed
if (branch =~ /master$/) == nil
    puts "Received branch #{branch}, not deploying."
    exit
end

# 3. Copy files to deploy directory
deploy_to_dir = File.expand_path('../deploy')
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f master`
puts "DEPLOY: master(#{to}) copied to '#{deploy_to_dir}'"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接