1

So I messed up a little bit, and pushed some of my changes into master before reviewing it. To fix it, I split off the merged version into its own branch, then checked out master again, reverted it, and pushed it back in.

I now want people to review my code, but in the PR it only includes the changes I've made since splitting off the two branches, but not the original changes I made. I realize this is a little confusing, so I made a picture:

Diagram of commits.

Basically, I'm looking to pull 'd' into 'c', but Git is only recognizing the differences from 'd', even though the changes made in 'b' should included in 'd' and are not a part of 'c'.

Do you know how I'd get git to recognize that there are other differences?

Edit: Just to clarify, "revert the revert" won't work because I want the differences to show up in a pull request, not just merge successfully.

Adam L.
  • 91
  • 5
  • 2
    Possible duplicate of [How do I "un-revert" a reverted Git commit?](https://stackoverflow.com/questions/8728093/how-do-i-un-revert-a-reverted-git-commit) – Jonathan Hall Jul 02 '18 at 15:55

2 Answers2

1

The problem occurs because as far as git is concerned, b is already in master (along with more recent changes that happen to undo b). This interferes not only with the review, but with the actual merge result.

There are a couple techniques commonly used to address this. Sometimes you "revert the revert"; sometimes you copy B to create a new commit B' that does the same thing as B, but isn't topologically "already in master."

In your case, you want the latter. (The only clean way to "revert the revert" would be on master, which would again bypass the review. You want the changes from B reflected on your branch instead.)

So you have

... a -- b -- d <--(your_branch)
          \
           !b <--(master)

(I've renamed c from your picture to !b to better describe what it is - a revert of b.)

You need some expression that resolves to a.

  • This could be the commit hash for a (or an abbreviated form of the commit hash); if you have that available, I'd just use that.

  • Or, in this example you could use your_branch~2 because your_branch has 2 commits after a (those being b and d).

  • If you're not sure how many commits are on your_branch, you might be able to use something like $(git merge-base your_branch master)^ (note the ^ at the end). This relies on the fact that b is the common ancestor of your branch and master, but will fail if there have been any later merges between the two branches.

Anyway, whatever expression you come up with, I'll use a as a placeholder in the following command. So you'd do a "forced rebase" of your_branch.

git rebase -f a your_branch

This should give you

       d
      /
a -- b -- !b <--(master)
 \
  b' -- d' <--(your_branch)

(I still show the original d commit in this diagram, but default git output will no longer show it because it's unreachable. After some time, it will be deleted by the garbage collector. b' is just like b except it isn't reachable by master; and d' is just like d except its parent is b'.

This is a history rewrite of your_branch, so given that you've previously pushed your_branch you would now have to force-push it (git push --force-with-lease). If there's a chance that anyone else has fetched your_branch - and especially if they might have already based work on it - then you need to communicate with them about what you're doing. (See the git rebase docs under "Recovering from upstream rebase".) (If this is likely to be a problem, you could avoid this issue by creating a new branch that's never been pushed, and rebasing that instead of directly rebasing your_branch.)

Once this is done, you should be able to do your pull request.

Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
  • Wow, thank you for the super well-explained answer. I've seen a lot of answers just saying "revert your revert" but as you pointed out that won't work with a PR. Just to clarify, for the 'a' in the rebase command, would that be the 'your_branch~2' you mentioned earlier? Or just a's hash? – Adam L. Jul 02 '18 at 17:08
  • a's hash (or abbreviated hash) is fine; if you know a good way to get the has value, it's arguably the simplest from a "nothing can go wrong" standpoint. Something like `your_branch~2` is also fine *in the example*, but in the real case you have to be sure of the correct ref name and correct number of commits to skip over. Or, as noted, something like `$(git merge-base ...)` might be fine. There are many options; basically any expression that resolves to commit `a`. – Mark Adelsberger Jul 02 '18 at 18:20
0

The simplest (and the most frowned upon) way to do this is to overwrite the master instead of reverting anything.

Remove your commits from your local master with git reset --hard HEAD~N where N is the number of last commits you want to remove. Then git push -f origin HEAD:master to overwrite the remote master branch. This will essentially revert the branch to the state before you "messed up".

This approach however is somewhat dangerous and frowned upon because if anyone already pulled the "messed up" version of master they might run into trouble merging with it later.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
  • "if anyone already pulled ... they might run into trouble merging" is an understatement. They *will* run into trouble, and the intuitive way for them to fix their trouble will, as a side effect, undo the "fix". This isn't just "frowned upon", it's a functionally bad idea unless you take steps to coordinate with every other user of the repo. And once you account for that, it's usually not the simplest solution anymore. – Mark Adelsberger Jul 02 '18 at 18:23