裸库和非裸库有什么实际区别?

277
我一直在阅读Git中裸库和默认库的内容。我还不能理解它们之间的区别,以及为什么应该向裸库“push”。问题如下: 目前,我是一个项目的唯一成员,但稍后将有更多人参与其中,因此我正在使用Git进行版本控制。我在所有计算机上克隆裸库,当我完成其中一台计算机上的修改时,我会提交并将更改“push”到裸库。据我所知,裸存储库没有“工作树”,因此如果我克隆裸存储库,我将没有“工作树”。
我猜“工作树”存储项目的提交信息、分支等。这些不会出现在裸存储库中。因此,似乎最好将提交推送到具有“工作树”的存储库。
那么,为什么要使用裸存储库,而不使用默认存储库?实际的区别是什么?这对于更多参与项目的人来说可能没有好处。
您对此类工作的方法是什么?有什么建议吗?

4
AeroCross,您可以克隆一个裸仓库来创建一个非裸仓库(即具有工作区的仓库)。因此,使用 git clone 命令,您可以自由地在裸仓库和非裸仓库之间进行转换。 - Derek Mahar
18
@AeroCross的意思是:这不是关于转换;无论另一端是什么都无所谓。如果你运行 git clone --bare,你将得到一个裸库(bare repo),如果你运行 git clone,你将得到一个非裸库(non-bare one)。你曾经克隆过的每个公共项目(例如在Github上托管的)在另一端都是一个裸库(bare repository)。 - Cascabel
1
Jefromi,我正在更正AeroCross的观点,“所以如果我克隆裸仓库,我就没有'工作树'”,因此这是一种转换。并不是每个公共项目都必须是一个裸仓库。这只是典型的选择,因为裸仓库更节省空间,因为它没有工作树(尽管它与任何没有工作树的仓库一样节省空间)。 - Derek Mahar
3
@Derek:但是重点是,一旦它找到了.git目录,获取操作完全不知道远程库是否为裸库。它不会进行转换。它只是从远程库中获取所需内容,并将其放在应该放置的位置。没有什么需要转换的。这就是我试图向OP强调的。我很清楚公共项目不必是裸库,但因为人们并不愚蠢,它们基本上都是。我认为我做出了一个可以接受的概括。 - Cascabel
2
请参考向非裸仓库推送的内容,该链接提供了关于裸仓库使用的另一个优秀解释。 - Craig McQueen
显示剩余4条评论
11个回答

120

裸库和非裸库之间的另一个区别是,裸库没有默认远程存储库origin

~/Projects$ git clone --bare test bare
Initialized empty Git repository in /home/derek/Projects/bare/
~/Projects$ cd bare
~/Projects/bare$ git branch -a
* master
~/Projects/bare$ cd ..
~/Projects$ git clone test non-bare
Initialized empty Git repository in /home/derek/Projects/non-bare/.git/
~/Projects$ cd non-bare
~/Projects/non-bare$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

根据git clone --bare的手册页面:

而且,远程仓库的分支头直接复制到相应的本地分支头,而不映射到refs/remotes/origin/。使用此选项时,既不创建remote-tracking分支,也不创建相关的配置变量。

假设创建裸仓库时,Git会认为裸仓库将作为多个远程用户的起源存储库,因此它不会创建默认的远程origin。这意味着基本的git pullgit push操作将无法工作,因为Git认为在没有工作区的情况下,您不打算向裸仓库提交任何更改:

~/Projects/bare$ git push
fatal: No destination configured to push to.
~/Projects/bare$ git pull
fatal: /usr/lib/git-core/git-pull cannot be used without a working tree.
~/Projects/bare$ 

3
是的,我同意这可能是裸仓库和非裸仓库之间最重要的区别。事实上,非裸仓库有一个工作区,而裸仓库没有,这是一个重要的区别,但git clone --no-checkout也可以创建一个没有工作区文件的非裸仓库。 - Derek Mahar
14
非裸仓库不一定有默认的远程仓库"origin"。 - mipadi
3
你可以通过 git init 命令简单地创建一个 Git 仓库,这将创建一个未指定远程的非裸仓库。 - mipadi
3
mipadi,这是真的,但它也不是克隆。 - Derek Mahar
3
这是错误的。如果一个仓库被 clone 了,它将会有一个默认的远程源。如果它在本地被 init 了,它就没有这样的远程源。这与它是否为裸仓库无关。 - Noufal Ibrahim
显示剩余10条评论

101

我知道已经晚了5年,但是没有人真正回答这个问题:

那么,为什么应该使用裸库,为什么不使用?有什么实际区别吗?我想这对更多在一个项目上工作的人来说并不会有益处。

