1

I have cherry-picked one commit from the other developers for some work.

After few hours one of the developer has updated his patch.

If i cherry-pick new patch then there are conflicts.

  1. Two added files: - in case new files are present in same patch
  2. Both Modified: - same file in old patch and new patch is modified.

Is there any way to discard previous patch Or I have to manually resolve the conflict?

Shayki Abramczyk
  • 36,824
  • 16
  • 89
  • 114
user4828248
  • 39
  • 1
  • 7

1 Answers1

2

You have two direct and obvious options:

  • Add a second commit that undoes the first cherry-pick. (Now you have a source snapshot that looks as if you never did the first cherry-pick.)

  • Remove the first cherry-pick, i.e., alter your commit history.

Which of these to use depends on many things, including whether you're of the school that says "never rewrite any history ever" (in which case you must use the first approach) or whether you have additional commits beyond the cherry-picked one, in graph order.

Remember that each Git commit is uniquely identified by its hash ID. If we use uppercase letters to represent those commits (instead of raw hash IDs) we can draw a clearer picture of what is going on. For instance, suppose we start out with this series of commits:

... <-F  <-G  <-H   <--yourbranch (HEAD)
       \
        I  <-J   <--origin/theirbranch

You then decide that for whatever reason, you like their commit J, so you run git cherry-pick <hash-of-J> or git cherry-pick theirbranch. This copies the effect of commit J, making a new commit. We could call this commit K but let's use J' to indicate that it's a copy of J:

...--F--G--H--J'  <-- yourbranch (HEAD)
      \
       I--J   <-- origin/theirbranch

At some point in the future, they—whoever they are—have discarded their commit J in favor of their new and improved commit K, so that after you run git fetch you have:

...--F--G--H--J'  <-- yourbranch (HEAD)
      \
       I--K   <-- origin/theirbranch

(Commit J has vanished entirely: new commit K points back directly to I. This is that "history rewriting" that people talk about.)

You can rewrite your own history, at this point, using git reset --hard. Of course this throws out any work you're doing, but let's suppose you did not do any new work so that this is OK:

             J'  [abandoned]
            /
...--F--G--H   <-- yourbranch (HEAD)
      \
       I--K   <-- origin/theirbranch

You can now cherry-pick their commit K more easily since it does not conflict with your copy J' of their original J. This results in you having:

             J'  [abandoned]
            /
...--F--G--H--K'  <-- yourbranch (HEAD)
      \
       I--K   <-- origin/theirbranch

But suppose, on the other hand, that you do have work that depends on your J' copy of their J. For instance, suppose you have made a half dozen more commits since you cherry-picked their J to make your J'? Then you have this right now:

...--F--G--H--J'-L--M--N--O--P--Q   <-- yourbranch (HEAD)
      \
       I--K   <-- origin/theirbranch

It's now much harder for you to get rid of your J': it's embedded in your history, which extends all the way back from Q to F.

You can use interactive rebase (git rebase -i) to attempt to pluck J' out of your history by copying commits L-M-N-O-P-Q to new commits L'-M'-N'-O'-P'-Q' that are a lot like the originals, but skip J':

             J'-L--M--N--O--P--Q   [abandoned]
            /
...--F--G--H--L'-M'-N'-O'-P'-Q'  <-- yourbranch (HEAD)
      \
       I--K   <-- origin/theirbranch

(Notice that this looks a lot like repeated cherry-picking. That's because that's what git rebase is, it's repeated cherry-picking!) But if that's too painful, or objectionable for other reasons, you can now make a new commit R that reverses the effect of your copy J' of their original J, and add that to your branch:

...--F--G--H--J'-L--M--N--O--P--Q--R   <-- yourbranch (HEAD)
      \
       I--K   <-- origin/theirbranch

New commit R may be easy to make (or may not), by running:

git revert <hash-of-J>

The git revert command tells Git: Figure out what changed in the given commit, and make a new commit that has the effect of undoing those changes. For every line I added to some file, remove that line from that file. For every line I removed from some file, put that line back. For every file I deleted wholesale, bring that file back; for every file I created from scratch, remove that file entirely.

When this process completes, you have a snapshot in commit R that looks as though commit J' never happened. (If your changes in commits L through Q conflict with this attempt to back out J', you will generally see a merge conflict during the revert process. Note that if this is the case, you will generally see the same conflicts if you use an interactive rebase to discard commit J' while copying L-through-Q to new commits.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • torek, is above explantion is true for the new patch as well, i.e if other developer does commit --amend to the J rather commiting new K. – user4828248 Aug 29 '18 at 07:11
  • `git commit --amend` is probably *how* the other developer created K-instead-of-J. All that `--amend` really does is make the *new* commit point back to the *current commit's parent*, effectively shoving the current commit out of the way at the end of the line. You literally can't change a commit, and `git commit --amend` doesn't try to do so: it just makes a *different* commit be the last one on the branch. – torek Aug 29 '18 at 14:58