V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
git
Pro Git
Atlassian Git Tutorial
Pro Git 简体中文翻译
GitX
aheadlead
V2EX  ›  git

一个诡异的 cherry-pick 问题

  •  
  •   aheadlead · 2017-05-24 11:19:17 +08:00 · 2584 次点击
    这是一个创建于 2741 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大家好,我是 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$
    
    5 条回复    2017-05-25 15:30:03 +08:00
    doctorlai
        1
    doctorlai  
       2017-05-24 21:34:57 +08:00
    竟然是中文的.
    SoloCompany
        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 上面
    aheadlead
        3
    aheadlead  
    OP
       2017-05-25 09:07:17 +08:00
    @SoloCompany 非常感谢您的回复

    请问这个机制有没有特别的名字?
    SoloCompany
        4
    SoloCompany  
       2017-05-25 15:26:20 +08:00   ❤️ 1
    @aheadlead 应该没有吧,你可以直接 man git-merge 文档, 如果你不希望 git 自动检测文件移动, 可以使用 -Xno-renames, 你甚至可以使用 -Xrename-threshold 来控制相似度阈值, 这本身是一个缺点 (不像 svn 那样有准确的 copy 跟踪), 但在大部分场景下应该可以很好的工作
    aheadlead
        5
    aheadlead  
    OP
       2017-05-25 15:30:03 +08:00
    @SoloCompany 非常感谢!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5562 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 56ms · UTC 01:35 · PVG 09:35 · LAX 17:35 · JFK 20:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.