你们处理这种工作的方法是什么?有什么建议吗?

引用Loeliger/MCullough书(ISBN 978-1-449-31638-9,第196/7页)的话:

裸库似乎没有什么用处,但其角色却至关重要:作为协作开发的权威焦点。其他开发者从裸库中克隆和提取(fetch),并将更新推送(push)到其中...如果您设置了一个开发者push更改的库,它应该是裸库。实际上,这是更一般的最佳实践的特例,即公开的存储库应该是裸的。


简而言之(以外行的方式解释):“bare”意味着你可以使用push命令,但不能在仓库内部进行开发,也就是说,你必须使用git clone命令。而非bare则相反,即在仓库文件夹内有一个工作区,你可以在其中编辑和提交所有文件,但不能从外部推送任何内容进来。 - undefined

89
区分裸仓库和非裸仓库是不合理且误导性的,因为工作区不是仓库的一部分,而且一个仓库不需要工作区。严格来说,Git仓库包括描述仓库状态的对象。这些对象可以存在于任何目录中,但通常存在于工作区顶级目录下的.git目录中。工作区是表示存储库中特定提交的目录树,但它可以存在于任何目录中或根本不存在。环境变量$GIT_DIR将工作区链接到其来源的存储库。
Git命令git clonegit init都有选项--bare,可创建没有初始工作区的仓库。Git混淆了工作区和仓库两个不同但相关的概念,然后使用令人困惑的术语“裸仓库”来区分这两个概念。

3
你提出了一个非常好的观点。是否可以这样说:“非裸的 Git 存储库具有补充工作区目录树(但裸的 Git 存储库没有)”? - crazyTech

80

裸仓库指的是 .git 文件夹本身,即裸仓库的内容与本地工作仓库中 .git 文件夹的内容相同。

  • 在远程服务器上使用裸仓库可以允许多个贡献者推送他们的工作。
  • 非裸仓库(有工作树)适用于项目每个贡献者的本地机器。

2
多个贡献者不能同时推送他们的工作吗?没有必要使用裸仓库。使用普通仓库,很多人也可以并行工作。 - Mugen
1
@Mugen,如果你使用非裸仓库,在本地尝试推送到检出的分支时会收到错误消息。 - Andrew Anderson
1
@Deepak,那么“裸仓库”这个术语的意义是什么呢?为什么不直接称之为普通的一切但是没有 ".git" 文件夹的东西?或者是普通东西的 ".git" 子文件夹? - Pacerier

46
一个默认/非裸的 Git 仓库包含两个状态:
  1. 所有文件在仓库中的快照(这就是Git术语中的“工作树”)
  2. 所有曾经在仓库中的文件所做的所有更改的历史记录(似乎没有一个简明扼要的 Git 术语来概括所有内容)
快照是你可能认为是你的项目的内容:你的代码文件、构建文件、辅助脚本和任何你用 Git 版本控制的其他东西。 历史记录是一种状态,它允许你检出不同的提交并获得完整的快照,以了解当该提交被添加时,仓库中的文件看起来像什么。它由一堆对 Git 内部的数据结构组成,你可能从未直接与其交互过。重要的是,历史记录不仅存储元数据(例如“用户 U 在时间 T 作为提交 C 的一部分添加了这么多行到文件 F 中”),还存储数据(例如“用户 U 添加了 这些确切的行 到文件 F”)。
裸仓库的关键思想是你实际上不需要快照。Git 保留快照是因为它对人类和其他不是 Git 进程的进程与你的代码交互很方便,但快照只是重复了已经在历史记录中的状态。 裸仓库 是一个没有快照的 Git 仓库。它只保存着历史记录。
为什么要这样做?如果你只打算使用 Git 与你的文件交互(也就是说,你不会直接编辑你的文件或使用它们来构建可执行文件),你可以通过不保留快照来节省空间。特别地,如果你在某个服务器上维护一个集中式版本的仓库(也就是说,你基本上在托管自己的 GitHub),那么该服务器应该有一个裸仓库(你仍然应该在本地机器上使用非裸仓库,因为你可能想编辑你的快照)。
如果你想要更深入地了解裸仓库以及另一个示例用例,请参阅我在此处编写的博客文章:https://stegosaurusdormant.com/bare-git-repo/

2
“非裸”仓库还包含Git中用于持续开发所需的暂存区,详情请参阅https://dev59.com/wFUM5IYBdhLWcg3wKNuB。 - qneill
4
这个答案在理解差异方面最有用。谢谢。 - mynameistechno

22

