On Git Conflict resolution

In my years as a consultant, I’ve seen the full range in the spectrum between love and hate for those who know Git but given the rate of adoption and its popularity in the development forums, I’d say that either way it has been a huge success.

One interesting aspect of Git is that even when a single developer is working on a project, by using branching or other Git features, conflicts may arise. It is very true that the inner workings of Git can become vastly complex for projects with long commit histories, tons of code, multiple entangled branches and lots and lots of contributing developers, exponentially increasing the risk of conflicts appearing as people collaborate; however, Git Conflicts are not necessarily bad, they are a seemingly natural part of the day-to-day dynamics of a software development team; on the other hand, dealing badly with those conflicts is what can make a project fail and utterly frustrate the team.

I’ll tell you in advance that I will not share here the specific Git commands to fix a conflict, that you can find elsewhere; instead I will treat the subject in a much broader perspective.

Common types of Git Conflicts

For non-trivial projects the tasks carried out by the team using this tool can become very complex and thus, subject to errors. This is the true nature of Git conflicts and here are some of the most common causes:

  • Committing broken (non-functional) code or with missing assets (i.e., CSS files, UI scripts, hard-coded resources, images, fonts, icons, etc.).
  • Pushing new changes over outdated code: failing to update (pull) latest code to the local branch before pushing local changes to the remote repository.
  • Git failing automatic code merging: when multiple people work on the same files, changes may conflict with each other. Note that Git auto-merging algorithms are optimized for code but they cannot deal with complex file formats (i.e. tree structures in XML, binary blobs).
  • Carelessly pushing a reset or rebased branch to the remote repository or misusing Git commands like cherry-pick, rebase, or merge.
  • Merging or rebasing branches that have diverged from the remote repository and with different changes on the same lines of code.
  • Conflicts when migrating from different version control systems like SVN or Mercurial to Git or due to differences in file encoding between Operating Systems.

It’s less risky if you have a process

The original author of Git Linus Torvalds has manifested his surprise on the extensive use and utterly complicated contexts where Git is now present. And even now that we have full graphical user interface tools like SourceTree or GitHub Desktop, instead of just the Command Line Interface, it remains a fact that Git command list is big and not that simple to use.

One way to reduce the complexity of the use of Git and reduce the risk of Conflicts for a Software Development team is to use a well-stablished process (that could be customized if needed) and that the team is disciplined on its implementation. This helps in two ways: conflicts should rarely appear and when they do, they should be easy to fix.

Examples of well-thought Git tool implementation processes that have been proven to work in both small and large teams and projects are Trunk-based Development (TBD) and GitFlow.

But still things can (and will) go wrong

In the presence of an extreme event (i.e. when the process was not followed by one or more team members), it is common that stress builds up from the moment of the discovery of the git conflict up to the first (and subsequent) attempts to fix it.

The following process is suggested to approach these kinds of events:

  1. Don’t panic, stop what you are doing the moment you perceive the anomaly.
  2. If you are not 100% sure of what the problem is and how to fix it don’t try to do it by yourself. Remember that it might not be you who caused it and you may not have everything you need in your local repository to deal with it.
  3. Promptly inform your peers and your leader about the incident, the faster everyone knows, the greater the chances of recovery.
  4. In coordination with the team leadership, ask for help from senior members or even Git specialists from other teams to clearly identify the cause and impact of the mishappen (e.g. what other devices have been affected and what parts of the code and assets might also been implicated).
  5. Involve the whole team when planning for the solution: it may very well be that one of the team members just happens to have a clean snapshot of the currently damaged code from just seconds before the incident. Additionally, as Git is distributed, many times the optimal solution lies not just in one computer or branch.
  6. Always prioritize the use of Git tools for the fix above any other proposed “remedies”. Backup restores, manually editing/replacing files or mangling the .git directory are discouraged. Restoring a backup as a fix should be the last resource as for me is like Rick Sanchez from the “Rick and Morty” show moving to the next dimension after a massive and irreparable fail: “you may very well be able to move on, but you didn’t really fixed what happened”.
  7. Make a backup before applying the fix: Creating a copy of the local repository where the fix will be applied can help you go full “Groundhog day” mode when trial and error is needed.
  8. Test the solution locally before pushing it to the remote server and If possible, test it on a different machine from where the anomaly was detected. This independent double check might help when a false sense of security is based on wrong (often only local) assumptions.
  9. Document the incident and make the necessary changes in your practice to avoid future incidents (process improvement).
  10. Socialize the improvement and continuously train your team.

As stated earlier, the former process works best if a mature process for version control (like TBD of Git Flow) is in place, and it is neither infallible nor comprehensive for every situation. It is rare but in occasions several trial-and-error sessions are the best course of action; other times (e.g., due to definitive hardware failure) a full recovery is not possible and the next best thing is to settle with the “almost latest” version of the code (being the best case only losing a partial day worth of work) but, by following the previously depicted process, you can increase your chances of a better outcome.