大家好,我是 git 萌新(刚开始正儿八经用 git )
在学习 cherry-pick 的时候,发现 git 的 merge 看起来并不单是会对同名文件进行 merge。而看起来 git 自有一套机制去追踪文件的名字变化。
实际场景是我在两个分支上,分支 B 上的文件 2 是用 cp 命令,从分支 A 复制文件 1 过来的。而随后我对分支 A 上的文件 1 修改并 commit 之后,在分支 B 上 cherry-pick 分支 A 修改文件 1 的那个 commit。
居然神奇的修改了分支 B 上的文件 2 !
本小白不太理解为什么会改掉分支 B 上的文件 2
下面是一个完整的重现过程,希望朋友们指点一下本蒟蒻 感谢各位大哥
aheadlead@my-computer:~$ mkdir git-playground
aheadlead@my-computer:~$ cd git-playground/
aheadlead@my-computer:~/git-playground$ git init
初始化空的 Git 版本库于 /home/aheadlead/git-playground/.git/
aheadlead@my-computer:~/git-playground$ echo "README" > README
aheadlead@my-computer:~/git-playground$ git add README & git commit -m "add README"
[1] 19843
[master (根提交) 04cbbb4] add README
1 file changed, 1 insertion(+)
create mode 100644 README
[1]+ 已完成 git add README
aheadlead@my-computer:~/git-playground$ git checkout -b A
切换到一个新分支 'A'
aheadlead@my-computer:~/git-playground$ printf "11111\n22222\n33333\n44444\n55555\n" > 1
aheadlead@my-computer:~/git-playground$ cat 1
11111
22222
33333
44444
55555
aheadlead@my-computer:~/git-playground$ git add 1 & git commit -m "add 1"
[1] 19871
[A 8162adc] add 1
1 file changed, 5 insertions(+)
create mode 100644 1
[1]+ 已完成 git add 1
aheadlead@my-computer:~/git-playground$ git lg
* 8162adc - (2 秒钟前) add 1 - aheadlead (HEAD, A)
* 04cbbb4 - (2 分钟前) add README - aheadlead (master)
aheadlead@my-computer:~/git-playground$ cp 1 2
aheadlead@my-computer:~/git-playground$ git checkout master
A 2
切换到分支 'master'
aheadlead@my-computer:~/git-playground$ git checkout -b B
A 2
切换到一个新分支 'B'
aheadlead@my-computer:~/git-playground$ git add 2
aheadlead@my-computer:~/git-playground$ git commit -m "add 2"
[B be0cc6d] add 2
1 file changed, 5 insertions(+)
create mode 100644 2
aheadlead@my-computer:~/git-playground$ git lg
* be0cc6d - (2 秒钟前) add 2 - aheadlead (HEAD, B)
| * 8162adc - (2 分钟前) add 1 - aheadlead (A)
|/
* 04cbbb4 - (4 分钟前) add README - aheadlead (master)
aheadlead@my-computer:~/git-playground$ git checkout A
切换到分支 'A'
aheadlead@my-computer:~/git-playground$ sed 's/33333/?????/g' -i 1
aheadlead@my-computer:~/git-playground$ cat 1
11111
22222
?????
44444
55555
aheadlead@my-computer:~/git-playground$ git add 1
aheadlead@my-computer:~/git-playground$ git commit -m "replace ????? from 33333"
[A 76eda04] replace ????? from 33333
1 file changed, 1 insertion(+), 1 deletion(-)
aheadlead@my-computer:~/git-playground$ git checkout B
切换到分支 'B'
aheadlead@my-computer:~/git-playground$ git lg
* 76eda04 - (10 秒钟前) replace ????? from 33333 - aheadlead (A)
* 8162adc - (3 分钟前) add 1 - aheadlead
| * be0cc6d - (66 秒钟前) add 2 - aheadlead (HEAD, B)
|/
* 04cbbb4 - (5 分钟前) add README - aheadlead (master)
aheadlead@my-computer:~/git-playground$ git cherry-pick 76ed
[B 5bee333] replace ????? from 33333
1 file changed, 1 insertion(+), 1 deletion(-)
aheadlead@my-computer:~/git-playground$ ls
2 README
aheadlead@my-computer:~/git-playground$ cat 2
11111
22222
?????
44444
55555
aheadlead@my-computer:~/git-playground$
1
doctorlai 2017-05-24 21:34:57 +08:00
竟然是中文的.
|
2
SoloCompany 2017-05-25 01:54:40 +08:00 1
首先,git 是缺少文件复制 /移动跟踪功能的,但却能实现文件的复制 /移动 (参考 log — follow)
其次,没错,git 的跟踪完全就是靠猜,根据每次 commit 的变化, 文件相似度, 文件名重合度来猜是否发生了复制 /移动 最后,因为一切都是靠猜,cherry-pick 的时候,因为 B 分支不存在复制后的文件 2,就会尝试查找可能的文件进行合并,由于文件 2 在改动前内容和文件 1 完全相同,sha1 完全一样,在分支 A 上能匹配到文件 1,所以就会直接把文件 2 的改动应用到文件 1 上面 |
3
aheadlead OP |
4
SoloCompany 2017-05-25 15:26:20 +08:00 1
@aheadlead 应该没有吧,你可以直接 man git-merge 文档, 如果你不希望 git 自动检测文件移动, 可以使用 -Xno-renames, 你甚至可以使用 -Xrename-threshold 来控制相似度阈值, 这本身是一个缺点 (不像 svn 那样有准确的 copy 跟踪), 但在大部分场景下应该可以很好的工作
|
5
aheadlead OP @SoloCompany 非常感谢!
|