在代码仓库中创建一个新分支是在使用 Git 时非常常见的任务。它是将不相关的更改彼此分离的主要机制之一。它也经常是指定合并到主分支中的内容的主要方式。
如果没有分支,所有内容都必须经过挑选,否则您的所有工作都将有效地合并为压缩的变基。问题是,分支继承了它们所fork的分支的工作,这可能会导致您意外地推送您从未打算包含在新分支中的提交。
解决方案是始终从主分支 fork(除非您不打算这样做)。这是一个很容易说出的规则,但不幸的是,它也同样容易忘记,因此了解规则背后的原因可能会有所帮助。
分支不是文件夹
很自然地将 Git 分支视为文件夹。
但它不是。
当您创建一个分支时,您并没有创建一个干净的环境,即使它看起来像是那样。一个分支继承了其父分支包含的所有数据。如果父分支是主分支,那么您的新分支包含项目的共同历史记录。但是,如果父分支是主分支之外的另一个分支,那么您的新分支包含主分支中的历史记录以及另一个分支的历史记录。我经常用乐高积木来思考,所以这里有一个可视化示例,它不是那些复杂的 Git 节点图(但实际上是,偷偷地)。
假设您的主分支是一个乐高底板。

(Seth Kenlon CC BY-SA 4.0)
当您从 main
创建一个分支时,您添加了一块积木。假设您添加了一个名为 blue
的分支。

(Seth Kenlon BY-SA 4.0)
blue
分支包含底板的历史记录以及您在 blue
上所做的任何工作。在代码中,这就是目前为止发生的事情
$ git branch
* main
$ git checkout -b blue
分支的分支
如果您在仍然位于 blue
分支中时又创建另一个分支,那么您将在 main
以及 blue
的基础上构建。假设您创建一个名为 red
的分支,因为您想开始构建一个新功能。

(Seth Kenlon CC BY-SA 4.0)
这本身并没有什么问题,只要您理解您的 red
分支是建立在 blue
之上的。您在 blue
分支中所做的所有工作也存在于 red
中。只要您不希望 red
成为一个仅包含 main
分支历史记录的全新开始,这是一种完全可以接受的构建 repo 的方法。但是请注意,项目所有者无法例如接受 red
更改,而不接受一堆 blue
更改,至少在不费很多功夫的情况下是这样。
彻底分离
如果您实际上想要做的是将 blue
和 red
开发为单独的功能,以便项目所有者可以选择合并一个而不是另一个,那么您需要这两个分支都仅基于 main
。这很容易做到。您只需先检出 main
分支,然后从那里创建您的新分支。
$ git branch
* blue
main
$ git checkout main
$ git checkout -b red
这是用乐高积木表示的样子

(Seth Kenlon CC BY-SA 4.0)
现在您可以仅向项目所有者交付 blue
,或仅交付 red
,或两者都交付,并且项目所有者可以决定将什么附加到官方仓库的 main
上。更好的是,blue
和 red
都可以继续单独开发。即使您完成了 blue
并且它被合并到 main
中,一旦 red
的开发人员合并来自 main
的更改,那么 blue
就可用于新的 red
开发。

(Seth Kenlon CC BY-SA 4.0)
分支示例
这是一个简单的演示,说明了这个原理。首先,创建一个带有主分支的 Git 仓库
$ mkdir example
$ cd example
$ git init -b main
用一个示例文件填充您新生的项目
$ echo "Hello world" > example.txt
$ git add example.txt
$ git commit -m 'Initial commit'
然后检出一个名为 blue
的分支,并进行一个您不想保留的愚蠢提交
$ git checkout -b blue
$ fortune > example.txt
$ git add example.txt
$ git commit -m 'Unwisely wrote over the contents of example.txt'
查看日志
$ git log --oneline
ba9915d Unwisely wrote over the contents of example.txt
55d4811 Initial commit
首先,假设您很高兴继续在 blue
之上进行开发。创建一个名为 red
的分支
$ git checkout -b red
查看日志
$ git log --oneline
ba9915d Unwisely wrote over the contents of example.txt
55d4811 Initial commit
您的新 red
分支以及您在 red
中开发的任何内容都包含您在 blue
中所做的提交。如果这是您想要的,那么您可以继续开发。但是,如果您打算重新开始,那么您需要从 main
而不是从 blue
创建 red
。
现在检出您的主分支
$ git checkout main
查看日志
$ git log --oneline
55d4811 Initial commit
目前为止看起来不错。blue
分支与 main
隔离,因此它是从不同方向分支的干净基础。是时候重置演示了。因为您尚未在 red
上做任何事情,所以您可以安全地删除它。如果这发生在现实生活中,并且您已经在 red
上开始开发,那么您将不得不从 red 中挑选您的更改到新分支中。
但这只是一个演示,所以删除 red
是安全的
$ git branch -D red
现在创建一个名为 red
的新分支。这个版本的 red
旨在作为一个全新的开始,与 blue
不同。
$ git checkout -b red
$ git log --oneline
55d4811 Initial commit
尝试进行新的提交
$ echo "hello world" >> example.txt
$ git add example.txt
$ git commit -m 'A new direction'
查看日志
$ git checkout -b red
$ git log --oneline
de834ff A new direction
55d4811 Initial commit
最后看一下 blue
$ git checkout blue
$ git log --oneline
ba9915d Unwisely wrote over the contents of example.txt
55d4811 Initial commit
red
分支有它自己的历史记录。
blue
有它自己的历史记录。
两个不同的分支,都基于 main
。
谨慎 Fork
像许多 Git 用户一样,我发现使用 Git 感知的提示符更容易跟踪我当前的分支。在阅读了 Moshe Zadka 关于它的文章后,我一直在使用 Starship.rs,我发现它非常有用,尤其是在对需要所有合并请求仅包含在一个分支上的一个提交的打包项目进行大量更新时。
在 20 个或更多参与者之间进行数百次更新的情况下,管理这种情况的唯一方法是经常检出 main,pull,并创建一个新分支。Starship 立即提醒我当前的分支以及该分支的状态。
您是从主分支还是从另一个分支 fork 一个新分支取决于您想要实现的目标。重要的是您要理解创建分支的位置很重要。请注意您当前的分支。
2 条评论