Git: Avoiding merge conflicts

Working with git makes using branches very normal and straightforward. As a matter of facts, every commit is on a different branch, because it happens on a different repository clone, to be “merged” with the remote through a push.
As well, this means that merge is a very common operation, and usually very straightforward, because git is actually very good at understanding what the the changes were and how to automatically merge them when possible.
However, sometimes that’s not possible, and then you need to merge manually. Again, this is usually a straightforward operation, but conflicts can always happen, and then the merge can be difficult.

However, sometimes a merge conflict can be just a product of a wrong order in the commits, and can be avoided altogether with a bit of history rewrite.

This is one of those cases, as it happened:

  1. A fairly significant commit on main (in this case, a branch was merged)
  2. Then create a new feature branch
  3. Because of some issue, the commit on main from point 1 was reverted
  4. New development happened on the feature branch
  5. Finally, the feature branch was merged

This gave origin to quite a lot of merge conflicts. Solving them required as well quite a lot of changes in code, in ways that were not easy to understand.

Solution was then simple, but required rewriting the branch history:

  • On the branch, check the point where the branch started diverging. This meant quite a lot of git reset HEAD~1 and git merge main and git merge --abort.
    I am pretty sure there is a better way to get there, but as there were not that many commits on the branch, this approach took me just a couple of minutes.
  • Once that point was found, create a new branch. Generally speaking, I always try to make possibly destructive changes like it happens when rewriting history on a new branch; in the worst case, I can always delete it and start the work again.
  • Now, we begin to fix. In this case, this means immediately merge the branch with main.
  • Restore the correct status on that branch; in this case, revert the revert commit. Ideally, we should have done this step in yet another branch, but hey, I was here just to fix this issue.
  • Cherry-pick all commits from the original branch onto this one. Again, I am pretty sure that there is a better and more efficient way to do this, but I am not aware of it, and for the limited use I am doing, cherry-pick is good enough. The only problem is that the commit will look authored from two different people in the history, but it was not a deal breaker for me.
  • Set the remote tracking branch for this branch to the original branch, and then force pull (as usual, when rewriting history).

Advantage: now the commits are clear, and the only “new” commit is now clearly an automated operation (revert a commit) so it’s not something forcing a new review and a new set of tests.

Overall, it’s another case when a bit of playing with git history gives out very nice results. Git may not be the best possible UI (I liked Mercurial much more) and it might require you to know a bit too much about its internals; still, when you can make it to work and solve problems, is a nice pleasure :-)