非裸仓库只是一个已检出的工作树。工作树不存储有关仓库状态(分支、标签等)的任何信息;相反,工作树只是实际存储在仓库中文件的表示,它允许您对文件进行操作(编辑等)。


那就意味着我可以在裸仓库中添加分支、标签等,然后从裸仓库拉取到非裸 / 生产仓库中吗? - AeroCross
3
mipadi,非裸库可能没有检出的工作树。如果您使用 git clone --no-checkout 创建非裸库,则会出现这种情况。在这种情况下,非裸库有一个工作区位置,但是Git不会将任何文件检出到该工作区。 - Derek Mahar

21

裸仓库有以下优点:

  • 减少磁盘空间的使用
  • 远程推送相关问题较少(因为没有工作树来使得同步出现问题或者产生冲突)

3
那么裸仓库是与多人合作且无法访问其仓库的最佳/推荐方式吗?(有点像 SVN 吗?) - AeroCross
2
AeroCross,我认为裸仓库是这种情况的不错选择。 - Derek Mahar
我认为“n个提交领先”消息与分支(即引用)有关,而不是与工作树相关。 - aderchox
@aderchox 是的,这里没有任何与此相矛盾的内容吗? - sehe
我的意思是,如果开发人员向远程存储库推送更改,那么所有其他贡献者无论远程存储库是裸库还是非裸库,都将不同步。也许我没有完全理解您回答中“同步”的含义。 - aderchox
1
如果远程仓库不是裸仓库,它会导致工作树与已检出的 HEAD 不同步。这就是为什么不允许这样做的原因,因此拥有一个裸仓库的优势就在于它没有工作树。 - sehe

17

非裸库允许您(进入您的工作树)通过创建新的提交来捕获更改。

裸库仅通过从其他存储库传输更改来进行更改。


这是有道理的,因为开发人员的工作站将拥有一个非裸库,并且更改会被推送到服务器上的裸库中。 - Clomp

14

我并不是Git的“专家”。 我已经使用TortoiseGit一段时间了,每当我创建一个时,就会问我是否要创建一个“bare” repo。我正在阅读这个教程:https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-init,它解决了这个问题,但我仍然不太理解概念。这篇文章帮了我很多:http://bitflop.com/tutorials/git-bare-vs-non-bare-repositories.html。现在,第一篇文章也有意义了!

根据这些来源,在简短地说,一个“bare” repo用于在服务器上设置分发点。 它不适用于您的本地计算机。 通常,您将提交从本地计算机推送到远程服务器上的裸库,您和/或其他人从该裸库中拉出到本地计算机。 因此,您的GitHub、Assembla等远程存储/分发repo是创建“bare” repo的示例。 如果您要设置自己的类似“共享中心”,则可以自己创建一个。


注意:2022年后,BitFlop链接已不存在。该域名正在出售中。然而,Atlassian Bitbucket教程仍然是一个有用的链接! - Clomp

4

这并不是一个新的答案,但它帮助我理解以上答案的不同方面(而且对于评论来说太长了)。

只需使用Git Bash尝试:

me@pc MINGW64 /c/Test
$ ls -al
total 16
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:35 ./
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:11 ../

me@pc MINGW64 /c/Test
$ git init
Initialized empty Git repository in C:/Test/.git/

me@pc MINGW64 /c/Test (master)
$ ls -al
total 20
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:35 ./
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:11 ../
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:35 .git/

me@pc MINGW64 /c/Test (master)
$ cd .git

me@pc MINGW64 /c/Test/.git (GIT_DIR!)
$ ls -al
total 15
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:35 ./
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:35 ../
-rw-r--r-- 1 myid 1049089 130 Apr  1 11:35 config
-rw-r--r-- 1 myid 1049089  73 Apr  1 11:35 description
-rw-r--r-- 1 myid 1049089  23 Apr  1 11:35 HEAD
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:35 hooks/
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:35 info/
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:35 objects/
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:35 refs/

git --bare一样:
me@pc MINGW64 /c/Test
$ ls -al
total 16
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:36 ./
drwxr-xr-x 1 myid 1049089 0 Apr  1 11:11 ../

me@pc MINGW64 /c/Test
$ git init --bare
Initialized empty Git repository in C:/Test/

me@pc MINGW64 /c/Test (BARE:master)
$ ls -al
total 23
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:36 ./
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:11 ../
-rw-r--r-- 1 myid 1049089 104 Apr  1 11:36 config
-rw-r--r-- 1 myid 1049089  73 Apr  1 11:36 description
-rw-r--r-- 1 myid 1049089  23 Apr  1 11:36 HEAD
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:36 hooks/
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:36 info/
drwxr-xr-x 1 myid 1049089   0 Apr  1 11:36 objects/

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