4

A colleague, whom we'll call Aaron, was assigned to renovate a section of a website as a long-term project. He created a new Git branch, called aaron. All his changes were made on this branch. While he was working, I continued to maintain the site as a whole, committing my changes to master.

Eventually, Aaron merged his branch into master. This somehow reverted all of the commits I'd made to master between the time of the merge and the time when the aaron branch was first created. If I type git show <hash of merge commit>, I can see diffs for every file I changed while Aaron was working on his branch. Those diffs show a reversion of every change I made. It looks the way it would if Aaron had manually copied the contents of each file on his branch into master and committed the changes. (He didn't do this. I'm just trying to illustrate what the log is showing.)

According to Aaron, he didn't do anything weird. He says he just ran git pull origin/aaron.

What could have caused this? Is it possible that git pull origin aaron would have reverted all my changes to master?

Also, is there an easy way to reinstate my changes to master without reverting all of his work?

EDIT 1:

One of the files that was changed in master and then reverted after merge was foo.txt. So I did this:

git checkout aaron
git log foo.txt

The log does not reflect any changes to foo.txt after the moment the aaron branch was created. I was sort of expecting to see a reversion of my changes somewhere in the log for the aaron branch, but I didn't. So, is this final proof that Aaron did something other than the simple pull that he claims to have done?

EDIT 2:

I had said he typed origin/aaron, but he actually typed origin aaron. I've changed it above.

EDIT 3

As per suggestions below, I chose to solve this by rewriting history. I am at this point convinced that the problem was caused by a misguided attempt to resolve conflicts.

rlkw1024
  • 6,455
  • 1
  • 36
  • 65
  • If you use gitk to view the log are any files listed as changed in Aaron's merge commit? – asm Jan 18 '13 at 13:07
  • Yes, all the files that got reverted are listed as changed. The diffs show it very clearly: For every change I made to master, there's a section in the diff reverting the change. (I.e. you can see the individual lines being changed back.) – rlkw1024 Jan 18 '13 at 19:35
  • oops, missed that you had stated that. – asm Jan 18 '13 at 20:30

2 Answers2

5

The only way for git pull origin/aaron to do this is if he had a merge from master into aaron first, that discarded all of the changes on master (perhaps by using git merge -s ours master).

Go check the history. Are there any previous merges from master into aaron? And do they discard master's changes? If there are none, then the only explanation is he did not run git pull origin/aaron.

As for reinstating the changes, you could just back out his merge and re-create it yourself. That's going to modify history, but if you're ok with that, it's the easiest solution. If you're not ok with that, then it gets slightly more complicated. In that case, you'll want to create a temporary branch, back out his merge, and re-merge correctly. Then go back to master and run git read-tree tempbranch. This will update your index with the results of your temporary correct merge[1], and you can then commit this.

[1]: Note, this won't modify your working tree, so you'll want to follow this up with something like git checkout -- ..

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • Thanks! I just edited my question above in response to what you said. What are your thoughts? – rlkw1024 Jan 17 '13 at 22:33
  • @Jarrett: Yeah I think that means he did something more than just `git pull origin/aaron`. – Lily Ballard Jan 17 '13 at 22:36
  • In more recent versions of git, `git pull origin/aaron` is no longer valid. Still, that doesn't explain the scenario. Have you looked at Aaron's `git reflog`? – cmbuckley Jan 17 '13 at 23:21
  • [Looking at this question](http://stackoverflow.com/questions/2883840/differences-between-git-pull-origin-master-git-pull-origin-master), it used to be equivalent to just `git merge origin/aaron`. I started drafting a question to that effect, but still couldn't identify how even an outdated `origin/aaron` reference would cause this. – cmbuckley Jan 17 '13 at 23:55
  • @cbuckley: Huh, so I guess `git pull origin/foo` used to be equivalent to `git merge foo`, and is now disallowed because that was useless and confusing. Good to know. – Lily Ballard Jan 17 '13 at 23:57
  • 1
    @KevinBallard actually it would be `git merge origin/foo`, which might not be the same as `foo`. In fact, if someone else pushed to `foo` on `origin`, and I committed to my local copy of `foo` and didn't fetch in any way, then `foo`, `origin/foo` and `foo` on `origin` are now 3 different refs! – cmbuckley Jan 18 '13 at 00:02
  • @cbuckley: Err yeah, that's what I meant. – Lily Ballard Jan 18 '13 at 00:03
2

Given that all the changes occurred in the merge commit you're seeing what's known as an evil merge. man gitglossary defines it this way:

       evil merge
       An evil merge is a merge that introduces changes that do not appear in any
       parent.

There's an explanation of what this means in this answer. The important thing to know for you is that this isn't something that git will do by itself. Creating an evil merge requires human intervention in some way.

Where I've typically seen it happen is when someone does a merge and gets confused because of conflicts. For instance, one thing that could potentially have caused this is if aaron started his pull, got overwhelmed by conflicts, and decided that it was safe to solve the conflicts by copying his file(s) over top of the existing file(s). When he makes the merge commit after doing this you'll see exactly this.

As far as fixing it, I would do what Kevin Ballard suggested if you can. git -reset --hard the master branch back to how it looked before the merge and then redo the merge.

Community
  • 1
  • 1
asm
  • 8,758
  • 3
  • 27
  • 48