Eduard Egorov
Eduard Egorov

Reputation: 51

'git merge -s subtree' works incorrectly

A week ago, I've reset a state of 'ceph-ansible' folder in %current% branch with code from corresponding branch (that tracks an upstream from github):

# git read-tree --prefix=ceph-ansible/ -u ceph_ansible

Then I've committed several changes, including:

  1. Renamed file and commited:

    # git mv site.yml.sample site.yml
    
  2. Made some changes and committed

  3. Pulled updates from original branch by:

    # git merge -s subtree --squash ceph_ansible
    

It said:

Auto-merging ceph-ansible/site.yml.sample

blablabla

Squash commit -- not updating HEAD

Automatic merge went well; stopped before committing as requested

I can see that "my" ceph-ansible/site.yml is deleted (as well as few new files added by me), ceph-ansible/site.yml.sample is restored and doesn't contain my changes (it's just restored to the state before my changes).

git log ceph_ansible site.yml.sample shows 26th of August, 2016 (= latest changes ceph-ansible branch), my changes in %current% branch was this October, so it shouldn't be any conflict either.

I believe there is some obvious explanation for this behavior. Could you help me please?

UPDATE: I've noticed that my git is quite old (1.8.3) and built it from source tarball (2.10.1). Now the output is:

# ~/gitbuild/git-2.10.1/git merge -s subtree --squash ceph_ansible
fatal: refusing to merge unrelated histories

A quick googling showed (Git refusing to merge unrelated histories ) that the default behavior is changed. Adding ' --allow-unrelated-histories' clears the error message but the merge itself is still wrong (my changes are lost).

This error message might explain why git can't merge it correctly (since these repos doesn't has any relations, right). Can somebody confirm this please? Doesn't "merge -s subtree" really merges branches?

Upvotes: 3

Views: 310

Answers (1)

Eduard Egorov
Eduard Egorov

Reputation: 51

I've posted this question to Git's mailing list and few kind persons gave me an excellent explanation of this behavior:

When you merge from ceph_ansible, there is no shared history, and git uses the empty tree as a common ancestor.

I think possibly you're not fully understanding what the --squash flag does... that's what's causing your issue here, not the -s option.

A squash merge takes the commits that would be merged from the origin branch and squashes them into a single patch and applies them to the current branch as a new commit... but this new commit is not a merge commit (that is, when you look at it with "git show" etc. the commit will have only one parent, not two--or more--parents like a normal merge commit).

Basically, it's syntactic sugar for a diff plus patch operation plus some Git goodness wrapped around it to make it easier to use.

But ultimately once you're done, Git has no idea that this new commit has any relationship whatsoever to the origin branch. So the next time you merge, Git doesn't know that there was a previous merge and it will try to merge everything from scratch rather than starting at the previous common merge point.

So either you'll have to use a normal, non-squash merge, or, if you don't want to carry the ceph_ansible history in your project, you need to record the original upstream commit somewhere (probably in the commit message when you commit the read-tree result), and then ask git to use that as the merge-base during subsequent merges (which will require using plumbing codes, as git-merge wants to compute the merge base itself).

The git subtree command (from contrib) allows yet another way: it squashes history of merged subproject (as if with interactive rebase 'squash'), then merges this squash commit.

Upvotes: 2

Related Questions