Why after merge does GIT say "Already up-to-date", but differences between branches still exist?

26,783

Solution 1

Reverting Merges vs. Resetting Merges

My guess is that you actually are Already-up-to-date.

The problem is that git revert doesn't undo the merge, it only undoes the changes that the merge brought with it. When you create a merge commit, you're combining the commit histories of those two branches.

Merging

     develop
        |
A---B---C
 \       \
  E---F---M
          |
      newfeature

In the case above, develop is merged into newfeature, creating the M commit. If you were to run git log newfeature you would see all the commits from both branches, however from the perspective of the newfeature branch, all those changes were performed by the M commit.

Reverting

The git revert command does not remove any commits, instead it creates a new commit that undoes the changes that the commit contained. For example if you had a commit containing this diff...

-This is the old sentence.
+This is the new sentence.

Then reverted this, the revert command would create a new commit that just preformed the opposite diff, it simply flips the signs.

-This is the new sentence.
+This is the old sentence.

This is really useful for undoing damage caused by commits that other developers already have. It moves history forward rather than changing history.

Reverting Merges

However, in the context of a non-fastforward merge it may have an undesired effect.

     develop
        |
A---B---C
 \       \
  E---F---M---W
              |
         newfeature

Assuming W is a reversion commit, you can see how running git log newfeature will still include all the commits from the develop branch. As a result, additional merges from develop will no work, because it doesn't see anything missing from your branch.

Using git reset instead of revert.

In the future, you might want to consider using git reset --hard <ref> (where <ref> is the commit hash of the merge) to undo a merge if that merge has not been shared with other developers. In the example above, after having created merge commit M, running the command git reset --hard F would result in the following.

     develop
        |
A---B---C
 \       \
  E---F---M
      |
  newfeature

As you can see this technique doesn't obliterate the commit as some people tend to think, it simply moves your branch back to the commit you selected. Now if you ran git log newfeature you would only get commit F, E, and A. Now the merge is actually gone from your branches history, so a later attempts to re-merge in develop will cause no problems.

This method is not without its complications. Realize that you are now modifying history, so if the newfeature branch was pushed to a remote branch after the M merge was made, then git is going to think you are simply out of date and tell you that you need to run git pull. If its just you working on that remote branch, then feel free to force-push - git push -f <remote> <branch>. This will have the same effect of the reset but on the remote branch.

If this branch is being used by multiple developers, who would have by now already pulled from it - then this is a bad idea. This is the very reason git revert is useful, because it undoes changes without changing the actual history.

Using reset on history is really only on option for commits that have not been shared.

The solution - reverting the reversion.

If the merge commit has already been shared, then the best approach is probably to use git revert on that merge. However as we said before, you can not then simply merge the branch back in and expect all the changes from that branch to re-appear. The answer is to revert the revert commit.

Lets say you did some work on the develop branch after having revered the merge in newfeature. Your history would look something like this.

         develop
            |
A---B---C---D
 \       \
  E---F---M---W
              |
         newfeature

If you merge develop into newfeature now, you would only get D because its the only commit that is not already part of the history of the newfeature branch. What you also need to do is revert that W commit - git revert W should do the trick followed by git merge develop.

                 develop
                    |
A---B---C-----------D
 \       \           \
  E---F---M---W---M---G
                      |
                 newfeature

This restores all the changes made by the original merge commit - which were actually made by C and B but were reverted in W, it then brings in D via a new merge commit G I would recommend reverting the revert before merging in the recent changes to develop, I suspect doing it in that order will have a lower chance of triggering conflicts.

TL;DR

Reverting creates a 'revert commit'. When undoing a revert, you need to run the revert command on the revert commit that was created when you reverted the first time. It should be easy enough to find, git tends to auto-comment on reverts so that they start with the word "Reverted".

git revert <commit>

Solution 2

Found a hacky solution. But it works.

Whatever eddiemoya has answered is totally helpful. Thanks a lot for explanation. I encountered the similar situation. Where, I was able to see a lot of content in git diff <branch> but git merge was saying already up to date.

