我应该如何使用git-worktree?

322
我阅读了 Github有关git-worktree的帖子。他们写道:

假设你正在一个名为feature的Git库分支上工作,此时用户报告说master中存在一个高紧急性的bug。首先,你需要创建一个链接的工作树,并通过新建一个名为hotfix的分支来检出到master分支下[…] 你可以解决这个bug,推送hotfix,并创建一个pull请求。

当我在一个名为feature的分支上工作,而master中又有一个高紧急性的bug被报告时,我通常会隐藏我正在进行的所有工作并创建一个新分支。完成后,我可以继续工作。这是一个非常简单的模型,我已经使用它工作多年了。
另一方面,使用git-worktree也有自己的限制:

例如,不允许在两个链接的工作树中同时检出相同的分支,因为这将允许在一个工作树中提交的更改使另一个工作树失步。

为什么我要为已经解决的问题选择一个更复杂的工作流呢?

有没有关于 git-worktree 的任何事情之前不能做,而且这个全新的、复杂的功能可以证明其存在的合理性?


18
在合并或变基时发生冲突后,无法隐藏未合并的路径。 - chirlu
23
如果你使用的是编译型语言,stash 的意思是当你取消 stash 后需要重新编译所有内容。 - mb14
2
我们有几个基于同一(300 MB)源代码的不同产品,我计划将它们全部合并到一个大型仓库中,并使用工作树在不同文件夹中保持每个产品的检出状态,而不是拥有一堆不同步的巨大克隆。 - endolith
12个回答

333

对我来说,Git工作树是长期以来最大的改进。我从事企业软件开发,在这个领域,保持旧版本(比如3年前发布的版本)是很常见的。当然,你可以为每个版本创建一个分支,这样你就可以轻松地切换到它并修复错误。然而,切换成本很高,因为在此期间,你完全重构了代码库和编译系统。如果你切换,你的IDE会试图适应项目设置,可能会出现问题。

通过使用工作树,你可以避免不断重新配置。使用工作树在单独的文件夹中检出旧分支,对于每个分支,你都有一个独立的IDE项目。

当然,过去也可以通过多次克隆仓库来实现这一点,这也是我的做法。但是,这意味着浪费硬盘空间,更糟糕的是需要多次从仓库获取相同的更改。


11
你不必多次从代码库中获取相同的更改,只需要复制第一个克隆的.git目录即可。 - misiu_mp
4
但这仍将占用更多的磁盘空间? - mx1up
6
作为一个使用2-3个高度复制的仓库来构建一个功能分支,同时在另一个分支上进行开发的人,我将每个本地仓库作为其他仓库的远程,完全同意Sebi对缺点的描述(需要大量的提取和推送!)此外,一旦我转换到工作树,我认为我将不再担心本地同名分支分歧的问题(这种情况大约每6-10个月发生一次,因为我在数天内被多次打断,并最终从多个仓库中进行相同的功能分支操作,但忘记同步它们...) - sage
4
@iheanyi — (1). 如果IDE维护与给定目录关联的外部数据文件(例如索引数据库),则速度会更快。如果您在“同一”目录中频繁更改内容,通常会使IDE数据缓存无效,并且必须重新索引。 - Steve Hollasch
8
随着时间的推移,所有内容的历史记录将会比任意时刻工作树文件的大小更加庞大。所有内容的历史记录等同于.git目录。如果从上游克隆多个本地分支,那么就会有许多相同数据库的本地副本,因为每个克隆都有自己的.git数据库。如果有多个本地工作树,每个树都使用同一个.git数据库。是的,如果你有你的本地工作树的本地副本,Git会硬链接许多.git目录下的内容,但不适用于Windows操作系统。 - Steve Hollasch
显示剩余4条评论

103

我可以看到一些用途。

如果您有一个长时间运行的测试套件,想象一下需要几个小时,如果您开始运行它,它会有效地阻塞该副本,直到测试完成。在这些测试期间切换分支将会以难以理解的方式破坏它们。

因此,使用 git-worktree ,我可以启动第二个工作副本,为另一个分支进行工作。

