记一次"诡异"的git merge错误
前言
今天照常开发,在日常部署测试的时候进行git merge 竟然出现了"代码丢失"的情况,相当诡异,特此记录。
问题由来
首先介绍下公司的日常发布测试的策略,公司使用git进行代码管理。如果某应用同时有多人开发,采用的方式是A会基于master新建feature_a分支,B基于master新建feature_b分支,然后在日常测试部署的时候通常会新建一个新的tag分支,如tag/20160711_test_xxxx,然后将要测试部署的分支feature_a和feature_b合并到tag分支上去,然后使用tag分支部署并且测试。这样做是完全没有问题的,但是诡异的事情发生了,今天这么操作的时候合并出的tag分支丢了一行import,mavan编译一直出错
问题描述与分析
最开始碰到这个问题的时候我一直以为是发布构建系统的问题(公司内部系统),后来又怀疑是我在解决merge冲突的时候手贱删除了这行,重新merge还是出现这个问题。后来由于还得开发其他功能,想着要不先让feature_b分支先单独发布测试好了。一直到下午手里的事情忙完了准备搞定这个问题,发现同事feature_b分支还是没有测试好,想着不能等他了,开始着手解决这个问题。
问题描述如下,feature_a和feature_b同时修改了某个文件xx.java,feature_a上xx.java大概类似于:
package xxx.xx
import com.xx.xx.A
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
public void aSpecMethod(A a){
}
}
feature_b上的xx.java大概类似于:
package xxx.xx
import com.xx.xx.B
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
public void bSpecMethod(B b){
}
}
按理说合并出来的代码应该类似于:
package xxx.xx
import com.xx.xx.A
import com.xx.xx.B
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
public void aSpecMethod(A a){
}
public void bSpecMethod(B b){
}
}
但是最终合并出来的代码却是类似于这样:
package xxx.xx
import com.xx.xx.B
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
public void aSpecMethod(A a){
}
public void bSpecMethod(B b){
}
}
筒子们发现问题了吗,NMmerge之后丢了import com.xx.xx.A这样,maven编译一直报解析不了A的错误,A都没引进来,能解析就怪了...
我也是年轻呀,开始的一个小时一直纠结在是不是这个发布系统的问题(哈哈,对不起,让你背锅了),后来开始仔细研究git merge的原理,期间看到了How-does-Git-merge-work这篇文章讲得比较通透。git merge的原理简单来说就是x+y-w的过程,其中w是x+y的merge base(也就是最近公共祖先),也即是说把y-w(y分支对w分支的改动)patch到x分支上,或者说是把x-w(x分支对w分支的改动)patch到y分支上,具体的做法就是:
merge过程在知道原理之后就开始查看feature_b和feature_a分支分别对于xx.java文件的改动,发现feature_b(同事分支)将import com.xx.xx.A这句删掉了(看到这我就应该反应过来的,但是我还是太连清...),我当时想你删了就删了呗,我在feature_a分支引用进来了呀,最后合并肯定还是存在的嘛(我承认自己没仔细想),我接着checkout 到feature_a看了下,有import com.xx.xx.A这句呀,最后合并的时候肯定应该有的呀,git merge肯定不能因为某些分支删了这行就不新增其他分支的代码,心中一千匹草泥马在奔腾呀!!!!
但是...重要的是但是,我知道git merge首先会找他们的merge base,会基于这个合并,在该场景里应该是master(feature_a和feature_b都是基于master新建的),我就checkout到了master上查看了下,发现master上赫然存在import com.xx.xx.A这行(当时xx.java没有并没有引用到A.java类,这个import是多余的),我瞬间反应过来问题了!!
问题解决-真相大白
问题就在于x+y-w时和w是强相关的,而在这个案例里面,w(也就是master)的代码为:
package xxx.xx
import com.xx.xx.A
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
master中虽然没有引用A,但是却把A引进来了(应该是某次删除代码的时候忘了把Import删掉了)
先分析下xx.java的合并过程吧
x:代表feature_a
w:master
y:代表feature_b
合并过程为x+(y-w)
y-w在这个过程中表现
增加
-import com.xx.xx.A
+import com.xx.xx.B
+public void bSpecMethod(B b){
+}
x为
package xxx.xx
import com.xx.xx.A
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
public void aSpecMethod(A a){
}
}
合并出来的代码应该为
package xxx.xx
import com.xx.xx.B
import com.xx.xx.Common
class xx {
public void commonMethod(Common common){
}
public void aSpecMethod(A a){
}
public void bSpecMethod(B b){
}
}
哈哈,import com.xx.xx.A就这么丢了,而且还就应该丢,丢才是正确的!
筒子们,看出来问题了吗,问题就在于同事B看到xx.java中A并没有被引用的时候把A 的import干掉了,而我在xx.java中加入的方法刚好用到了A(我以为A是我import进去的,没想到竟然master上竟然存在),造成结果就是git 记住的改动是feature_b把A的import干掉了,feature_a对这个import没有修改,结果就是...
知道了原因,解决就很简单了,故意将Import A挪个位置并Push让git意识到change,这样下次merge的时候git既会知道feature_b的改动也会知道feature_a的改动会触发一次merge conflict,然后手动解决就好了
警示
在对类进行修改删除了某些方法时,一定要将无用的import清理掉,保持import区的干净!!!