【问题标题】:How does git merge after cherry-pick work?樱桃挑选后git合并如何工作?
【发布时间】:2013-01-07 07:27:10
【问题描述】:

假设我们有一个master 分支。

然后我们创建一个newbranch

git checkout -b newbranch

newbranch 提交两个新的提交:commit1commit2

然后我们切换到master并制作cherry-pick

git checkout master
git cherry-pick hash_of_commit1

查看gitk,我们看到 commit1 及其精选版本具有不同的哈希值,因此从技术上讲,它们是两个不同的提交。

最后我们将newbranch合并成master

git merge newbranch

并看到这两个具有不同哈希值的提交合并没有问题,尽管它们暗示应该应用两次相同的更改,因此其中一个应该失败。

git 是否真的在合并时对提交的内容进行智能分析,并决定不应两次应用更改,或者这些提交在内部被标记为链接在一起?

【问题讨论】:

    标签: git git-merge cherry-pick


    【解决方案1】:

    简答

    别担心,Git 会处理的。

    长答案

    不像例如SVN1,Git 不以增量格式存储提交,而是基于快照2,3。虽然 SVN 会天真地尝试将每个合并的提交作为补丁应用(并且由于您描述的确切原因而失败),但 Git 通常能够处理这种情况。

    合并时,Git 会尝试将两个 HEAD 提交的快照合并到一个新的快照中。如果两个快照中的一部分代码或文件是相同的(即,因为一个提交已经被精心挑选),Git 不会碰它。

    来源

    1Skip-Deltas in Subversion
    2Git Basics
    3The Git object model

    【讨论】:

    • 实际上,我会说you should worry about merging 并且“git 会处理它”并不是一个好的经验法则。
    • 事实上在某些情况下合并会导致重复的内容。 Git 有时会处理它,但有时不会。
    • 这是非常错误的。 Got 几乎以所有可转换的形式存储文件文件。如果我没记错的话,SVN 是用来存储快照的。
    • @he_the_great,不。 SVN 的 skip-delta 存储格式(!= 快照)有据可查 in the manual。我真的不明白你所说的 condevable 是什么意思。我不是母语人士,但我很确定这不是一个真实的词。
    • @he_the_great 但即使是打包文件,文件的任何给定哈希都会产生完整的文件。是的,它使用增量进行压缩,但它不是提交中更改的增量,而是文件哈希之间的增量。就提交对象而言,它正在引用一棵树,该树正在引用完整文件的哈希值。在引擎盖下数据被压缩不会影响 git 的工作方式。就提交而言,Git 存储完整文件,据我了解,SVN 存储提交的增量。
    【解决方案2】:

    在这样的合并之后,您可能在历史记录中进行了两次精心挑选的提交。

    防止这种情况的解决方案我引用article 建议具有重复(樱桃采摘)提交的分支在合并之前使用rebase:

    git cherry-pick 之后的 git 合并:避免重复提交

    假设我们有 master 分支和一个分支 b:

       o---X   <-- master
        \
         b1---b2---b3---b4   <-- b
    

    现在我们迫切需要 master 中的提交 b1 和 b3,但不是 b 中的剩余提交。所以我们要做的是检查主分支 和 cherry-pick 提交 b1 和 b3:

    $ git checkout master
    $ git cherry-pick "b1's SHA"
    $ git cherry-pick "b3's SHA"
    

    结果是:

       o---X---b1'---b3'   <-- master
        \
         b1---b2---b3---b4   <-- b
    

    假设我们在 master 上再次提交,我们得到:

       o---X---b1'---b3'---Y   <-- master
        \
         b1---b2---b3---b4   <-- b
    

    如果我们现在将分支 b 合并到 master:

    $ git merge b
    

    我们会得到以下结果:

       o---X---b1'---b3'---Y--- M  <-- master
         \                     /
          b1----b2----b3----b4   <-- b
    

    这意味着 b1 和 b3 引入的更改将在 历史。为了避免这种情况,我们可以变基而不是合并:

    $ git rebase master b
    

    这会产生:

       o---X---b1'---b3'---Y   <-- master
                            \
                             b2'---b4'   <-- b
    

    最后:

    $ git checkout master
    $ git merge b
    

    给我们:

       o---X---b1'---b3'---Y---b2'---b4'   <-- master, b
    

    编辑 大卫柠檬的评论提出的更正

    【讨论】:

    • 关于变基的重要提示!它会自动“跳过”所有精心挑选的提交。
    • 老实说,这听起来好得令人难以置信,我必须亲眼看到它。也 rebase 修改提交,你的最后一个时间线应该是 ---Y---b2'---b4'
    • 完美运行。如果您不想在历史上两次进行精心挑选的提交,这将非常有帮助。
    • 难道不应该注意,虽然 rebase 很漂亮,但使用它的危险是从旧 b 创建的任何分支或分支都将不同步,用户可能需要求助于东西比如 git reset --hard 和 git push -f?
    • @JHH 这就是我们在这里重新设置本地分支的原因
    猜你喜欢
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-11
    • 2016-04-16
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多