What is the difference between git cherry-pick and git format-patch | git am?

后端 未结 2 1672
北荒
北荒 2020-12-11 17:13

I sometimes need to cherry-pick a tag with a certain fix into my branch, and used to do so via

git cherry-pick tags/myfix

This works, but c

2条回答
  •  春和景丽
    2020-12-11 17:38

    Mark Adelsberger's answer is correct (and upvoted, and probably you should accept it). But there is a historical oddity to note here.

    In fact, cherry-pick was once implemented as git format-patch | git am -3, and git rebase still uses this particular method of copying commits for some kinds of rebase.1 The problem here is that this fails to detect renamed files, and sometimes—under (rare) conditions that are hard to describe, but I will try—mis-applies changes where a proper three-way merge would apply them correctly. Consider for instance this case:

    @@ -123,5 ... @@
         }
       }
    -  thing();
       {
         {
    

    where the surrounding context around a deleted line is just braces (or worse, white-space)—something that uselessly matches, in other words. In your version of this same file, due to some other event, what were lines 123-through-127 are now earlier or later in the same file. Let's say, for instance, that they're now lines 153-158. Meanwhile lines 123-127 in your file read:

        }
      }
      thing();
      {
        {
    

    but these lines are correct: the call to thing() that should be deleted (because it's wrong) has moved down, but there's a call to thing() that should not be deleted, in that same place.

    A three-way merge will compare the merge base against your version, and maybe—maybe, depending on luck and diff-able context—discover that you inserted various lines so that the erroneous call that should be deleted is now on line 155, not line 125. It will then perform the correct deletion since it knows that what was the base's line 125 is your line 155.

    The rename-detection is the more important of these two differences between format-patch-then-apply vs true three-way-merge, but both do matter, in some cases. Running git cherry-pick does the more thorough, slower, more-often-correct thing.


    1In particular, only the non-interactive git rebase ever uses format-patch, and even then, only if you don't use the -m option, specify a merge strategy with -s, or specify an extended-option with -X. Any of these three force the non-interactive rebase to use the cherry-pick method.

    Note that Git documentation calls the -X arguments "strategy-option options" or "strategy-option arguments", which is a very clumsy phrase either way. I like the word "extended" here since it explains why it's -X, i.e., eXtended. An extended option is simply an option passed to the strategy you choose with -s: Git does not know what additional options each -s understands, so whatever you give to -X, Git gives to the chosen strategy, and the strategy itself then either accepts the -X option and does something, or complains about it as unknown.

提交回复
热议问题