And I was not able to find the exact revert commit due to lot of reverts in the log. (Yeah bad thing. Shouldn't have happened in the first place)

Solution

git checkout branchX -- .

This will stage all the changes from branchX to my current branch. Use on of your favorite git client, unstage and revert whatever is not intended.

Make a new commit and be happy :)

Solution 3

I've met the same situation too. What I have done is I just created a new branch and cherry picked all desired files from different commits and then merged this branch.

The actions looks like this:

  • created a new branch
  • used git cherry-pick -n xxxxxxx to get desired files from different commits
  • then commit these files git commit -m 'Your commit message'
  • then merge this cherry picked branch into desired branch
Share:
26,783
Peter White
Author by

Peter White

My computers are all Linux and I could never go back to Windows. Linux is the best tool in my box. Since converting to it, I think my productivity has increased four-fold!

Updated on October 16, 2020

Comments

  • Peter White
    Peter White over 3 years

    I was originally working in 'newfeature' branch and I was called to fix a bug on the live branch urgently. I created a branch for that called 'generalmaintenance', did the job and then switched to develop and merged it in. I now want to return to 'newfeature' branch and to merge in the changes that I merged into it earlier.

    When I switched to 'newfeature' and merged in 'develop', there were conflicts in 3 files.

    I got in a tangle resolving the conflicts and eventually decided to use the "Revert" command in the 'Team' menu of Aptana Studio 3 (which is my IDE). I expected this to roll me back to before the merge, which it appears to have done.

    Anyway, when I merge in 'develop' again, it says, Already-up-to-date, but when comparing files between the two branches, they are very different and the changes I added in the other branch are not being merged in.

    How will I merge the two branches now please?

  • Peter White
    Peter White about 11 years
    Many thanks Eddie, for the detailed explanation (+1) for that. Will study it again to digest it and give your solution a try before accepting your answer. I'm 'quietly confident' that this will be the solution.
  • eddiemoya
    eddiemoya about 11 years
    FYI: I made some minor but important edits to correct a few things. Most importantly I corrected my example on reverting the merge commit. It was git revert M, I corrected it to git revert W. Also, the second diff was wrong. The one explaining a revert commit. Fixed the +/- symbols.
  • f01
    f01 about 8 years
    Learned our lesson - careful git diff and review of commits if merging from the same branch where the reverted PR was generated!
  • Astitva Srivastava
    Astitva Srivastava over 5 years
    I am facing an issue. The branch that I merged doesn't have any new commit. And reverting the revert commit doesn't let me merge the develop branch to newfeature branch and says "Already up to date". Please help.
  • eddiemoya
    eddiemoya over 5 years
    @AstitvaSrivastava The branch you merged doesn't have new commits, but you still need to merge it? I'm confused by those statements. If there aren't new commits then theres nothing to merge. However, the way you said something gives me a possible clue to your problem. "merge develop into newfeature". You might be merging them the wrong way. If newfeature was based on develop, and develop has not changed, it has nothing new to newfeature. Try merging newfeature into develop instead - if thats what you need. I can't be sure.
  • Astitva Srivastava
    Astitva Srivastava over 5 years
    @eddiemoya Actually, what happened is as follows. Initally, "develop" had changes, I merged it to "newfeature", but while doing that I got conflicts, in which I resolved them wrongly. Then I reverted to fix the wrongly fixed conflicts. And then when I tried to merge again, it said, nothing to merge. Then I read your answer and tried to revert the revert. But still I was not successful in merging. Later I hard resetted it and removed remote branch as force push is disabled.
  • paulvs
    paulvs almost 4 years
    This answer makes sense for me, until it recommends reverting the revert commit. How does this allow the feature branch to merge in from the develop branch? In my testing it didn't work as explained here.
  • eddiemoya
    eddiemoya almost 4 years
    @paulvs It's hard to know what you are specifically dealing with without more details. Broadly speaking, reverts of merge commits hide the commits that were introduced by that merge commit. Think of it like a light switch. Reverting a merge commit "turns off" the merge. To bring it back, you have to "undo" it, by turning the merge back on. Reverting, in itself creates a commit, and by reverting it you undo the revert - which will "turn on" the merge again. I hope that helps.