此外,当我切换到其他分支进行快速调查时,我的IDE会认为很多文件突然发生了变化,并且会索引所有这些更改,只是在我切换回来时不得不重新索引它们。

第三种用例是使用与 git-diff 不同的其他工具(例如常规 diff ),在两个目录而不是两个分支之间进行文件比较。


6
对于这些来说,git clone不也同样适用吗? - jthill
20
克隆一个大的远程仓库可能需要很长时间。我正在使用一个需要几分钟才能完成克隆的代码库。我认为你可以使用 git clone --reference 来完成它。此外,所有其他分支的管理将只在一次而不是每个工作目录中进行。 - Andreas Wederbrand
6
从本地克隆,而不是从远程克隆。我不理解分支管理的问题,你能否澄清一下? - jthill
22
我尝试使用克隆,发现管理上存在问题。我不再有一个分支集,而是有一组克隆版,无法在单个用户界面中同时查看它们。如果我需要挑选一些更改,我必须将它们获取或推送到某处。这为所有操作增加了额外的步骤,虽然一切都能做到,但总会有一些阻力。 - max630
2
而且,当涉及到设置备份时,单一的存储库要容易得多。 - max630
我们可以为每个分支保持克隆。但这将消耗磁盘空间。是的,为了节省带宽,您可以克隆本地存储库。如果您的存储库为300MB,并且您需要处理3个问题,那么您会克隆3次,因此达到1GB。如果您有类似于mono-repo的设置,其中大小通常很大,我们会多次克隆吗?gi工作树完美地解决了这个问题。我认为,人们不应该把磁盘空间和网络带宽看作是廉价的资源。如果可以采用worktree,您组织中的存储库数量将显著减少... - Prabhu U

84

其中一个明显的用途是同时比较不同版本的行为(而不是源代码),例如网站或仅单个网页的不同版本。

我在本地尝试过了。

  • 创建目录page1

  • 在其中创建src目录并对其进行git init

  • src中创建带有少量内容的page1.html并提交它。

  • $ git branch ver0

  • $ git worktree add ../V0 ver0

  • src主分支中添加更多文本到page1.html并提交它。

  • $ git branch sty1

  • sty1分支中编辑page1.html(添加一些独特的CSS样式)并提交它。

  • $ git worktree add ../S1 sty1

现在,您可以使用Web浏览器同时打开和查看这3个版本:

  • ..\page1\src\page1.html // 无论git当前是什么

  • ..\page1\V0\page1.html // 初始版本

  • ..\page1\S1\page1.html // 实验性样式化版本


5
我不明白这如何说明使用工作树相比克隆的好处。 - iheanyi
2
@iheanyi 你也可以这样说 branch;答案也是一样的:它更轻量级,而且专为此工作而建。 - OJFord
2
@OJFord 这正是重点所在。这个答案没有向我解释worktree与其他命令的区别。显然它不是分支或克隆的别名,但我在这里看到的效果似乎是一样的。我不明白这比使用分支或克隆更轻量级的原因。 - iheanyi
1
@iheanyi 这与使用分支不同 - 您无法仅使用分支一次获取工作树的多个状态 - 并且比第二个(..,第n个)克隆更轻量级。我的意思是,您也可以说分支'为什么不只克隆并进行更改',但单个存储库中的多个分支是一种更轻量级和更易于管理的获取该行为的方式。 - OJFord
2
@iheanyi 我认为答案最清晰的部分是最后一段/项目符号:您可以同时查看三个不同分支(或其他引用)上“page1.html”的状态。您不能仅使用分支来实现这一点,还需要使用克隆或工作树。工作树避免了克隆会使用的额外磁盘空间和网络使用情况。(这就是我所说的“轻量级”的含义。)它在语义上也更清晰,比起混乱的myrepo1、myrepo2、myrepo3,回来时想知道为什么有三个相同存储库的克隆以及应该使用哪一个。 - OJFord
显示剩余2条评论

