git-worktree 和 git-subtree 有什么区别?



就在我以为Git不会变得更复杂的时候,我发现了Git工作树。这要么是子树的同义词,要么是我从来不知道的特性。工作树和子树相同还是不同。如果它们不同,它们有什么不同?工作树解决了什么问题?

这些非常不同。为了正确理解它们,让我们定义工作树(或"工作树"或"工作树木"或这些拼写的几乎任何变体),关于索引提交

您已经知道提交保存快照,并且每个提交都有一个唯一的哈希ID,用于命名一个特定的提交。同一提交可能有许多其他名称(例如分支和/或标记名称),但只有一个哈希ID。您可能还知道提交有元数据:是谁创建的(名称和电子邮件地址)、何时(时间戳)以及为什么(显示git log的消息)。每个提交还有一个散列ID,或者更确切地说,是一个父列表,通常只有一个条目。父级是在此之前的提交,因此Git可以在提交链中向后遍历,以随时间显示内容。(具有两个父哈希ID的提交是合并提交。具有父散列ID的提交为

根提交提交中的所有内容(包括文件)始终处于完全冻结状态。你不能更改其中的任何一个,而不是一位,原因是哈希ID实际上是提交所有内容的加密校验和。如果你只是以某种方式更改了一位,校验和就会不同,所以这将是一个不同的提交,使用不同的哈希ID。

这意味着存储在任何提交中的所有文件都将被冻结。它们还被压缩为一种特殊的仅限Git的格式,只有Git才能读取。这对历史来说很好,但我们将如何完成任何工作?这是工作树进入图片的地方。

要处理文件,我们必须让Git将它们复制到提交的中。这将使文件恢复到日常形式,在那里,所有东西都可以读取它们——编辑器、编译器,以及你电脑上的任何东西——当然,它们是可写/可更改的。您处理/处理文件的地方是工作树

因此,在当前提交(但已选择)和工作树之间,每个文件都有两个副本:提交中的冻结副本和工作树中有用的副本。

Git可以到此为止,而其他版本控制系统,如Mercurial(请参阅Mercurial),也可以做到这一点。但由于各种原因——其中许多原因与"走得很快"有关——Git为每个文件添加了第三个副本。第三个副本进入Git所称的索引暂存区缓存。(你看到的名称取决于谁或Git的哪个部分在调用。)索引中的文件与提交中的文件形式几乎相同,只是在索引中,它们被而不是冻结。如果你愿意的话,它们更容易结冰,或者"泥泞"。

索引还保持工作树上的标签,以便它们紧密配对:索引"知道"工作树中的内容,或者如果它不知道——如果索引的缓存方面已经过时——它知道

,这有助于Git快速发现发生了什么变化。此外,当您运行git commit时,Git甚至不会在工作树上查看(除了在您将为日志消息编辑的文件中添加一些注释)。它只是将现成的文件从索引中冻结出来,索引在这里获得其名称暂存区,以进行新的提交。

最后,当你在Git中进行提交时,你会一直有三个活动副本:

  • HEAD提交副本被冻结并且仅限于Git
  • 索引副本是泥泞的:只有Git,但没有完全冻结。最初它与HEAD副本匹配,但您可以用git add覆盖它
  • 工作树副本是正常的和流动的,你可以用它做任何事情

索引和工作树是成对的。此外,索引在合并冲突期间扮演着一个扩展的角色:它最终持有三个提交的文件副本,这是合并的三个输入。当它处于这种扩展模式时,如果不完成或中止合并,您甚至无法git stash或以其他方式摆脱修改后的索引和工作树状态。

这给我们留下了一个需要解决的问题:如果在处理某个问题的过程中,我们需要非常紧急地修复某个其他分支中的某个bug,该怎么办?我们可以再做一个克隆,这是传统的答案。如果我们没有处于冲突合并的中间,我们可以使用git stash;这是另一个答案。一个不是非常令人满意,另一个如果我们正在进行合并,那就没用了。

因此,输入git worktree add。使用git worktree add,您可以将另一对索引和工作树添加到现有存储库中。有一个非常强的约束(出于良好的实现特定原因):每个添加的工作树都必须在其自己的分支上,否则使用"分离的HEAD"模式。也就是说,如果您的主工作树位于分支feature/short上,则没有添加的工作树可以使用此分支。它们可以使用masterhotfixdevelop,但不能使用feature/short。(或者,他们可以在存储库中的任何地方的任何提交中使用分离的HEAD。)

当您完成任何添加的辅助工作树时,您可以简单地rm -rf它,然后从其他辅助工作树或主工作树中运行git worktree prune,让Git搜索而找不到添加的工作树。这将"解锁"添加的工作树已检出的任何分支。

同时,git subtree命令是一个奇特的shell脚本,可以将现有存储库的某些部分提取到一个新的存储库中,以便在其他地方使用,或者将正在其他地方使用的现有存储库提取出来,并尝试从中带回内容。因此,这是一个存储库到存储库的传输,或者在某些情况下,至少是它的设置。

(RomainValeri也提到了git-merge-subtree合并策略,它与git subtree有点相关,因为它旨在处理合并三个输入中的一个或两个输入中子树的重命名。)

这些概念并不相似,除了听起来相似之外,这种比较似乎很奇怪。

git worktree(doc)是一个合适的git命令(而子树是一个贡献,感谢Chris提供的信息),它基本上可以帮助您管理同一repo上的多个工作树,以及几个额外的子命令(listadd等)。

而子树,除了上述贡献之外,也是可用的合并策略之一。

但正如我所说,这两者并不是特别相关,即使一个可以在多工作树回购的上下文中使用子树合并。。。我想,这不是你问题的一部分。

最新更新