标签 分支 下的文章

你好!我一直在投入写作一本关于 Git 的小册,因此我对 Git 分支投入了许多思考。我不断从他人那里听说他们觉得 Git 分支的操作方式违反直觉。这使我开始思考:直觉上的分支概念可能是什么样,以及它如何与 Git 的实际操作方式区别开来?

在这篇文章中,我想简洁地讨论以下几点内容:

  • 我认为许多人可能有的一个直觉性的思维模型
  • Git 如何在内部实现分支的表示(例如,“分支是对提交的指针”)
  • 这种“直觉模型”与实际操作方式之间的紧密关联
  • 直觉模型的某些局限性,以及为何它可能引发问题

本文无任何突破性内容,我会尽量保持简洁。

分支的直观模型

当然,人们对分支有许多不同的直觉。我自己认为最符合“苹果树的一个分支”这一物理比喻的可能是下面这个。

我猜想许多人可能会这样理解 Git 分支:在下图中,两个红色的提交就代表一个“分支”。

我认为在这个示意图中有两点很重要:

  1. 分支上有两个提交
  2. 分支有一个“父级”(main),它是这个“父级”的分支

虽然这个观点看似合理,但实际上它并不符合 Git 对于分支的定义 — 最重要的是,Git 并没有一个分支的“父级”的概念。那么,Git 又是如何定义分支的呢?

在 Git 里,分支是完整的历史

在 Git 中,一个分支是每个过去提交的完整历史记录,而不仅仅是那个“分支”提交。因此,在我们上述的示意图中,所有的分支(mainbranch)都包含了 4 次提交。

我创建了一个示例仓库,地址为:https://github.com/jvns/branch-example。它设置的分支方式与前图一样。现在,我们来看看这两个分支:

main 分支包含了 4 次提交:

$ git log --oneline main
70f727a d
f654888 c
3997a46 b
a74606f a

mybranch 分支也有 4 次提交。最后两次提交在这两个分支里都存在。

$ git log --oneline mybranch
13cb960 y
9554dab x
3997a46 b
a74606f a

因此,mybranch 中的提交次数为 4,而不仅仅是 2 次“分支”提交,即 13cb9609554dab

你可以用以下方式让 Git 绘制出这两个分支的所有提交:

$ git log --all --oneline --graph
* 70f727a (HEAD -> main, origin/main) d
* f654888 c
| * 13cb960 (origin/mybranch, mybranch) y
| * 9554dab x
|/
* 3997a46 b
* a74606f a

分支以提交 ID 的形式存储

在 Git 的内部,分支会以一种微小的文本文件的形式存储下来,其中包含了一个提交 ID。这就是我一开始提及到的“技术上正确”的定义。这个提交就是分支上最新的提交。

我们来看一下示例仓库中 mainmybranch 的文本文件:

$ cat .git/refs/heads/main
70f727acbe9ea3e3ed3092605721d2eda8ebb3f4
$ cat .git/refs/heads/mybranch
13cb960ad86c78bfa2a85de21cd54818105692bc

这很好理解:70f727main 上的最新提交,而 13cb96mybranch 上的最新提交。

这样做的原因是,每个提交都包含一种指向其父级的指针,所以 Git 可以通过追踪这些指针链来找到分支上所有的提交。

正如我前文所述,这里遗漏的一个重要因素是这两个分支间的任何关联关系。从这里能看出,mybranchmain 的一个分支——这一点并没有被表明出来。

既然我们已经探讨了直观理解的分支概念是如何不成立的,我接下来想讨论的是,为何它在某些重要的方面又是如何成立的。

人们的直观感觉通常并非全然错误

我发现,告诉人们他们对 Git 的直觉理解是“错误的”的说法颇为流行。我觉得这样的说法有些可笑——总的来说,即使人们关于某个题目的直觉在某些方面在技术上不精确,但他们通常会有完全合理的理由来支持他们的直觉!即使是“不正确的”模型也可能极其有用。

现在,我们来讨论三种情况,其中直觉上的“分支”概念与我们实际在操作中如何使用 Git 非常相符。