39
  1. 您可能有合法的理由想要/需要在文件系统中同时拥有多个工作树。

    • 在需要进行其他更改(例如编译/测试)而仍需操作已检出的文件时

    • 通过普通的差异工具比较文件

    • 在合并冲突期间,我经常希望能够导航到源代码上,同时解决文件中的冲突。

    • 如果需要反复切换,那么每次检出和重新检出都会浪费时间,而这些是使用多个工作树可以避免的。

    • 通过git藏起来切换分支会给心理带来一定的负担。有些人发现通过从不同的目录打开文件而不是git藏起来切换分支,心理负担更小。

  2. 有些人问:“为什么不做多个本地克隆呢?” 的确,使用“--local”标志,您无需担心额外的磁盘空间使用。这(或类似的想法)是我到目前为止所做的。与本地克隆相比,链接的工作树的功能优势包括:

    1. 使用本地克隆,您的额外工作树(位于本地克隆中)根本无法访问源或上游分支。克隆中的“origin”将不同于第一个克隆中的“origin”。

      • 运行git log @{u}..git diff origin/feature/other-feature可能非常有帮助,但这些方法已经不再可行了,或者更加困难。通过各种变通方法,本地克隆也可以实现这些想法,但是通过链接的工作树,您可以更好、更简单地完成这些操作。
    2. 您可以在工作树之间共享引用。如果您想比较或借用另一个本地分支的更改,现在您可以这样做。


19
你可以使用一个简单的命令列出所有的工作树,而克隆则需要自己跟踪它们。 - Ian Ringrose
截至Git 2.7.0,似乎是这种情况。好知道。 - Alexander Bird

32

我最喜欢的,也是可能最常见的使用场景之一,每个人都应该使用git worktree来审查团队成员的拉取请求,同时仍然在主要的工作树上处理自己的更改。


1
你不会在像GitHub这样的平台上进行代码审查吗?本地审查PR的工作流程是什么? - Ayush Mandowara
15
有些情况下,审核者可能希望在本地运行代码,以查看用户界面(UI)等内容。 - Thomas Hirsch
1
@AyushMandowara 在本地你可以使用tig来浏览更改,你可以使用git notes添加注释,你可以随意使用git diff等等。 - CervEd
我真的希望在我选择的 UI(我的情况下是 KDiff3)中看到复杂的差异,而不是在我们工作时使用的平庸的浏览器视图。 - Eike

15

我最开始遇到这个问题是因为想知道这些花哨的工作树可以用来做什么。自那以后,我已经将它们整合到我的工作流程中,并且尽管我最初有些怀疑,但我发现它们非常有用。

我正在处理一个相当大的代码库,需要很长时间才能编译。我通常在我的机器上使用当前的开发分支以及我当前正在工作的功能分支和代表当前实时系统状态的主分支。

对我来说最大的好处之一显然是,每次切换分支(即工作树)时我不必重新编译整个项目。一个不错的副作用是,我可以进入开发工作树,在那里进行操作,然后更改目录到我当前功能分支的工作树并重新基于它而无需先拉取。


1
这是我使用worktree的相同用例,而且你不必从多个相同仓库的克隆中不断获取,节省带宽和时间。由于只有一个.git实例与元数据,我还可以在珍贵的NVMe SSD上节省许多GB。 - ericcurtin

13

简而言之:如果你需要同时检出两个工作树,无论出于何种原因,git-worktree 是一种快速且占用空间小的方式。

如果你创建了另一个工作树,大部分仓库的内容(即 .git)将是共享的。这意味着,如果你在一个工作树中创建了一个分支或者获取了数据,那么在任何其他工作树中也可以访问到这些内容。假设你想在分支 foo 上运行测试套件,而不必将其推送到某个地方进行克隆,并且你想避免本地克隆仓库的麻烦,那么使用 git-worktree 就是一种不错的方式,它可以在另一个地方创建一个状态的新副本,无论是临时的还是永久的。就像克隆一样,当你完成任务后,只需要删除它即可,它的引用将在一段时间后被自动垃圾回收。


