用乐高类比解释 Git 分支

使用这个有用的乐高类比来理解为什么在 Git 中分支的位置很重要。
2 位读者喜欢这篇文章。
Open Lego CAD
图片版权:CC BY-SA 4.0 Klaatu Einzelgänger

在代码仓库中创建一个新分支是在使用 Git 时非常常见的任务。它是将不相关的更改彼此分离的主要机制之一。它也经常是指定合并到主分支中的内容的主要方式。

如果没有分支,所有内容都必须经过挑选,否则您的所有工作都将有效地合并为压缩的变基。问题是,分支继承了它们所fork的分支的工作,这可能会导致您意外地推送您从未打算包含在新分支中的提交。

解决方案是始终从主分支 fork(除非您不打算这样做)。这是一个很容易说出的规则,但不幸的是,它也同样容易忘记,因此了解规则背后的原因可能会有所帮助。

分支不是文件夹

很自然地将 Git 分支视为文件夹。

但它不是。

当您创建一个分支时,您并没有创建一个干净的环境,即使它看起来像是那样。一个分支继承了其父分支包含的所有数据。如果父分支是主分支,那么您的新分支包含项目的共同历史记录。但是,如果父分支是主分支之外的另一个分支,那么您的新分支包含主分支中的历史记录以及另一个分支的历史记录。我经常用乐高积木来思考,所以这里有一个可视化示例,它不是那些复杂的 Git 节点图(但实际上是,偷偷地)。

假设您的主分支是一个乐高底板。

Main branch

(Seth Kenlon CC BY-SA 4.0)

当您从 main 创建一个分支时,您添加了一块积木。假设您添加了一个名为 blue 的分支。

Main and blue branches

(Seth Kenlon BY-SA 4.0)

blue 分支包含底板的历史记录以及您在 blue 上所做的任何工作。在代码中,这就是目前为止发生的事情

$ git branch
* main
$ git checkout -b blue

分支的分支

如果您在仍然位于 blue 分支中时又创建另一个分支,那么您将在 main 以及 blue 的基础上构建。假设您创建一个名为 red 的分支,因为您想开始构建一个新功能。

Main and blue and red branches

(Seth Kenlon CC BY-SA 4.0)

这本身并没有什么问题,只要您理解您的 red 分支是建立在 blue 之上的。您在 blue 分支中所做的所有工作也存在于 red 中。只要您不希望 red 成为一个仅包含 main 分支历史记录的全新开始,这是一种完全可以接受的构建 repo 的方法。但是请注意,项目所有者无法例如接受 red 更改,而不接受一堆 blue 更改,至少在不费很多功夫的情况下是这样。

彻底分离

如果您实际上想要做的是将 bluered 开发为单独的功能,以便项目所有者可以选择合并一个而不是另一个,那么您需要这两个分支都仅基于 main。这很容易做到。您只需先检出 main 分支,然后从那里创建您的新分支。

$ git branch
* blue
main
$ git checkout main
$ git checkout -b red

这是用乐高积木表示的样子

Main, blue, and red branches

(Seth Kenlon CC BY-SA 4.0)

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

Image of a lego git branch

(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 一个新分支取决于您想要实现的目标。重要的是您要理解创建分支的位置很重要。请注意您当前的分支。

标签
Seth Kenlon
Seth Kenlon 是一位 UNIX 极客、自由文化倡导者、独立多媒体艺术家和 D&D 爱好者。他曾在电影和计算机行业工作,通常同时进行。

2 条评论

用乐高作为例子真是太棒了!
谢谢!

顺便说一句:指向 starship 文章的链接是错误的,并给出了 404 错误。应该是这个

https://open-source.net.cn/article/22/2/customize-prompt-starship

问候!

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.