Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right way to merge branches when `git diff` shows changes but `git merge` does nothing

I get stumped when git diff other_branch shows differences and git merge other_branch does nothing. What's the right way to incorporate that "missing" code into my_branch?

This SO answer diagramed a situation similar to mine.

o---A---B---C-------G   my_branch
     \       \     /
      --*D*---E---F    other_branch

Surprisingly changes in D aren't automatically merged into my_branch when I do

git checkout my_branch
git merge other_branch

to bring in F to G

Of course I can manually create a patch and apply it to my_branch, like this:

git diff other_branch my_branch > patchfile
git checkout my_branch
patch -p1 < patchfile 

But I want to do this right, without mucking up the git log. With the patch approach, git will record the changes in D and my latest patch as happening independently of each other (code churn). I want to simplify the git log and make sure others don't have unintended things happen (quietly and insidiously) when they do a git merge my_branch.

like image 506
hobs Avatar asked May 10 '13 20:05

hobs


1 Answers

You seem to have two issues here at the same time.

Assuming a history like the one you described:

                my_branch
                    v
----A---B---C-------G   
     \       \     /
      --*D*---E---F
                  ^
             other_branch

Why the diff is not empty:

This situation is produced by the commands you mentioned, i.e. being on my_branch and then doing git merge other_branch. This action produced a merge commit G and moved the current branch (my_branch) forward, but left other_branch where it was. This is how merges always work! Naturally, there will still be a difference between my_branch (G) and other_branch (F) at this point -- they point to different commits after all.

If you want the two branches to be identical, you can move up other_branch in the following ways:

  1. git checkout other_branch; git merge my_branch -- this is a fast-forward merge since G is a descendant of F
  2. [while not on other_branch!] git branch -D other_branch; git branch other_branch my_branch -- this will remove other_branch and then re-create it in the same location where my_branch currently is
  3. git checkout other_branch; git reset --hard my_branch -- this will move other_branch to point exactly where my_branch is currently pointing

After doing one of these steps, both my_branch and other_branch will be pointing to G.


Why the changes of commit D are not in my_branch (G)

Git merges histories, not changes. This usually means that Git will take the two sides of a merge (and, if available, their youngest common ancestor commit, aka the merge-base) and try to combine them. Since Git commits are snapshots, not changesets/deltas/diffs, this no information of other commits or changes is used in the actual merge.

Using the merge-base, Git does a so-called three-way merge, using the common ancestor to try and smartly resolve the changes on both sides. If it can't it will interrupt the merge and require the user to resolve the conflicts.

What probably happened in your history is that either commit E or F reverted (or overrode) the changes of D -- this might have happened during the merge which produced E or actual (manual) changes in F.

As you yourself mentioned in a comment, you often switch between commits and make edits. You will thereby be floating changes a lot (Git carries uncommitted changes over during checkouts), which might very well make it difficult to tell where changes should go and where they actually do go. Try to avoid this by either committing any changes before changing branches or using git stash to buffer them.

like image 186
Nevik Rehnel Avatar answered Oct 06 '22 17:10

Nevik Rehnel