导读
很多读者希望鹅厂程序员们分享更多 git 操作技巧。”git坑太多了“、”在工作中我经常遇到这个情况:忙了一天准备提交代码下班,结果 git 合并冲突把刚写好的代码覆盖掉了,血压飙升!““合并前文件还在的,合并后就不见了”,“我遇到 git 合并的 bug 了” ——这是程序员高频遇到的场景。鹅厂毕鸣一如何攻破这个 git 使用时的痛点?欢迎继续阅读。
目录
1 背景
2 强行合并
3 智能合并
4 取巧合并
5 优雅合并
01
背景
在一个岁月静好的一天,作为开发的你来到工位,看了看项目计划和待办事项,你发现,需要按顺序完成两个需求:
需求一:产品列表需求的开发。
需求二:用户管理需求的开发。
其中用户管理需求包括两个部分,即用户配置管理子需求和用户权限管理子需求。
根据前期会议对齐的结论,产品列表需求要求独立上线,产品管理的两个子需求要求一起上线。
于是,你分别从主干拉取了两个分支,一个是 /,用来做产品列表需求的开发,一个是 /,用来做用户管理两个子需求的开发。
然后,岁月静好,你用了两周时间在 / 分支开发完毕了产品列表需求的开发工作,进行提测。
然后切分支到 / 转而进行用户管理需求的开发工作,这个开发工作大概用时一个月,两个子需求各两周的开发周期。
又过了两周,岁月依然静好,你基本开发完用户配置管理子需求,
又过了一周,当你对用户权限管理子需求的开发进行到50%时,
项目节奏突然变了!
经过紧急开会对齐,你得到了一个消息,需求的优先级和上线时间进行了调整,为了能够满足客户要求,产品列表功能需要和用户配置管理子功能后天就要上线,为了提高效率,测试同学将一起测试这两个功能,测试通过后,再合入主干进行冒烟测试,之前的提测不再生效。
至于,用户权限管理子需求的交付时间,依然需要按时完成。
这时,然后你看着眼前的这两个分支,陷入了沉思。
这时,负能量爆棚的你先后尝试了以下几种方案:
我:“跟项目组表示这两个子需求都在一个分支上,无法分开,且代码有关联,所以得等用户权限管理子需求开发完毕后才能提测。”
项目组的商务同学:“已经跟客户承诺,必须XXX前上线,不能等!”
我:“加个班把用户权限管理子需求做完,然后一起上线。”
项目组的测试同学:“十分认同你的工作态度,并表示自己不想加班多写一堆测试用例,也不想多测功能!”
家属同学:“你要是再晚回来就不让你进门了!”
我:“跟项目组直接摆烂,表示只开发完了产品列表功能,用户配置管理子功能需要时间开发。”
项目组的项目管理同学:“进度我天天都在跟,你明明晨会上说用户配置管理子功能做完了!”
我:“决定下次再也不把两个子需求放一个分支了,再信XXX的话我就是狗,并表示一定要解决这个问题,并捍卫工程师“一定能解决工程问题”的尊严。”
然后,你又重新看了下 / 分支的代码,你发现,事情似乎没有这么糟,用户配置管理子功能的代码和正在开发的用户权限管理子需求的代码并没有那么的耦合,你可以通过文件目录来进行简单的区分。
这时,你想到了,可以发起两次向主干的合入,一次是将 / 分支合入 ,一次是将 / 的部分目录合入 。
项目组的测试同学提出了不同意见,他表示,他主要做代码合并前的功能测试,分两次发起合并,除了要做两次功能测试外,还可能会导致两个功能的联动逻辑测试不充分,把问题带到主干,测试同学希望的是,只发起一次合并,这样测试比较完整,问题比较可控:
你想了想似乎很有道理,但似乎又没有道理,这里到底应该选择哪一种其实也是一个有意思的点。
但这其实不是这篇文章的重点,因为不论是哪种方案,都会遇到一个相同的问题:如何将一个分支部分文件/文件夹优雅地合并到另一个分支。
OK,看起来这个问题的解决与否成为你是否成功捍卫工程师尊严的关键环节,那么我们来一起解决它。下面就是捍卫尊严的解决方案:
02
强行合并的方式
事实上 git 是一个功能丰富的命令,比如最常用的切换分支:
git checkout A //切换到A分支
还可以与 git 联合使用:
git branch A //新建A分支
git checkout A //切换到A分支
当然也可以用快捷方式:
git checkout -b A //新建A分支并切换到A分支
同时 git 后面除了跟分支,还可以跟某次提交和文件,这里就涉及到另一个功能:
git checkout [] [--]
即:用于拿暂存区的文件覆盖工作区的文件,或者用指定提交中的文件覆盖暂存区和工作区中对应的文件。
是可选项,如果省略则相当于从暂存区(index)检出。这和 git reset 重置命令(例如 git reset HEAD )大不相同:重置的默认值是 HEAD,而检出的默认值是暂存区。因此重置一般用于重置暂存区(除非使用--hard参数,否则不重置工作区),而检出命令主要是覆盖工作区(如果不省略,也会替换暂存区中相应的文件)。
该命令(包含了路径 的用法)不会改变 HEAD 头指针,主要是用于拿指定版本的文件覆盖工作区中对应的文件。如果省略,则会拿暂存区的文件覆盖工作区的文件,否则用指定提交中的文件覆盖暂存区和工作区中对应的文
举个例子:
如果要放弃修改工作空间内容:
在git add命令执行前可以使用git checkout -- add.txt // 用暂存区的内容覆盖工作区的内容
在git add命令执行后可以使用git checkout HEAD -- add.txt // HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文
当然这两个命令不可逆,所以要慎重操作。
假设我们按照测试同学推荐的方案,即把 / 分支的部分目录合并到 / 分支上 ,且需要合并的目录结构为/src//
步骤如下:
git checkout feature/product_list
git checkout feature/user_manager /src/product/*
意味着将 / 分支的 src/ 文件夹的内容强行覆盖到 / 分支,但这个方法比较暴力,不推荐使用,原因有三个:
03
智能合并的方式
既然强制合并太暴力,那怎么智能合并呢?这里 git 没有直接的命令进行使用,需要一些工作技巧:
事实上 git merge 与 git 是项目中经常使用的命令,有的时候会混淆了两个命令的概念,这里做一下简单的区分。
git merge 即就是常规的合并:
git merge feature //将分支 feature 合并到当前分支上
git 即就是物理意义上的变基:
git checkout feature //切换当前分支为featrue分支
git rebase master // 将当前分支变基到当前分支(即feature分支)
两者的区别如下图所示:
参考资料:
主要的结论是:
在 / 分支的基础上先创建一个新的分支 /。
git checkout feature/product_list
git checkout -b feature/product_list_temp
然后合并 / 分支到 /。
git merge feature/user_manager --on-off
将 / 分支合并到 / 后,这里通过 merge,将 src/ 文件夹下的代码进行合并,并解决了冲突,这时 src/ 的文件夹的代码被智能合并了,代码冲突解决了,同时保留了合并的历史记录。
再用强制合并方式中的 git 命令强制把 分支的 src/ 文件夹合并到 分支。
git checkout feature/product_list
git checkout product_list_temp src/produc
这里解决了强制合并方式的问题2。
至于问题1,保留 分支吧,嗯,虽然不太优雅,但在大的需求修改下,没有人力做细致合并的话,这样也是一个工程上有效的办法。
参考资料:
04
取巧合并的方式
智能合并的方式基本解决了强制合并方式的问题2,但也留下了问题1的坑,那有没有优雅的方法呢?
这里就要具体问题具体分析,首先,如果在 / 分支严格按照需求的顺序进行开发,那在用户配置管理子功能开发完毕的这个 ,其实可以通过 git 命令恢复回来,然后新拉个分支的方式合并回 / 的方式解决。
在 / 分支上通过 在本地会滚到那在用户配置管理子功能开发完毕的节点。
git checkout feature/user_manager
git checkout commmit_id
然后基于 / 分支的这个节点新建分支 /。
git checkout -b feature/tmp_user_manager
将 / 分支合并到 /,这里通过 merge。
git checkout feature/product_list
git merge -b feature/tmp_user_manager
在 / 分支合并到 ,这里通过 merge。
git checkout master
git merge -b feature/product_list
当然,如果在 / 分支交叉顺序对两个子需求进行开发,但每次提交都能是独立为某一个子需求开发提交出来,其实可以通过 git chery-pick 来解决。
智能合并中讲了 git mergr 和 git 两个合并命令的区别,其实还有一种合并命令——gir chery-pick。
git chery-pick 相对于上面两个合并分支的命令,git chery-pick 主要是将某次/某几次提交进行合并。
git -pick 的使用场景就是将一个分支中的部分的提交合并到其他分支,使用以下命令以后,这个提交将会处在 的最前面。
git checkout master
git cherry-pick
参考资料:
如果 / 分支对 src/ 文件夹的修改主要来自于某次或某几次的提交(比如主要是完成某个需求或修改某个缺陷导致的修改),则可以直接使用。
git checkout feature/product_list
git checkpick ...
这样就解决了强制合并方式的3个问题,因为本质上来讲,这次合并就是将 / 分支上几次提交,提交到 / 上来。
05
优雅合并的方式
当然,取巧合并是预设前提的,如果对 src/ 文件夹的修改并不独立,比如,在 / 分支中某次提交中同时顺道为了用户权限管理子需求修改 src/ 和 src/ 两个文件夹怎么办?
一般来说,当你去问组内项目经验丰富的工程师时,大概率他会建议你用智能合并的方式。
如果你在纠结,这样就没有整个文件夹的修改记录了,项目经验丰富的工程师会建议在这次合并的 上写上“欲看记录,去 分支”看,并强调不要删除该分支。
如果你说,我不想这个方案,我就是想在当前分支看到所有修改,并优雅地合并某个文件夹的内容。
这个时候,绝大部分项目经验丰富的工程师会对你执着的精神表示认同,并不想再理你了。
但,既然看到这里了,笔者一定会一个兜底方案。
git checkout --patch source_branch src/product
优雅的代价就是花一定的前置时间打基础。
git -p 类似已交互的形式打补丁。
git 会跟你逐个掰扯 分支上的 src/ 文件夹下的这些文件怎么处理。
是的,只要你愿意一个一个文件掰扯,你就能得到一个有完整提交记录的文件夹。
这时,你可能会有一个疑问,那和我一个一个修改文件有什么区别?
区别就是这样同时保留了代码提交的修改记录!
所以当你花了1个小时去逐个对齐了这个文件夹下每个文件的修改点后,你就可以跟测试说:提测!
———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需109元,全站资源免费下载 点击查看详情
站 长 微 信: nanadh666