基于上一个分支,重新定位多个 Git 分支的工具
作者:Sec-Labs | 发布时间:
项目地址
https://github.com/Shopify/git-chain
Git Chain
Git Chain 是一个基于 Git 的工具,可以自动执行多个 Git 分支的 rebase 操作。
相关技术点
- Git
- Ruby
项目用途
在软件开发中,如果你想将一个大型的 feature 分成多个较小的 pull requests 来更好地进行 code review,就需要为每个 pull request 创建单独的 Git 分支。如果一个分支的更改不依赖于另一个分支,那么你可以将所有分支都基于主分支(master branch)进行开发和合并。但是,如果一个分支的更改依赖于另一个分支的更改,那么你就需要手动执行多个 rebase 操作。Git Chain 可以帮助你自动化这个过程,只需要使用一个命令 git chain rebase 就可以完成整个 rebase 操作链的重构。
例如:
假设你的一个 feature 包含三个 pull request,分别是添加一个新的数据库表,使用该表创建一个新的 model,以及构建一个用户界面以用于编辑记录。你需要创建三个分支,分别是 feature-database,feature-model 和 feature-ui。你需要将 feature-database 分支基于 master 分支创建,将 feature-model 分支基于 feature-database 分支创建,将 feature-ui 分支基于 feature-model 分支创建。这就是一个典型的 Git Chain 操作链。
Git Chain 会根据当前分支自动检测操作链,你也可以使用 -c 选项显式指定操作链的名称。当然,在这个过程中也可能会出现冲突,需要手动解决。
Git Chain
Git Chain是一款基于先前分支进行多个Git分支的变基工具。
Git Chain有什么用?
如果你正在开发一个更大的功能,并希望以更小,更易于审查的拉取请求发货,那么很有可能你会为每个拉取请求创建单独的分支。如果更改不相互依赖-恭喜🎉!您可以简单地将所有分支基于主分支并在上面工作和合并而不会干扰。
但是,如果您的第二个分支依赖于第一个的更改,第三个分支依赖于第二个分支的更改,那么在其中一个基础分支发生更改(例如,因为您在审查中解决了注释)的情况下,您将最终进行大量的手动变基。
Git Chain可以帮助您自动化此任务。您可以指定一系列分支,并使用单个命令将它们全部变基:git chain rebase。
要求
- Git(当然)
- 系统Ruby(
/usr/bin/ruby -v >=2.6.3)
安装
$ git clone https://github.com/Shopify/git-chain /usr/local/share/git-chain # 或您认为合适的任何文件夹
$ ln -sv /usr/local/share/git-chain/bin/git-chain /usr/local/bin/ # 或您PATH中的任何位置
$ git chain # 现在应该可以工作了
演示

示例
让我们看一个例子。想象一下以下功能:您想添加一个新的数据库表(拉取请求1),使用表添加模型(拉取请求2)并构建用于编辑记录的用户界面(拉取请求3)。
在开始时,您将拥有这个好看的干净的git历史(git log --oneline --all --graph,分支名称括在括号中):
* e7888f9 (HEAD -> feature-ui) feature-ui.2
* a743802 (feature-model) feature-model.1
* 9cc4914 (feature-database) feature-database.1
* f6ba0e9 (master) master.1
您可以继续在分支上工作,您的同事将其更改合并到主分支中,一段时间后,git历史记录如下:
* 56b953a (feature-model) feature-model.2
| * e7888f9 (HEAD -> feature-ui) feature-ui.2
| * 14090bb feature-ui.1
|/
* a743802 feature-model.1
| * 8c46072 (feature-database) feature-database.2
|/
* 9cc4914 feature-database.1
| * fdca13e (master) master.2
|/
* f6ba0e9 master.1
要返回线性git历史,您需要手动将所有分支变基到彼此的顶部,即将database分支变基到master,将model分支变基到database,将ui分支变基到model。
让我们使用Git Chain自动化此过程。
设置链
Git Chain首先需要了解预期的分支顺序。我们称之为“分支链”。您可以使用git chain命令创建一个。
$ git chain setup -c awesome-feature master feature-database feature-model feature-ui
Setting up chain awesome-feature
链的名称可以使用-c选项指定。第一个参数(在本例中为master)是我们希望将来将链变基到的基本分支的名称。
每个链只需要执行一次此设置步骤。
变基链
现在,您可以一次性重新基于所有分支:
$ git chain rebase
Rebasing the following branches: ["master", "feature-database", "feature-model", "feature-ui"]
Git Chain根据您当前所在的分支(分支只能是一个链的一部分)来检测当前链。您可以使用您在设置期间已经看到的-c选项显式指定链。
$ git checkout master
$ git chain rebase
Current branch 'master' is not in a chain.
$ git chain rebase -c awesome-feature
Rebasing the following branches: ["master", "feature-database", "feature-model", "feature-ui"]
之后,您将得到一个干净的历史记录:
* 7974771 (HEAD -> feature-ui) feature-ui.2
* 1427391 feature-ui.1
* 9877787 (feature-model) feature-model.2
* 3ad1096 feature-model.1
* 8e333d6 (feature-database) feature-database.2
* 00ac4d1 feature-database.1
* fdca13e (master) master.2
* f6ba0e9 master.1
处理冲突
当然,并非所有情况都像这种理想化的情况一样简单。您肯定会遇到重新基础操作无法清除应用并且需要解决合并冲突的情况。当变基失败时,Git Chain会停止并将存储库保持在该状态。您必须手动解决冲突,完成当前的变基并再次调用git chain rebase以继续。
让我们看另一个包含冲突更改的示例。
* 97637af (a) a.2 (conflicting with b)
```将分支 `a` 打补丁到 `master` 上是可行的,但将分支 `b` 打补丁到 `a` 上时会发生冲突。
$ git chain rebase 正在打补丁以下分支:["master", "a", "b", "c"] 无法将 b 打补丁到 a 上。修复补丁并再次运行 'git chain rebase'。 ...
[...解决冲突...] $ git rebase --continue 成功地打了补丁并更新了 refs/heads/b。 $ git chain rebase 正在打补丁以下分支:["master", "a", "b", "c"]
```
推送链到 GitHub 的 pull request
GitHub 支持通过用户界面设置 pull request 的基础分支。现在,您需要为链中的所有分支手动执行此操作。
编辑 pull request:

设置基础分支:

完成此操作并设置所有远程后,您可以使用 git chain push 推送链中的所有分支。