以下内容为自己的学习笔记,如需求转载,请声明原文链接 微信大众号「ENG八戒」mp.weixin.qq.com/s/J2b130UVF…

Git:Cherry-Pick 桃色圈套

Cherry-Pick 咋一看这姓名就很时髦,用来干啥呢?

Cherry-Pick 是什么?

假设在开发某些功用或者修正 bug 的时分把代码 commit 到了过错的分支 A,而且分支 A 最新版本已经覆盖了该 commit。看到这样你不会抓狂?被逼无法之下,你可能会这样弥补一番:

首要,切换到分支 A,找到需求备份的 commit 内容,逐一备份出来,而且保存 commit 描绘,然后再切换回即将接纳兼并的目标作业分支,接着把备份的内容逐一更新到其时分支中,最终再提交新的 commit 而且填上前面保存下来的 commit 描绘。

假如更新内容庞大,没有相关指令能够主动化完结上面的操作,全程靠手动操作,那么估量 Ctrl C/V 都会罢工。

幸运的是,Git 其实提供了这样一个指令 cherry-pick 协助咱们一步完结上面的弥补措施。其指令格局是

git cherry-pick <commit-hash>

上面的指令格局中,commit-hash 是在提交 commit 时 git 主动生成的 hash 串,代表每个独一无二的 commit。

Cherry-Pick 用于从其它分支提取某些 commit,而且兼并到其时作业分支,一同还会把之前提交的描绘也拷贝进来。这样大大简化了拉取其他分支某个 commit 的难度。

为何要用 Cherry-Pick

在多人协作的团队项目中,办理员通常会创立一个主线分支,比方命名为 develop,然后答应各个开发者根据主线分支分叉出特性分支,并在各自特性分支上进行特性功用的开发。在特性分支上,功用开发验证结束后,开发人员再提交 merge 兼并请求,将特性分支兼并到主线分支,兼并请求由办理员审阅通往后才履行具体兼并动作。

这里边,在兼并分支内容时,有些情况需求分开讨论。

假如,特性分支最终开发结束,并得到完整的认可,那么能够选用 merge 操作将特性分支新增的所有 commits 都兼并到主线分支。

可是,假如在特性分支没有开发结束又被遗忘很久远的时分,后来想想又需求用到其间的部分更新内容,比方,bug 补丁等,那么这个特性分支里的部分 commit 才是值得被拉取的内容,用 merge 显着不可,应该用 cherry-pick。

已然 cherry-pick 和 merge 都是用于兼并 commit,那么差异在哪?

Cherry-Pick 只针对某个分支的某些指定 commit,而不是悉数,而 merge 会把被选中分支的所有不同 commit 都拉取过来,这正是 cherry-pick 和 merge 的中心差异所在。

此外,相似兼并功用的指令还有 rebase,后边有时机再撩她。

怎样运用 Cherry-Pick

多人开发的团队项目在代码办理上,许多费事的操作(例如兼并分支代码等)都在相似 Gitlab 的平台上进行,平台能够尽最大限度规范化推广代码的兼并。关于 Gitlab 的介绍,能够检查八戒以前写的文章《在局域网建立一个带 web 操作页面的 git 版本服务器 – Gitlab》。

当然,假如接纳兼并内容的分支不受管控,彻底能够自己在本地经过 git 指令兼并或者用集成 git 功用的客户端可视化操作。带有 git 功用的可视化客户端也比较多,例如 TortoiseGit、SourceTree 和各大热门 IDE 等。

接下来挑选运用 git 指令来简略做个示例。

预备一个本地代码库房

$ cd ~
$ mkdir sample && cd sample
$ git init
Initialized empty Git repository in ~/sample/.git/

添加第一个改动,这里创立一个文本文件 index.txt 并写入字符串 index,然后提交到默许分支 master

$ echo index>index.txt
$ git add .
$ git commit -m "add index file"
[master (root-commit) e423b8c] add index file
 1 file changed, 1 insertion( )
 create mode 100644 index.txt
$ git status
On branch master
nothing to commit, working tree clean

再预备两个特性分支 A 和 B

$ git branch A
$ git branch B
$ git branch
  A
  B
* master

git branch 指令只会创立新分支,但不会切换。

