一个真实的故事
以前上学的时候各种课设作业基本上都是单打独斗地“独立”开发,就算是小组合作,也基本上个人负责个人的开发模块,并不是真正意义上的“多人协作开发”,因此对项目的分支管理一直不是很熟悉......于是,在入职的第二个月,第一次进行多人协作开发时,出了一个小小的状况:
当时有一个大项目因为工程量浩大,开出了很多需求,这些需求又被拆成了各个子任务分配给不同的同事进行开发。提测的时候,大家是把自己的个人featrue分支合并入一个专门的测试分支feature/beta上交由QA进行测试的。不过,由于这个项目开发时间紧、开发任务重,提测之后产品那边仍在源源不断的增加新的需求和修改,我们的代码依然有大量内容需要修改。
QA和产品那面催得急,再加上对业务的不熟悉、频繁地切换分支,到最后,我的好多修改都直接在featrue/beta上操作了TAT......测试进入第二周后,由于修改的地方实在太多,测试分支的代码历史记录十分混乱,并且主R发现每次将自己的开发分支合并进测试分支时,都有很多的冲突需要解决,自己的代码还总是会莫名其妙地被别人覆盖。众所周知,手动解决冲突是个非常繁琐的事情,当时时间紧迫着实令人头大。所以,主R决定将现有的测试分支删除重新建立一个新的,然后负责开发的同事们再将自己的featrue分支重新合并进去。
那么,问题来了。憨憨的我提测后的修改都是直接在测试分支上进行的,一旦删掉该分支,就意味着我的修改都没了(一删回到提测前!)天呐这可真是太可怕了!
不知道各位有没有遇到过和我一样的情况(不会吧不会吧,不会有人和我一样蠢吧?)。幸好当时我关联远程仓库测试分支的本地分支还在,于是,同事帮我将这个本地分支上的commit代码移植到了提测前远程仓库里我自己的开发分支上。而使用的命令,就是git cherry-pick。
上面是我自己亲身经历的事情。在实际开发过程中,我们有时候也会遇到切错基准分支的情况,最终开发完成之后才发现merge不回去了。这种时候,cherry-pick同样可以解决问题。
(第一次看到这个命令语句脑海中的第一反映是「拣樱桃」???好吧,其实cherry有珍贵的意思,这个命令可以理解为「精心挑选」,和它的功能其实还蛮贴切的hhh)
cherry-pick的基本用法
git cherry-pick
上面的命令会获取某一分支的一个提交,并作为一个新的提交引入到当前分支上。
举个例子,现在代码仓库里有master和feature两个分支。
如果我们想将featrue分支上的提交C6应用到master分支上,那么我们可依次执行以下命令:
git checkout master
git cherry-pick C6
上面操作完成之后,master分支就变成了这个样子:
注意,图中的C6'是一个自动生成的新的提交,可以理解为featrue分支上C6的副本。C6'拥有一个新的commit ID。
解决冲突
如果操作过程中发生代码冲突,Git会暂停cherry-pick,让用户决定如何解决冲突:
- --continue
手动解决冲突并将修改的文件重新加入暂存区后,可以使用 git cherry-pick --continue 让cherry-pick过程继续执行。 - --abort
取消此次操作,分支恢复到cherry-pick前的样子,没有改变。 - --quit
退出cherry-pick,但不回到操作前的样子,当前分支中未冲突的内容状态将为modified。
cherry-pick的高级用法
1)批量转移提交
cherry-pick也可以一次转移多个提交。
提交 (start-commitHash, end-commitHash] 之间的所有commit(不包括start-commitHash):
git cherry-pick...
提交 [start-commitHash, end-commitHash] 之间的所有commit(包括start-commitHash):
git cherry-pick^...
注意,无论是对单个commit进行cherry-pick,还是批量处理,一定要根据commit提交的先后顺序,按照时间线的先后顺序来处理。
2)转移到另一个分支
git cherry-pick
上面的命令表示将目标分支
git cherry-pick ..git cherry-pick ^HEAD
如果想要移植某个分支上的所有提交,可以使用上面两个命令,表示应用所有提交引入的更改。这些提交是branchname的祖先但不是HEAD的祖先。
这么说可能有些绕,来看个图:
假设我们现在有三个分支:master、branch1和branch2,它们的提交历史如上图。如果我们使用 git cherry-pick ..branch1 或者 git cherry-pick ^HEAD branch1 ,那么会将属于branch1的但不属于branch2的祖先的所有提交(C3、C4、C5)引入到当前分支branch2上,并生成新的提交。
参考:
[Git] Git整理(五) git cherry-pick的使用
git cherry-pick 教程