变基操作使用的是“直观”的分支概念

现在,让我们回到最初的图片。

当你在 main 上对 mybranch 执行 变基 rebase 操作时,它将取出“直观”分支上的提交(只有两个红色的提交)然后将它们应用到 main 上。

执行结果就是,只有两次提交(xy)被复制。以下是相关操作的样子:

$ git switch mybranch
$ git rebase main
$ git log --oneline mybranch
952fa64 (HEAD -> mybranch) y
7d50681 x
70f727a (origin/main, main) d
f654888 c
3997a46 b
a74606f a

在此,git rebase 创建了两个新的提交(952fa647d50681),这两个提交的信息来自之前的两个 xy 提交。

所以直觉上的模型并不完全错误!它很精确地告诉你在变基中发生了什么。

但因为 Git 不知道 mybranchmain 的一个分叉,你需要显式地告诉它在何处进行变基。

合并操作也使用了“直观”的分支概念

合并操作并不复制提交,但它们确实需要一个“ 基础 base ”提交:合并的工作原理是查看两组更改(从共享基础开始),然后将它们合并。

我们撤销刚才完成的变基操作,然后看看合并基础是什么。

$ git switch mybranch
$ git reset --hard 13cb960  # 撤销 rebase
$ git merge-base main mybranch
3997a466c50d2618f10d435d36ef12d5c6f62f57

这里我们获得了分支分离出来的“基础”提交,也就是 3997a4。这正是你可能会基于我们的直观图片想到的提交。

GitHub 的拉取请求也使用了直观的概念

如果我们在 GitHub 上创建一个拉取请求,打算将 mybranch 合并到 main,这个请求会展示出两次提交:也就是 xy。这完全符合我们的预期,也和我们对分支的直观认识相符。

我想,如果你在 GitLab 上发起一个合并请求,那显示的内容应该会与此类似。

直观理解颇为精准,但它有一定局限性

这使我们的对分支直观定义看起来相当准确!这个“直观”的概念和合并、变基操作以及 GitHub 拉取请求的工作方式完全吻合。

当你在进行合并、变基或创建拉取请求时,你需要明确指定另一个分支(如 git rebase main),因为 Git 不知道你的分支是基于哪个分支的。

然而,关于分支的直观理解有一个比较严重的问题:你直觉上认为 main 分支和某个分离的分支有很大的区别,但 Git 并不清楚这点。

所以,现在我们要来讨论一下 Git 分支的不同种类。

主干和派生分支

对于人类来说,mainmybranch 有着显著的区别,你可能针对如何使用它们,有着截然不同的意图。

通常,我们会将某些分支视为“ 主干 trunk ”分支,同时将其他一些分支看作是“派生”。你甚至可能有派生的派生分支。

当然,Git 自身并没有这样的区分(“派生”是我刚刚构造的术语!),但是分支的种类确实会影响你如何处理它。

例如:

  • 你可能会想将 mybranch 变基到 main,但你大概不会想将 main 变基到 mybranch —— 那就太奇怪了!
  • 一般来说,人们在重写“主干”分支的历史时比短期存在的派生分支更为谨慎。

Git 允许你进行“反向”的变基

我认为人们经常对 Git 感到困惑的一点是 —— 由于 Git 并没有分支是否是另一个分支的“派生”的概念,它不会给你任何关于何时合适将分支 X 变基到分支 Y 的指引。这一切需要你自己去判断。

例如,你可以执行以下命令:

$ git checkout main
$ git rebase mybranch

或者

$ git checkout mybranch
$ git rebase main

Git 将会欣然允许你进行任一操作,尽管在这个案例中 git rebase main 是极其正常的,而 git rebase mybranch 则显得格外奇怪。许多人表示他们对此感到困惑,所以我提供了一个展示两种变基类型的图片以供参考:

相似地,你可以进行“反向”的合并,尽管这相较于反向变基要正常得多——将 mybranch 合并到 main 和将 main 合并到 mybranch 都有各自的益处。

下面是一个展示你可以进行的两种合并方式的示意图:

