Search code examples
gitgit-mergegit-commitgit-merge-conflict

How to merge a git branch that results in only a single commit (like using --squash), but allowing future merges without conflicts?


I have a branch with many commits in it since creating it and would like to make a single merge commit to move it back to master, usin a single commit. So the only solution I know if is to use git merge --squash branchname. This works well, but if someone adds more commits to branchname and I again merge it into master, I get conflicts from the initial new commits on branchname. How do I prevent merge conflicts, while still only keeping a single commit in master for each merge? I have looked into using git merge --no-ff but that still moves all the commits from branchname into master.


Solution

  • echo $(git rev-parse $result $result^ $merged) > .git/info/grafts
    git merge topic
    

    where $result is the commit produced by the squash merge and $merged is the commit you merged from (i.e. given git checkout master; git merge topic, $result is master after the merge, and $merged is topic after the merge).

    The info/grafts file contains temporary, repo-local ancestry overrides. The echo above records, only in this repo, that the result of the squash merge also has the squash-merged commit as its parent -- i.e. it records an accurate merge history. Unrecorded merges only work so long as subsequent merges of the two branches still see the merged changes the same way.

    Don't forget that rebase and filter-branch will also see the grafted ancestry, and if they rewrite the $result commit they'll record that ancestry in the new commit.


    It's certainly common to merge feature branches, it's just not common to do it without recording it, because that makes it impossible for git (or any vcs) to always find the correct merge base. Many unrecorded-merges leave no correct merge base at all in the history, you have to make one to get a good merge later. Cherry-picks and squash merges are great so long as the technical debt is paid by a recorded merge before further modifications obscure the matching change hunks.

    If the history you're merging is ugly, you're going to love interactive rebase. It lets you create the commit history you would have made if you'd had the foresight to do it that way in the first place. Cleaning up changes for publication is a fundamental part of the workflow in many, many projects.