THIS USER NEEDS HELP THIS USER NEEDS HELP - 2 months ago 7
Git Question

Roll back to an old commit, but apply back my commits

I have been working on a feature branch

A
that I merged to
dev
branch. I made several more commits to the
dev
branch after merge. However, I realized that the current head of
dev
branch is broken and would like to test my
A
branch by rolling back to a certain commit in
dev
branch and re-applying all the commits of
A
and extra ones I did after merge in
dev
. How could I achieve this?

EDIT: Many people have committed to
dev
, and I merged in my branch
A
which is composed of only my commits. Basically, I want to remove the commits of all other people upto certain old commit.

The rollback is for testing purpose only to see if branch A is somewhat compatible with branch
dev
. It doesn't need to be "up-to-date", but need A to merge onto a certain point in
dev
where the code actually used to work.

Answer

Use a temporary branch.

Let's draw what you have now, as a series of commits on branch dev and your feature branch. Let's assume for the moment that you haven't merged your feature branch into dev (even though you have, i.e., let's start from just before you did git checkout dev && git merge feature):

...--o--*--A--B--C--D--E--F      <-- dev
         \
          G---H--I--J--K---L     <-- feature

Now, you already did git checkout dev && git merge feature or equivalent, so let's add the merge commit to the drawing:

...--o--*--A--B--C--D--E--F--M   <-- dev
         \                  /
          G---H--I--J--K---L     <-- feature

"Your" commits are the ones on branch feature that are not also on dev before the merge, i.e., G through L, plus of course your new merge M.

Now you say you have added several more commits as well, so let's draw them in:

...--o--*--A--B--C--D--E--F--M--N--O    <-- dev
         \                  /
          G---H--I--J--K---L            <-- feature

It's at this point that you realized that something is broken in, say, E and maybe F as well. So now you want to see what happens if you test "commit O as it would be if we didn't have commits E and/or F".

Method one: make temporary branch and revert bad commits

(This is usually the easiest method. You already did the merge; the other method makes you re-do the merge, and merges tend to be a bit harder than reverts.)

Step 1 is to get a temporary branch. While on dev, simply git checkout -b temp, so that you now have:

...--o--*--A--B--C--D--E--F--M--N--O    <-- dev, HEAD -> temp
         \                  /
          G---H--I--J--K---L            <-- feature

Now you can run git revert <id-of-E> <id-of-F> to revert the two "bad" commits. This does what git cherry-pick does, except in reverse: it finds out what changed from E to F (so that it can be repeated, or in this case, un-done) and "reverse applies" that change to un-do it. Then it finds out what changed from D to E, in the same way, and reverse-applies that change to undo it as well.

Now you have:

                                     f'-e'  <-- temp
                                    /
...--o--*--A--B--C--D--E--F--M--N--O        <-- dev
         \                  /
          G---H--I--J--K---L                <-- feature

where f' is the commit that "undoes" F, and e' is the commit that "undoes" E.

Now you can run your tests.

Method two: make temporary branch, then re-merge and cherry-pick

Again, we start with the idea that E and F are "bad". So this time, we want a temporary branch that points directly to commit D: the last "good" commit on dev. Find the ID of commit D and then:

git checkout -b temp <id-of-D>

Now you have this—which is the same drawing as before, but we've added a bump "up" for the new branch label:

                   D                  <-- HEAD -> temp
                  / \
...--o--*--A--B--C   E--F--M--N--O    <-- dev
         \                /
          G--H--I--J--K--L            <-- feature

Now run git merge feature. This makes a new merge commit, quite different from M, that merges commit L into commit D. Drawing the resulting graph is a bit difficult since the merge line crosses over the dev branch (I should have taken a different approach than lifting D up a line), but here's a sort of stab at it:

                   D-------m          <-- HEAD -> temp
                  / \     .
...--o--*--A--B--C   E--F--M--N--O    <-- dev
         \               ./
          G--H--I--J--K--L            <-- feature

Now use git cherry-pick to copy commits N and O. This is similar to our earlier revert, except that it doesn't try to "undo" the changes, but instead just to copy them. Let's call these copies N' and O' (uppercase since they're copies, not reverse-copies):

                   D-------m--N'--O'  <-- HEAD -> temp
                  / \     .
...--o--*--A--B--C   E--F--M--N--O    <-- dev
         \               ./
          G--H--I--J--K--L            <-- feature

Now run your tests as before.

What if the tests fail?

If things still don't work, it's up to you to decide how to deal with that. However, you can continue to make new commits on your temp branch until you have fixed things, then decide how to collect up those fixes and either rework feature, or add them after commit O, or whatever. This particular part is really easy, because you have a branch!

Comments