Git 对于分支之间缺乏层次结构感觉有些奇怪

我经常听到 “main 分支没什么特别的” 的表述,而这令我感到困惑——对于我来说,我处理的大部分仓库里,main 无疑是非常特别的!那么人们为何会称其为不特别呢?

我觉得,重点在于:尽管分支确实存在彼此间的关系(main 通常是非常特别的!),但 Git 并不知情这些关系。

每当你执行如 git rebasegit merge 这样的 git 命令时,你都必须明确地告诉 Git 分支间的关系,如果你出错,结果可能会相当混乱。

我不知道 Git 在此方面的设计究竟“对”还是“错”(无疑它有利有弊,而我已对无休止的争论感到厌倦),但我认为,这对于许多人来说,原因在于它有些出人意料。

Git 关于分支的用户界面也同样怪异

假设你只想查看某个分支上的“派生”提交,正如我们之前讨论的,这是完全正常的需求。

下面是用 git log 查看我们分支上的两次派生提交的方法:

$ git switch mybranch
$ git log main..mybranch --oneline
13cb960 (HEAD -> mybranch, origin/mybranch) y
9554dab x

你可以用 git diff 这样查看同样两次提交的合并差异:

$ git diff main...mybranch

因此,如果你想使用 git log 查看 xy 这两次提交,你需要用到两个点(..),但查看同样的提交使用 git diff,你却需要用到三个点(...)。

我个人从来都记不住 ..... 的具体用意,所以我通常虽然它们在原则上可能很有用,但我选择尽量避免使用它们。

在 GitHub 上,默认分支具有特殊性

同样值得一提的是,在 GitHub 上存在一种“特殊的分支”:每一个 GitHub 仓库都有一个“默认分支”(在 Git 术语中,就是 HEAD 所指向的地方),具有以下的特别之处:

  • 初次克隆仓库时,默认会检出这个分支
  • 它作为拉取请求的默认接收分支
  • GitHub 建议应该保护这个默认分支,防止被强制推送,等等。

很可能还有许多我未曾想到的场景。

总结

这些说法在回顾时看似是显而易见的,但实际上我花费了大量时间去搞清楚一个更“直观”的分支概念,这是因为我已经习惯了技术性的定义,“分支是对某次提交的引用”。

同样,我也没有真正去思索过如何在每次执行 git rebasegit merge 命令时,让 Git 明确理解你分支之间的层次关系——对我而言,这已经成为第二天性,并没有觉得有何困扰。但当我反思这个问题时,可以明显看出,这很容易导致某些人混淆。

(题图:MJ/a5a52832-fac8-4190-b3bd-fec70166aa16)


via: https://jvns.ca/blog/2023/11/23/branches-intuition-reality/

作者:Julia Evans 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

掌握管理本地/远程分支等最常见的 Git 任务。

Git 的主要优势之一就是它能够将工作“分叉”到不同的分支中。

如果只有你一个人在使用某个存储库,分支的好处是有限的。但是,一旦你开始与许多其他贡献者一起工作,分支就变得必不可少。Git 的分支机制允许多人同时处理一个项目,甚至是同一个文件。用户可以引入不同的功能,彼此独立,然后稍后将更改合并回主分支。那些专门为一个目的创建的分支,有时也被称为 主题分支 topic branch ,例如添加新功能或修复已知错误。

当你开始使用分支,了解如何管理它们会很有帮助。以下是开发者在现实世界中使用 Git 分支执行的最常见任务。

重命名分支

有时候,你或许会错误地命名了一个分支,或者你会想要在内容合并到主分支后,使用同一个分支在不同的错误或任务之间切换。在这种情况下,重命名主题分支就会很有帮助。

重命名本地分支

1、重命名本地分支:

$ git branch -m <old_branch_name> <new_branch_name>

当然,这只会重命名你的分支副本。如果远程 Git 服务器上存在该分支,请继续执行后续步骤。

2、推送这个新分支,从而创建一个新的远程分支:

$ git push origin <new_branch_name>

3、删除旧的远程分支:

