Resolving Git Merge conflicts (manually)
Merge conflict, you said?
A merge conflict is something that no one enjoys dealing with. A merge conflict can happen when two branches have conflicting changes. Assume you have a file F on branch feat-A, and the same file exists on branch feat-B, as shown in the picture below. If branch feat-A makes changes to line 4 of F and feat-B does the same, trying to merge the two branches will confuse Git. Amid that confusion, Git gently leaves the task to the unlucky person committing the merge to solve the conflict. Merge conflicts can also happen if feat-A edits F while feat-B deletes it.
A job to which no one applies
Fixing a merge conflict is the process of choosing what to keep and what to discard. You may opt to keep changes from both branches instead of letting go of one and keeping the other. Interestingly enough, you can dump both!
The way it works is that as soon as Git detects a conflict, it will stop the merge process and gently edit files to mark where the conflicts are. Below is an example of a file edited after Git found conflicting changes.
Let us get acquainted with the parties in the hostility from the picture above:
- Line 38, Git inserted
<<<<<<< HEAD
to inform you of where one conflict starts - Line 61 shows where that conflict ends, annotated by
>>>>>>> main
- The words HEAD and main are the branches associated with the content that follows
- The
=======
on line 59 delimits where changes from one branch end and where those of the other branch start. The conflict is that Git does not know whether to choose the changes from the top side of the=======
or the ones below.
Note that we are using VS Code to nicely or pretty format the result of the merge conflict annotations that Git made. The below picture shows how the above file looks without a fancy text editor meddling in this quarrel using a simple text editor such as TextEdit or Notepad:
Pretty ugly, right? It helps to have the content formatted, certainly. However, we won’t choose the easy and shiny way of doing things when it comes to fixing merge conflicts today. We will not click on buttons from a GUI to resolve the conflicts. Git believes we are smart enough to fix this, so let’s show off.
We look at both sides of the =======
line and decide what we want to keep. In the file from the picture above, someone edited what used to be in between =======
and >>>>>>> main
on the main branch. Fortunately, the right content is the one in between <<<<<<< HEAD
and =======
which is what the current branch has. From here, we have four options:
- If we decide that the content above
=======
is the suitable heir to this section of our file, we delete anything that’s below=======
. From the picture above(the fancy text editor), it is line 60 that we are deleting. That means we have decided to keep the content from line 39 down to 58. - Should we decide it is the other way around, we will delete content from 39 to 58 and keep line 60 only.
- We can also decide to keep changes from both sides of the
=======
. To do so, we delete the line containing=======
which is line 59(democracy!) - finally, we can opt to bring havoc by dropping everything(it is simple this way!). To do so, we delete everything between
<<<<<<< HEAD
and>>>>>>> main
.(dictatorship!)
Once we select and apply one of the above options, we also make sure to remove the annotations Git made; the lines <<<<<<< HEAD
, =======
, and >>>>>>> main
. Keeping them may render your file invalid.
Before closing this tutorial on peace negotiation, below is what the file would look like after choosing the first option as discussed earlier:
Peace treaty
Although many tools exist capable of resolving merge conflicts in a Git repository, nothing beats the comfort and confidence of doing this potentially sleep-depraving task in your local environment where you have total control.
If possible, it is recommended to resolve merge conflicts manually to keep the highest level of awareness on changes that merging two branches will cause. Should the merge result in something unexpected, going back to a previous state or version of your code is always within reach, and you may quickly reverse the merge.