3
文档显示,你不能在两个工作副本中拥有相同的分支,这是一个严重的限制。但使用Mercurial时,仅存在一些小问题即可解决。 - hypersw
1
当然可以。手册上有说明;寻找“--force”。但是如果您在一个地方更新分支并期望在另一个地方使用它,这将不方便,因为工作树没有更新。 - jsageryd
是的,在这方面,Mercurial中的分支概念更加透明。 一个工作树中的分支如何出现在另一个工作树中?与多个上行链接一样吗?我的第一次使用工作树的实验中,在两个工作树中运行fetch最终得到了两个名为'origin/master'的不同指针。 - hypersw
1
工作树(顾名思义)只是一个工作树,具有一些额外的功能;仓库在所有工作树之间共享。两个工作树之间唯一的区别是检出的分支可以不同(对于合理的工作流程来说应该不同)。可以在单独的工作树中提交,因此它也有自己的索引(即暂存区)来完成这项工作。单独的工作树中的.git文件是一个文本文件,其中包含其配置的路径,该路径位于原始仓库中。 - jsageryd
2
@WilsonF: git checkout --ignore-other-worktrees <branch>https://git-scm.com/docs/git-checkout/2.12.2#git-checkout---ignore-other-worktrees@WilsonF:git checkout --ignore-other-worktrees <branch> https://git-scm.com/docs/git-checkout/2.12.2#git-checkout---ignore-other-worktrees - jsageryd
显示剩余2条评论

8
我有一个相当不寻常的问题:我在同一台机器上进行Windows和Linux开发。我在Windows虚拟机中运行Linux,将一些Windows目录挂载到Linux机器中并直接使用。这使得我可以使用Windows来管理文件,但是在Linux中构建。这是一个跨平台项目,因此它从相同的目录结构中构建Windows和Linux。
问题在于,当在同一目录中使用Linux和Windows构建系统时,它们会互相冲突;下载库等复杂的构建步骤使用相同的目录名称。构建系统的Windows版本下载Windows特定的库,而Linux版本的构建系统下载Linux特定的库。
在理想的情况下,构建系统应该被修改为让Windows和Linux可以共存于同一目录中,但是现在,问题正在通过工作树来解决。 "Linux"文件夹可以生成特定于Linux的构建产物,而"Windows"文件夹可以生成特定于Windows的构建产物。虽然这并不是一个理想的解决方案,但是在等待构建系统错误得到解决的同时,这是一个很好的临时措施。
诚然,工作树并不是为此设计的;我必须将Windows版本和Linux版本保持在不同的分支上,尽管我真的更喜欢它们在同一分支上。尽管如此,它正在发挥作用,并且是工作树拯救一天的某种不寻常情况。

+1 这似乎是一个非常有效的解决方案,可以让 Make 在本地不进行每个配置构建输出目录。我有一个类似的 VMware Workstation 设置,其中包括 Ubuntu 和 macOS 客户端。 - Tanz87

5

我正在使用 git worktree 进行机器学习开发。

我有一个主要的功能代码,然后我想拆分不同实验的分支(不同算法和不同超参数)。git worktree 允许我将 dvc 集成到不同版本的我的代码中,以适应不同的算法。在运行完所有训练作业后,我评估最终指标并将最佳分支/模型合并到主分支。


4

我11年前发布了这个问题,不知何故它已经有了120k的浏览量。

我不确定Git是否支持这种操作,但理论上来说,它应该可以工作。

我的工作流通常涉及在多个分支中同时编辑文件。换句话说,我经常想要在一个分支中打开几个文件,同时在另一个分支中编辑另一个文件的内容。

我通常的解决方案是进行两次checkout,但很遗憾我不能在它们之间共享分支和引用。我希望只需使用同一个.git文件夹管理两个工作目录。

我知道本地git克隆解决方案(默认情况下,即硬链接共享对象,以及--shared选项,它设置了一个备用对象存储库),但这些解决方案仅减少了磁盘空间的使用,特别是在--shared的情况下,似乎存在风险。

是否有一种方法可以使用一个.git文件夹,并由其支持两个工作目录?或者Git是否硬编码为任何时候只检出一个工作目录?

答案是git工作树。


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