$ git push origin -d -f <old_branch_name>

重命名当前分支

当你要重命名的分支恰好是当前分支时,你不需要指定旧的分支名称。

1、重命名当前分支:

$ git branch -m <new_branch_name>

2、推送新分支,从而创建一个新的远程分支:

$ git push origin <new_branch_name>

3、删除旧的远程分支:

$ git push origin -d -f <old_branch_name>

使用 Git 删除本地和远程分支

为了保持存储库的整洁,通常建议你在确保已将内容合并到主分支后,删除临时分支。

删除本地分支

删除本地分支只会删除系统上存在的该分支的副本。如果分支已经被推送到远程存储库,它仍然可供使用该存储库的每个人使用。

1、签出存储库的主分支(例如 mainmaster):

$ git checkout <central_branch_name>

2、列出所有分支(本地和远程):

$ git branch -a

3、删除本地分支:

$ git branch -d <name_of_the_branch>

要删除所有本地主题分支并仅保留 main 分支:

$ git branch | grep -v main | xargs git branch -d

删除远程分支

删除远程分支只会删除远程服务器上存在的该分支的副本。如果你想撤销删除,也可以将其重新推送到远程(例如 GitHub),只要你还有本地副本即可。

1、签出存储库的主分支(通常是 mainmaster):

$ git checkout <central_branch_name>

2、列出所有分支(本地和远程):

$ git branch -a

3、删除远程分支:

$ git push origin -d <name_of_the_branch>

查看远程主题分支的作者

如果你是存储库管理员,你可能会有这个需求,以便通知未使用分支的作者它将被删除。

1、签出存储库的主分支(例如 mainmaster):

$ git checkout <central_branch_name>

2、删除不存在的远程分支的分支引用:

$ git remote prune origin

3、列出存储库中所有远程主题分支的作者,使用 --format 选项,并配合特殊的选择器来只打印你想要的信息(在本例中,%(authorname)%(refname) 分别代表作者名字和分支名称):

$ git for-each-ref --sort=authordate --format='%(authorname) %(refname)' refs/remotes

示例输出:

tux  refs/remotes/origin/dev
agil refs/remotes/origin/main

你可以添加更多格式,包括颜色编码和字符串操作,以便于阅读:

$ git for-each-ref --sort=authordate \
    --format='%(color:cyan)%(authordate:format:%m/%d/%Y %I:%M %p)%(align:25,left)%(color:yellow) %(authorname)%(end)%(color:reset)%(refname:strip=3)' \
    refs/remotes

示例输出:

01/16/2019 03:18 PM tux      dev
05/15/2022 10:35 PM agil     main

你可以使用 grep 获取特定远程主题分支的作者:

$ git for-each-ref --sort=authordate \
    --format='%(authorname) %(refname)' \
    refs/remotes | grep <topic_branch_name>

熟练运用分支

Git 分支的工作方式存在细微差别,具体取决于你想要分叉代码库的位置、存储库维护者如何管理分支、 压扁 squashing 变基 rebasing 等。若想进一步了解该主题,你可以阅读下面这三篇文章:


via: https://opensource.com/article/22/5/git-branch-rename-delete-find-author

作者:Agil Antony 选题:lkxed 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

开源软件的发行版和分支是不一样的。了解其中的区别和潜在的风险。

如果你们对开源软件有过一段时间的了解,一定曾在许多相关方面中听说过 分支 fork 发行版 distribution 两个词。许多人对这两个词的区别不太清楚,因此我将试着通过这篇文章为大家解答这一疑惑。

(LCTT 译注:fork 一词,按我们之前的倡议,在版本控制工作流中,为了避免和同一个仓库的 branch 一词混淆,我们建议翻译为“复刻”。但是在项目和发行版这个语境下,没有这个混淆,惯例上还是称之为“分支”。)

首先,一些定义

在解释分支与发行版两者的细微区别与相似之处之前,让我们先给一些相关的重要概念下定义。

开源软件 是指具有以下特点的软件:

  • 在特定的 许可证 限制下,软件供所有人免费分发
  • 在特定的许可证限制下,软件源代码可以供所有人查看与修改

