先吹一波
git
是一个非常好用的版本管理工具,自从用上了git,感觉其他神马都是浮云有木有!
然而,git有时候也会给你出点难题。
踩坑过程
git
的一个不足,导致我们多出了计划之外的工作量。
公司项目组件化重构,原来的项目结构是:
| -- MyProject
| -- gradle
| -- app
| -- build.gradle
| -- src
| -- build.gradle
| -- settings.gradle
组件化之后,是这样的:
| -- MyProject
| -- gradle
| -- layer_business
| -- app
| -- build.gradle
| -- src
| -- device
| -- build.gradle
| -- src
| -- transaction
| -- user
| -- ...
| -- layer_business_component
| -- comp_share
| -- comp_version
| -- ...
| -- comp_photo
| -- layer_business_base
| -- base_ui
| -- build.gradle
| -- src
| -- ...
| -- build.gradle
| -- settings.gradle
原来代码全都堆在app module
里面,后来移动放到了./layer_business/app里面,进一步又抽取分到各个子module里面。
而在这个过程中,业务开发还在旧的代码分支上进行。由于重构和业务开发是不同的两拨人,且考虑到此重构阶段代码改动较大,而业务开发又时间紧迫,所以在不同的分支进行,计划重构阶段性目标完成后合并业务分支的代码进来。
这样就出现了这种情况:
两个分支,一个分支对某些文件进行了修改,而另一个分支对同样这些文件移动了位置并且做了修改
那么这样还能不能成功合并?
不能。
合并之后的结果是:
| -- MyProject
| -- gradle
| -- app
| -- build.gradle
| -- src
| -- layer_business
| -- app
| -- build.gradle
| -- src
| -- device
| -- build.gradle
| -- src
| -- transaction
| -- user
| -- ...
| -- layer_business_component
| -- comp_share
| -- comp_version
| -- ...
| -- comp_photo
| -- layer_business_base
| -- base_ui
| -- build.gradle
| -- src
| -- ...
| -- build.gradle
| -- settings.gradle
你没看错,有两个app module存在:
./app/
./layer_business/app/
点击进去看,发现两个分支的代码并没有合并,两边修改的文件,同时存在。
原因分析
其实也不能叫分析,因为最后也没明确找到文档有此类说明。以下纯属猜测:
merge
之后,git
无法对此文件内容进行合并并且给出冲突定位,为什么呢?因为git
没有我们想象的那么聪明,它不知道,如果把这两个文件合并,那么对于文件位置,以那边的修改为准?它到底是该移到新目录,还是该留在原位置?所以git
索性不合并了,直接告诉你,对于主动发起合并的那个分支来说,这边做了修改,那边做了删除,怎么合并,你自己最清楚,所以你自己来!
起初我也不信伟大的git怎么会有这种问题呢,于是我动手验证了下:
验证问题
不信的同学可以跟着我进行一番验证:
$ mkdir gitest
$ cd gitest/
$ git init
Initialized empty Git repository in /Users/leonhe/repos/gitest/.git/
$ mkdir dir1
$ touch dir1/T.java
$ echo "initiation">dir1/T.java
$ git add .
$ git commit -m "initiation"
[master (root-commit) 28aac23] initiation
1 file changed, 1 insertion(+)
create mode 100644 dir1/T.java
$ git checkout -b develop
Switched to a new branch 'develop'
$ mkdir dir2
$ mv dir1/T.java dir2
$ echo "working on develop">>dir2/T.java
$ git add .
$ git commit -m "working on develop"
[develop 76e545e] working on develop
2 files changed, 2 insertions(+), 1 deletion(-)
delete mode 100644 dir1/T.java
create mode 100644 dir2/T.java
$ git checkout master
Switched to branch 'master'
$ echo "working on master">>dir1/T.java
$ git add .
$ git commit -m "working on master"
[master 16ecbc8] working on master
1 file changed, 1 insertion(+)
$ git merge develop
CONFLICT (modify/delete): dir1/T.java deleted in develop and modified in HEAD. Version HEAD of dir1/T.java left in tree.
Automatic merge failed; fix conflicts and then commit the result.
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
new file: dir2/T.java
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
deleted by them: dir1/T.java
$
上面的操作就是:
- 初始化
git
仓库 - 在
master
分支上创建dir1目录,并在dir1下创建T.java文件。 - 编辑T.java后,commit
- 切出一个
develop
分支,然后创建dir2目录 - 将dir1下的T.java文件移动到dir2下
- 编辑dir2/T.java并提交到develop分支
- 切回master
- 编辑dir1/T.java,commit
- merge
结果就是
CONFLICT (modify/delete): dir1/T.java deleted in develop and modified in HEAD. Version HEAD of dir1/T.java left in tree.
Automatic merge failed; fix conflicts and then commit the result.
再执行git status
查看详细状态
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
new file: dir2/T.java
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
deleted by them: dir1/T.java
$
git 提示,dir2/T.java是develop
分支新加的,而我们master
上的dir1/T.java文件被他们删掉了。
那么此时的文件目录结构是怎样的呢?
| -- gitest
| -- dir1
| -- T.java
| -- dir2
| -- T.java
merge
后,两个分支的T.java文件同时存在!
那么此时的两个T.java文件里是否有两个分支同时做的修改导致的冲突记录呢?
先看看dir1/T.java:
$ cat dir2/T.java
initiation
working on master
没有,再看看dir2/T.java:
$ cat dir2/T.java
initiation
working on develop
并没有任何冲突!
那么对于这次merge
,我们只能手动去合并了吗?至少我在网上找了一圈,没有找到好的办法。有的话,请告诉我,谢谢!
小结
当预计会出现这代码的改动时,一定要尽量频繁地进行两边同时进行的分支的合并,如果等到两边都改动很大了的时候才来合并,那就等着吧CONFLICT (modify/delete)。。。