做者:咕咚移动技术团队-nanchengit
题目看起来很像是提供解决方案的文章,但实际上我并不会给你们直接提供解决方案,咱们追求的历来不该该是答案,而是探索的过程。固然,若是你只想查看答案的话,请直接拉到文章最底部。vim
相信你们都知道,Git 相比于 SVN,优点不言而喻,以至于如今大多数公司的项目都在采用 Git 进行管理。做为一个开发人员,对 Git 的使用天然应该是驾轻就熟。post
若是你还不会使用 Git 的话,那我劝你仍是不要声张,好好的去学习一番,再本身弄个实验项目走一下流程,以避免遭到同事的鄙视。学习
每一个公司都会有本身不同的 Git 分支管理规范,特别是在开发人员较多的公司,Git 的分支管理规范就显得更加剧要。前面比较出名的 Git Flow 分支管理策略相信很多人都已经了解了,不熟悉的固然也能够去看看:nvie.com/posts/a-suc…code
Git Flow 管理方式把项目分为 5 条线,一般会是下面的管理方式。cdn
实际上,若是你熟悉 Git 的话,你会很快发现上面的管理方式会存在历史提交很是混乱的缺点,但以为不失为一个 Git 分支管理的经典。实际上,咱们能够用 rebase 去替换 merge 让 commit 看起来更加清晰。对 rebase 和 merge 的优劣对比这里暂不作讲解,感兴趣的能够直接 Google 搜索。blog
下面就给你们分享一下发生在咕咚项目的一次坑爹的 Git 体验。生命周期
咕咚项目组并无对开发者限制 Develop 分支和 Master 分支的权限,咱们暂时并无一个专门作代码 Review 和 PR 的角色,其实必定意义上也提现了团队对每一个人的信任。开发
咱们依然会基于 Develop 作开发主线,每一个需求迭代期,团队成员会从 Develop 拉取本身的分支,并命名于 feture/XX,而后各自在本身的分支上进行开发。get
因为你们开发业务上的不一样,因此在需求开发完毕,整合代码到 Develop 分支的时候,通常不会出现太多冲突的状况。
而我这边交接一个需求时,采用 merge 的时候出现了一个奇怪的问题,咱们姑且来重现一下事故现场。
首先使用 git branch
查看一下当前咱们的本地分支。
这里先简单提一下咱们要作的操做。
"feature8.28_buyGifts" 是咱们同事的分支,基于 "release8.27.0" 拉取,而 "feature8.29.0_nanchen" 是个人分支,基于 "release8.28.0" 分支拉取,因此我这边的分支包含了最新的代码。
如今因为某些缘由,我须要把同事的 "feature8.28_buyGifs" 分支代码合并到个人分支上,直接接手他的代码进行开发。
就不要吐槽为啥不按照功能搞分支开发了,缘由是由于他那边代码基本已经完成,如今只须要少许修改。
因此咱们就采用 git merge <branch>
命令进行 merge 操做。
咱们用 git status
更容易看明白冲突了什么。
能够看到,上面冲突的文件全是和同事开发的需求出现的冲突,因此出现这个冲突其实使人很是懊恼,由于是不可能有其余同事改动到这些文件的。
为了验证本身的想法,咱们随意打开一个文件查看。这里就采用 vim <filename>
查看第一个文件。
正如咱们所想,确实和同事编写的需求 Presents
类有关系,但看冲突内容就更一脸懵逼了,由于看起来,这应该是一个不会冲突的 merge。
因而赶忙使用 git merge --abort
撤销此次 merge。再在 "origin/feature8.29.0_nanchen" 查看咱们刚刚的文件提交历史。
能够很清晰的看到,确实是最近没有任何的修改记录。
一个 7 个月都没人动的文件,竟然 merge 的时候发生了冲突!这让我一脸懵逼。(手动黑人问号)
使用 git lg
查看一下该分支的提交历史,咱们但愿从中能获得某些思路。
注意其中红框中的 commit,咱们这位同事以前想往 "release8.28.0" 合并他分支的代码,后面又由于某些缘由,但愿撤销此次提交,他采用了 revert 进行处理。虽然 revert 对文件没有提交记录,但 Git 却认为咱们在当前分支更改了这些文件,因此在咱们 git merge
的时候,Git 认为这是一次冲突,并选择了告知咱们。
如若如咱们所想,那咱们只须要撤销此次 revert 操做便可。
咱们固然知道,能够经过 reset 命令放弃此次提交,但这里后面已经有了很是多的 commit,显然咱们这样是不行的,咱们须要另辟蹊径。
最容易想到的大概就是直接在 merge 的时候解决冲突了,但经过一系列查看之后,咱们发现文件改动量很是大,直接解决冲突并不是易事。因此咱们仍是得 想办法取消掉此次 revert 的 commit,再进行 merge。
咱们知道,代码回滚有三种方式:reset、checkout,还有咱们的 revert。直观感觉,咱们应该在 reset 上想办法。
咱们来看看 reset 有些怎样的操做方法。
主要想给你们讲讲:--soft 和 --hard 的区别。
咱们常常会用到 git reset --hard <commit>
作「毁尸灭迹」的操做,经常爽到不能自已,由于这不只能够回退到咱们想要的版本,并且还「直接丢弃」了后面提交的代码,真正的「毁尸灭迹」级别的操做。
而另一个 --soft 处理,实际上还具有点人性,虽然一样能够回退到咱们想要的版本,但目标版本后面的提交都还会存放在 stage 区域中,以便后面找出证据。
说到这,彷佛咱们已经有了思路。
git reset --soft <revert 操做的 commit ID>
回退到 revert 操做的版本;git reset --hard <revert 操做的前一个 commit>
干掉那次 revert 提交;固然,细心的你必定会发现,在第 1 步操做后,咱们还必须执行 git stash
命令把全部的改动存到暂存区,再在第 2 步操做后使用 git stash pop
命令取出来,直接进行第 2 步操做确定仍是会毁灭证据的。
这样彷佛能够解决咱们的问题,不过有个弊端:咱们后面那么多的提交被合并成一个提交了,之后咱们就没办法看到了,万一...
很多小伙伴会想到进阶方案:
git checkout feature8.29.0_nanchen
回到咱们的分支;改写历史?等等,好像还有一个操做:rebase。
rebase 是 Git 的一个神奇的命令,前面我也说了,总会有人不喜欢 merge 以后历史的分叉,这种分叉再汇合后会让结构看起来很是混乱,以至于没法管理。若是你不喜欢 commit 历史出现分叉,那 rebase 绝对是你的救星。
改写历史是 rebase 与生俱来的能力。咱们能够用 git rebase -i <commit>
进行历史的改写。
咱们试试看在咱们的项目中直接使用 git rebase -i <commit>
会怎样。
咱们会拿到分支后面的提交历史,而且前面还有一个 Commands。咱们能够从提示中看到,上面全写的 pick 就是表明保持这个提交的意思,edit 表明编辑这次提交...
咱们但愿删除这次 revert 此次提交,那固然咱们最关心的就是 drop 了,甚至咱们能够更加简单粗暴:直接删掉这一行。
而后咱们便开始处理了。
过程当中可能会出现冲突,咱们只须要解决就好。
解决掉冲突后,再使用 git add <filename>
把它们 merge 进去。
oh,咱们看到咱们已经 rebase 成功了。咱们再使用 git lg
查看一下提交历史。
咱们成功改写了历史!
历史改写结束,咱们还要作咱们最开始想作的事情,进行 merge 操做。
能够看到,此次咱们 merge 确实如咱们预期的再也不发生冲突,方案亲测有效!
写了这么多,想必你们对解决方案也算比较清楚了。咱们主要即是采用 git rebase -i <>
操做进入到 commit 历史编辑页面,而后进行历史改写处理!