开源软件可以按以下方式 使用

  • 以二进制或者源代码的方式下载,通常是免费的。(例如,Eclipse 开发者环境
  • 作为一个商业公司的产品,有时向用户提供一些服务并以此收费。(例如,红帽产品
  • 嵌入在专有的软件解决方案中。(例如一些智能手机和浏览器用于显示字体的 Freetype 软件

自由开源软件 free and open source software (FOSS)不一定是“零成本”的“ 免费 free ”。自由开源软件仅仅意味着这个软件在遵守软件许可证的前提下可以自由地分发、修改、研究和使用。软件分发者也可能为该软件定价。例如,Linux 可以是 Fedora、Centos、Gentoo 等免费发行版,也可以是付费的发行版,如红帽企业版 Linux(RHEL)、SUSE Linux 企业版(SLES)等。

社区 community 指的是在一个开源项目上协作的团体或个人。任何人或者团体都可以在遵守协议的前提下,通过编写或审查代码/文档/测试套件、管理会议、更新网站等方式为开源项目作出贡献。例如,在 Openhub.net 网站上,我们可以看见政府、非营利性机构、商业公司和教育团队等组织都在 为一些开源项目作出贡献

一个开源 项目 project 是集协作开发、文档和测试的结果。大多数项目都搭建了一个中央仓库用来存储代码、文档、测试文件和目前正在开发的文件。

发行版 distribution 是指开源项目的一份的二进制或源代码的副本。例如,CentOS、Fedora、红帽企业版 Linux(RHEL)、SUSE Linux、Ubuntu 等都是 Linux 项目的发行版。Tectonic、谷歌的 Kubernetes 引擎(GKE)、亚马逊的容器服务和红帽的 OpenShift 都是 Kubernetes 项目的发行版。

开源项目的商业发行版经常被称作 产品 products ,因此,红帽 OpenStack 平台是红帽 OpenStack 的产品,它是 OpenStack 上游项目的一个发行版,并且是百分百开源的。

主干 trunk 是开发开源项目的社区的主要工作流。

开源分支fork是开源项目主干的一个版本,它是分离自主干的独立工作流。

因此,发行版并不等同于分支。发行版是上游项目的一种包装,由厂商提供,经常作为产品进行销售。然而,发行版的核心代码和文档与上游项目的版本保持一致。分支,以及任何基于分支的的发行版,导致代码和文档的版本与上游项目不同。对上游项目进行了分支的用户必须自己来维护分支项目,这意味着他们失去了上游社区协同工作带来的好处。

为了进一步解释软件分支,让我来用动物迁徙作比喻。鲸鱼和海狮从北极迁徙到加利福尼亚和墨西哥;帝王斑蝶从阿拉斯加迁徙到墨西哥;并且北半球的燕子和许多其他鸟类飞翔南方去过冬。成功迁徙的关键因素在于,团队中的所有动物团结一致,紧跟领导者,找到食物和庇护所,并且不会迷路。

独立前行带来的风险

一只鸟、帝王蝶或者鲸鱼一旦掉队就失去了许多优势,例如团队带来的保护,以及知道哪儿有食物、庇护所和目的地。

相似地,从上游版本获取分支并且独立维护的用户和组织也存在以下风险:

  1. 由于代码不同,分支用户不能够基于上游版本更新代码。 这就是大家熟知的技术债,对分支的代码修改的越多,将这一分支重新归入上游项目需要花费的时间和金钱成本就越高。
  2. 分支用户有可能运行不太安全的代码。 由于代码不同的原因,当开源代码的漏洞被找到,并且被上游社区修复时,分支版本的代码可能无法从这次修复中受益。
  3. 分支用户可能不会从新特性中获益。 拥有众多组织和个人支持的上游版本,将会创建许多符合所有上游项目用户利益的新特性。如果一个组织从上游分支,由于代码不同,它们可能无法纳入新的功能。
  4. 它们可能无法和其他软件包整合在一起。 开源项目很少是作为单一实体开发的;相反地,它们经常被与其他项目打包在一起构成一套解决方案。分支代码可能无法与其他项目整合,因为分支代码的开发者没有与上游的其他参与者们合作。
  5. 它们可能不会得到硬件平台认证。 软件包通常被搭载在硬件平台上进行认证,如果有问题发生,硬件与软件工作人员可以合作找出并解决问题发生的根源。

总之,开源发行版只是一个来自上游的、多组织协同开发的、由供应商销售与支持的打包集合。分支是一个开源项目的独立开发工作流,有可能无法从上游社区协同工作的结果中受益。


via: https://opensource.com/article/18/7/forks-vs-distributions

作者:Jonathan Gershater 选题:lujun9972 译者:Wlzzzz-del 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

比较 Git 中四种切换分支的方法的优缺点。

 title=

所有大量使用 Git 的人都会用到某种形式的上下文切换。有时这只会给你的工作流程增加少量的开销,但有时,这可能是一段痛苦的经历。

让我们用以下这个例子来讨论一些常见的上下文切换策略的优缺点:

假设你在一个名为 feature-X 的分支中工作。你刚刚发现你需要解决一个无关的问题。这不能在 feature-X 分支中完成。你需要在一个新的分支 feature-Y 中完成这项工作。

方案 1:暂存 + 分支

解决此问题最常见的工作流程可能如下所示:

  1. 停止分支 feature-X 上的工作
  2. git stash
  3. git checkout -b feature-Y origin/main
  4. 一顿鼓捣,解决 feature-Y 的问题
  5. git checkout feature-Xgit switch -
  6. git stash pop
  7. 继续在 feature-X 中工作

优点: 这种方法的优点在于,对于简单的更改,这是一个相当简单的工作流程。它可以很好地工作,特别是对于小型仓库。

缺点: 使用此工作流程时,一次只能有一个工作区。另外,根据你的仓库的状态,使用暂存是一个麻烦的环节。

方案 2:WIP 提交 + 分支

这个解决方案和前一个非常相似,但是它使用 WIP( 正在进行的工作 Work in Progress )提交而不是暂存。当你准备好切换回来,而不是弹出暂存时,git reset HEAD~1 会展开 WIP 提交,你可以自由地继续,就像之前的方案一样,但不会触及暂存。

  1. 停止分支 feature-X 上的工作
  2. git add -u(仅仅添加修改和删除的文件)
  3. git commit -m "WIP"
  4. git checkout -b feature-Y origin/master
  5. 一顿鼓捣,解决 feature-Y 的问题
  6. git checkout feature-Xgit switch -
  7. git reset HEAD~1

优点: 对于简单的更改,这是一个简单的工作流,也适合于小型仓库。你不需要使用暂存。

缺点: 任何时候都只能有一个工作区。此外,如果你或你的代码审阅者不够谨慎,WIP 提交可能会合并到最终产品。

使用此工作流时,你永远不要想着将 --hard 添加到 git reset。如果你不小心这样做了,你应该能够使用 git reflog 恢复提交,但是你最好完全避免这种情况发生,否则你会听到心碎的声音。

方案 3:克隆一个新仓库

在这个解决方案中,不是创建新的分支,而是为每个新的功能分支创建存储库的新克隆。

优点: 你可以同时在多个工作区中工作。你不需要 git stash 或者是 WIP 提交。

缺点: 需要考虑仓库的大小,因为这可能会占用大量磁盘空间(浅层克隆可以帮助解决这种情况,但它们可能并不总是很合适。)此外,你的仓库克隆将互不可知。因为他们不能互相追踪,所以你必须手动追踪你的克隆的源仓库。如果需要 git 钩子,则需要为每个新克隆设置它们。

方案 4:git 工作树

要使用此解决方案,你可能需要了解 git add worktree。如果你不熟悉 Git 中的工作树,请不要难过。许多人多年来都对这个概念一无所知。

什么是工作树?

将工作树视为仓库中属于项目的文件。本质上,这是一种工作区。你可能没有意识到你已经在使用工作树了。开始使用 Git 时,你将自动获得第一个工作树。

$ mkdir /tmp/foo && cd /tmp/foo
$ git init
$ git worktree list
/tmp  0000000 [master]

你可以在以上代码看到,甚至在第一次提交前你就有了一个工作树。接下来去尝试再添加一个工作树到你的项目中吧。

添加一个工作树

想要添加一个新的工作树你需要提供:

  1. 硬盘上的一个位置
  2. 一个分支名
  3. 添加哪些分支
$ git clone https://github.com/oalders/http-browserdetect.git
$ cd http-browserdetect/
$ git worktree list
/Users/olaf/http-browserdetect  90772ae [master]

$ git worktree add ~/trees/oalders/feature-X -b oalders/feature-X origin/master
$ git worktree add ~/trees/oalders/feature-Y -b oalders/feature-Y e9df3c555e96b3f1

$ git worktree list
/Users/olaf/http-browserdetect       90772ae [master]
/Users/olaf/trees/oalders/feature-X  90772ae [oalders/feature-X]
/Users/olaf/trees/oalders/feature-Y  e9df3c5 [oalders/feature-Y]

与大多数其他 Git 命令一样,你需要在仓库路径下使用此命令。一旦创建了工作树,就有了隔离的工作环境。Git 仓库会跟踪工作树在磁盘上的位置。如果 Git 钩子已经在父仓库中设置好了,那么它们也可以在工作树中使用。

请注意到,每个工作树只使用父仓库磁盘空间的一小部分。在这种情况下,工作树需要只大约三分之一的原始磁盘空间。这这非常适合进行扩展。如果你的仓库达到了千兆字节的级别,你就会真正体会到工作树对硬盘空间的节省。

$ du -sh /Users/olaf/http-browserdetect
2.9M

$ du -sh /Users/olaf/trees/oalders/feature-X
1.0M

优点: 你可以同时在多个工作区中工作。你不需要使用暂存。Git 会跟踪所有的工作树。你不需要设置 Git 钩子。这也比 git clone 更快,并且可以节省网络流量,因为你可以在飞行模式下执行此操作。你还可以更高效地使用磁盘空间,而无需借助于浅层克隆。

缺点: 这是个需要你额外学习和记忆的新东西,但是如果你能养成使用这个功能的习惯,它会给你丰厚的回报。

额外的小技巧

有很多方式可以清除工作树,最受欢迎的方式是使用 Git 来移除工作树:

git worktree remove /Users/olaf/trees/oalders/feature-X

如果你喜欢 RM 大法,你也可以用 rm -rf 来删除工作树。

rm -rf /Users/olaf/trees/oalders/feature-X

但是,如果执行此操作,则可能需要使用 git worktree prune 清理所有剩余的文件。或者你现在可以跳过清理,这将在将来的某个时候通过 git gc 自行完成。

注意事项

如果你准备尝试 git worktree,请记住以下几点:

  • 删除工作树并不会删除该分支。
  • 可以在工作树中切换分支。
  • 你不能在多个工作树中同时签出同一个分支。
  • 像其他命令一样,git worktree 需要从仓库内运行。
  • 你可以同时拥有许多工作树。
  • 要从同一个本地仓库签出创建工作树,否则它们将互不可知。

git rev-parse

最后一点注意:在使用 git worktree 时,仓库根所在的位置可能取决于上下文。幸运的是,git rev parse 可以让你区分这两者。

  • 要查找父仓库的根目录,请执行以下操作:
git rev-parse --git-common-dir
  • 要查找你当前所在仓库的根目录,请执行:
git rev-parse --show-toplevel

根据你的需要选择最好的方法

就像很多事情一样,TIMTOWDI( 条条大道通罗马 there's more than one way to do it )。重要的是你要找到一个适合你需要的工作流程。你的需求可能因手头的问题而异。也许你偶尔会发现自己将 git worktree 作为版本控制工具箱中的一个方便工具。


via: https://opensource.com/article/21/4/context-switching-git

作者:Olaf Alders 选题:lujun9972 译者:Chao-zhi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出