切换到特性分支 B 开始特性开发,这里为演示起见单纯创立一个文件 feature.txt 并写入字符串 feature,然后将工程更新提交到其时分支 B

$ git switch B
Switched to branch 'B'
$ echo feature>feature.txt
$ ll
total 2
-rw-r--r-- 1 Administrator 197121 8 Nov  8 19:22 feature.txt
-rw-r--r-- 1 Administrator 197121 6 Nov  8 19:10 index.txt
$ git add .
$ git commit -m "create feature"
[B a827145] create feature
 1 file changed, 1 insertion( )
 create mode 100644 feature.txt
$ git log --oneline
a827145 (HEAD -> B) create feature
e423b8c (master, A) add index file

能够看到本地作业目录,在只要 index.txt 文件的基础上,多了个 feature.txt 文件。

除了 git switch 能够切换分支,还有 git checkout 也可,两者作用根本一样,可是建议一致运用 switch,避免记忆负担。

运用 git log 检查其时分支的提交日志,后边的选项 --oneline 会将日志信息简化到一行,方便检查提交记录比较多的情况。

当特性开发到这个阶段时,忽然发现工程代码其实有个 bug,自己发现—快乐坏了。已然分支 B 和 分支 A 都是从主线分支分叉而来,那么分支 A 的代码中也会存在相同的 bug。

着手修正分支 B 的 bug,这里简略起见只是创立一个文件 fix.txt 并写入字符串 fix,然后提交更新到库房

$ echo fix>fix.txt
$ git add .
$ git commit -m "fix bug"
[B 797dfa4] fix bug
 1 file changed, 1 insertion( )
 create mode 100644 fix.txt
$ git log --oneline
797dfa4 (HEAD -> B) fix bug
a827145 create feature
e423b8c (master, A) add index file

那么怎样把分支 B 里刚刚修正的 bug 更新应用到分支 A 中呢?假如运用 merge 兼并,那么分支 B 中还未开发完结的特性功用也会被一同同步到分支 A 中,这样不是咱们想要的成果,于是能够针对某些已提交的 commit 履行 git cherry-pick。

在履行 cherry-pick 时需求已提交 commit 的 hash。从上面分支 B 的提交日志能够找到这串 hash

...
797dfa4 (HEAD -> B) fix bug
...

797dfa4 便是咱们需求的 commit hash,虽然它不是一串完整的 hash,可是 git 只对比前面一部分就够了。

切换到分支 A 中,然后 cherry-pick

$ git switch A
Switched to branch 'A'
$ git log --oneline
e423b8c (HEAD -> A, master) add index file
$ git cherry-pick 797dfa4
[A e27c778] fix bug
 Date: Wed Nov 8 19:48:36 2023  0800
 1 file changed, 1 insertion( )
 create mode 100644 fix.txt
 $ ll
total 2
-rw-r--r-- 1 Administrator 197121 4 Nov  8 20:01 fix.txt
-rw-r--r-- 1 Administrator 197121 6 Nov  8 19:10 index.txt
$ git log --oneline
e27c778 (HEAD -> A) fix bug
e423b8c (master) add index file

能够看到履行 git cherry-pick 后,本地目录下也有了 fix.txt 文件,也便是分支 B 中做了修正 bug 的更新内容已经同步到分支 A 中,兼并成果成功。

圈套

上面的兼并进程是很理想化的顺畅,可是现实往往有些扎手。

比方,在分支 B 中,假如其时开发特性功用时刚好修改了有几行主线分支的原有代码,而修正 bug 时相同改动了这几行代码,那么在 cherry-pick 时有极大可能会出现兼并抵触,导致兼并失利,这时需求手动再处理抵触的代码段,保存符合修正 bug 目的的代码。

这种抵触,在 merge 操作中也很常见,原因根本相似。

所以说,cherry-pick 虽然很强大,但不是万能的,不要过度运用,运用时应该慎重。

一般在 merge 兼并操作能满足运用需求的情况下,优先建议运用 Merge,而不是 cherry-pick,只要在两个分支不是能够彻底兼并的前提下,再考虑运用 cherry-pick。因为 cherry-pick 在设计时便是为了解决某些指定 commit 的兼并,而非悉数。

这便是 cherry-pick 的桃色圈套,你说呢?