Git is probably the most popular version control system around. For many years, the git flow strategy has been the recommended way of working and system to manage and organise branches in the projects, only to be replaced by a more modern GitHub flow which is an iteration of the same ideas. In an ideal world, it provides a solid system to manage the development of new features and at the same time maintain existing ones by using short-lived branches. There are other alternatives like the trunk-based system that provide a different flavour but with similar ideas behind it. However, the reality is always something completely different.
The reality check
Some time ago a colleague left my team, so I inherited the project he was working on. He was working on two big features at the same time in that project that was the result of collaboration with two other teams. He had two branches, one per feature, that were branched out of the main branch months ago and have significant changes in each of them, many of those changes on the same files. The task that he left before leaving was to integrate all that code back to master. There were all types of conflicts: files moved and/or renamed, different versions of dependencies, different competing changes on the same lines… This is where the merge hell started.
The best solution is to avoid the problem
This type of merge conflict never has an easy solution, so the best solution is to avoid the problem. When working on a feature or fix, use short-lived branches as much as possible. Ideally, the age of the branch should not be longer than a couple of weeks, and it should be compromised of a few commits with not many changes. This way the merge will be likely resolved automatically without much effort.
If you are working on a big feature, try to split it into different parts. Once you start with a part, create a new branch for it and merge it into the main branch before starting with the next part. If this branch is used for continuous deployments, you can use feature flags or leave the function or module present in the code but without making use of it in “main logic” or other places of the codebase besides the…