Git #4: Conflicts
There is, in my opinion (and this is a very personal opinion) 3 barriers to learning Git: understanding the notion of c, knowing how to use the ribs, and manage the Conflicts.
A conflict occurs when two versions (branches, commits, etc.) of a project with incompatible modifications are merged. Basically, these are changes on the same lines of the same file. Git is able to resolve the simplest conflicts on its own, such as modifying the same file but in two separate places. On the other hand, when a file has been modified in two different branches on the same lines, only the affected developers can resolve the resulting conflict.
In the event of a conflict, the merger of two branches (such as a merge
) will result in failure and a message from Git prompting us to resolve the conflicts before proceeding with the merge.
Creating the Git Conflict
For demonstration purposes, let's artificially create a conflict. Two branches will be used: conflict-branch
et master
. The file conflict-file.txt
is created in each branch, with the line line from conflict branch
on the branch conflict-branch
et line from master
on master
.
Here's a quick tour of the repository after that.
# Let's see the difference in commits first $ git logmaster e58a6b9 (HEAD -> master) create conflict file on master Thu Feb 4 11:57:26 2021 +0100 Jules Chevalier 480b8fd (origin/master, origin/HEAD) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier $ git log conflict-branch e48246c (conflict-branch) create conflict file on conflict branch Thu Feb 4 11:51:34 2021 +0100 Jules Chevalier 480b8fd (origin/new-feature, origin/master, origin/HEAD) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier # Now in more detail the commits that differ between the two branches $git show e58a6b9 e58a6b9 (HEAD -> master) create conflict file on master diff --git a/conflict-file.txt b/conflict-file.txt @@ -0,0 +1 @@ +line from master $git show e48246c e48246c (conflict-branch) create conflict file on conflict branch diff --git a/conflict-file.txt b/conflict-file.txt @@ -0,0 +1 @@ +line from new branch
Now that everything is in place, let's unleash the heavens: it's time to merge the branches. As before, we return to master
to repatriate the modifications of conflict-branch
, thus creating a conflict since the same line of the same file was modified in both branches.
$ git checkout master Switched to branch 'master' $ git merge conflict-branch CONFLICT (add/add): Merge conflict in conflict-file.txt Auto-merge conflict-file.txt Automatic merge failed; fix conflicts and then commit the result.
Conflict resolution
It is now a question of resolving the conflict, in other words of deciding between the opposing versions. To facilitate this work, git modifies the problematic file by adding the two contradictory possibilities, to the developer to make the choice. To represent these possibilities, Git uses marqueurs de conflits
.
$cat conflict-file.txt <<<<<<< HEAD line from master ======= line from new branch >>>>>>> conflict-branch
Here, Git indicates that two versions are in conflict: the first, HEAD
(which represents the current position of the repository, here master
), and the second from conflict-branch
. To resolve the conflict, you must choose the version you want to keep and remove the conflict markers, which will give this.
$cat conflict-file.txt line from new branch
Then, git status
tells us how to proceed.
$cat conflict-file.txt line from master $ gitstatus On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Unmerged paths: (use "git add ..." to mark resolution) both added: conflict-file.txt
Git gives us two new hints: fix conflicts and run git commit
et use git add
to mark resolution. This is exactly the procedure we are going to follow: use git add
to mark the resolution of our conflict on the file conflict-file.txt then complete conflict resolution with git commit
.
Note: Git offers at the time of
git commit
to modify the merge commit, which is not necessary.
$ git add conflict-file.txt $ gitcommit [master 8eada8a] Merge branch 'conflict-branch' $ gitstatus On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean $ gitlog 8eada8a (HEAD -> master) Merge branch 'conflict-branch' Thu Feb 4 14:46:48 2021 +0100 Jules Chevalier e58a6b9 create conflict file on master Thu Feb 4 11:57:26 2021 +0100 Jules Chevalier e48246c (conflict-branch) create conflict file Thu Feb 4 11:51:34 2021 +0100 Jules Chevalier 480b8fd (origin/new-feature, origin/master, origin/HEAD) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier
Branch master
is now up to date! In all, we end up with 3 new commits: the commit already present on master
, the commit retrieved from the branch conflict-branch
, and finally the famous merge commit. This is a special commit, which carries the changes related to resolving the conflict. Its commit message makes it possible to locate the merger of the branches in the history, hence the interest of not modifying it.
As always, all that remains is to send the new version of master
on the server using git push
and the merger will be fully effective.
Git merge tools
Well, let's be honest, manually changing a one-line conflict by deleting 3 lines and 2 tags is fine. But when we have to deal with a real conflict, spread over several files, each containing several conflicting areas… We need a graphical tool more suitable for resolving such a conflict.
There are a number of them, with a similar operation: display the different versions in conflict side by side to help the user "visually" resolve the conflict, also displaying the "final" version which will be kept after the resolution of the conflict . Some tools also offer the "common ancestor" version, which represents the last commit in common with the two conflicting branches (in our example, this is the commit 480b8fd Merge branch 'python-hello-world'
). These three versions provide a view of the entire conflict, with both the two versions offered, but also the "original" version to better understand the changes. Finally, these tools make it possible to graphically validate the version to keep and to move quickly from one conflict to the next.
Git, conclusion
Conflict resolution can (no pun intended) be problematic. Beyond the difficulty of choosing the right version to keep, a bad resolution can break the existing code, for example by leaving conflict markers lying around which make the code useless...
By using graphical tools to resolve conflicts effectively and visually, we avoid the worst by ensuring that we do not lose code. It remains to have a clear idea of the intention of the authors of the different versions of the project in order to keep the correct version of the code…