Conflicts step by step
- Lesson 10
- 40 min
- Module 4: Resolve and recover
Lesson 10 of 15 · Git and Team Development EssentialsConflicts step by step
1. Understand a repository
2. Work with branches and remotes
3. Collaborate through reviews
4. Resolve and recover
5. Release and hotfix practice
What you will be able to do
Finish a merge when you and a teammate changed the same line. After this lesson, you can read both changes and keep what each person meant. You can test the result and complete the merge, or stop it and start again.
Before you startWhat to know and to have ready
What you need to know
git pullis a fetch, then a merge. Withpull.ff only, Git stops when the two histories are different. You learned this in Synchronize deliberately: Both people commit at the same time.- In this module, you commit, merge and revert on
main, so that the commands stay short. On a team, you make each change on a branch and merge it through a pull request, as in Pull-request sequence.
What you need on your computer
-
The practice repositories in
~/projects/nextia-learning-labs. You cloned them in Pull-request sequence: Set up the practice lab. If you do not have them, get them once:Terminal window cd ~/projectsgit clone https://github.com/Nextia-Intelligence/nextia-learning-labs.git -
Make the start state for this module.
--resetdeletes the oldc14-labfolder and makes it again. It changes nothing else. Run the same command at any time to start this module again.Terminal cd ~/projectssh nextia-learning-labs/C14/setup.sh m04 --resetOutput Ready: /Users/amira/projects/c14-labticket-notes your clonesam-notes Sam's cloneteam-remote.git the shared remoteNext: cd c14-lab/ticket-notesTo start this scenario again: sh nextia-learning-labs/C14/setup.sh m04 --reset -
Go into your clone with
cd c14-lab/ticket-notes.git statusshowsYour branch is ahead of 'origin/main' by 1 commit. -
The script sets
pull.ff onlyandmerge.conflictStyle zdiff3in the two clones. Your own commits use the name from Set up Git: Step 2: tell Git who you are. -
The script made its commits with fixed hashes, so they are the same as in this lesson. The commits that you make have different hashes. Use the ones from your own output.
When Git cannot merge by itself
Section titled “When Git cannot merge by itself”In Module 2, Amira merged Sam’s work, and Git did it alone. Git compares the two sides line by line. If each person changed different lines, Git takes both changes.
Git also uses a third version: the base, the last commit that both sides have. Module 3 called it the merge base. The base shows what each person changed. If both people changed the same line since the base, Git cannot know which version is correct. This is a conflict. Git stops the merge, writes both versions into the file, and waits for you to decide.
A conflict is not an error, and nothing is lost. It is a question that only a person can answer: what did each change mean, and what must the line say now?
In a merge, the two sides have names:
- Ours is your current branch, the place where HEAD points. In this lesson, that is Amira’s
main. - Theirs is the commit that you merge in. In this lesson, that is Sam’s commit from
origin/main.
Show the base in each conflict
Section titled “Show the base in each conflict”The setup script set merge.conflictStyle zdiff3 in the two clones. With this setting, Git also shows the base lines between the two sides. You can then see what each person changed, not only the result. Set it for all your repositories:
git config --global merge.conflictStyle zdiff3Worked example: two changes to ticket 102
Section titled “Worked example: two changes to ticket 102”The conflict
Section titled “The conflict”Amira added an action to ticket 102: Sent a new reset link. At the same time, Sam added the cause of the problem: The email was in the spam folder. Sam pushed first. Amira tries to push:
git pushTo /Users/amira/projects/c14-lab/team-remote.git ! [rejected] main -> main (non-fast-forward)error: failed to push some refs to '/Users/amira/projects/c14-lab/team-remote.git'hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. If you want to integrate the remote changes,hint: use 'git pull' before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details.She pulls. Because of pull.ff only, Git only fetches, then stops:
git pullFrom /Users/amira/projects/c14-lab/team-remote 05e00df..7e16da6 main -> origin/mainhint: Diverging branches can't be fast-forwarded, you need to either:...fatal: Not possible to fast-forward, aborting.The histories have diverged. She decides to merge, as in Module 2:
git pull --no-rebaseAuto-merging tickets.txtCONFLICT (content): Merge conflict in tickets.txtAutomatic merge failed; fix conflicts and then commit the result.The merge has started, but it is not complete. Git did not make a merge commit. git status shows what is still open:
git statusOn branch mainYour branch and 'origin/main' have diverged,and have 1 and 1 different commits each, respectively. (use "git pull" if you want to integrate the remote branch with yours)
You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge)
Unmerged paths: (use "git add <file>..." to mark resolution) both modified: tickets.txt
no changes added to commit (use "git add" and/or "git commit -a")both modified means that both sides changed tickets.txt in the same place. An unmerged path is a file with a conflict that you did not resolve yet. The output also gives the two ways forward: fix the conflict and commit, or abort the merge.
Read the conflict markers
Section titled “Read the conflict markers”Amira opens tickets.txt in her editor:
Ticket 101: printer offline. Restarted the spooler.<<<<<<< HEADTicket 102: password reset email not received. Sent a new reset link.||||||| 05e00dfTicket 102: password reset email not received.=======Ticket 102: password reset email not received. The email was in the spam folder.>>>>>>> 7e16da60a8af11e14488d6be51aeff28dadcfc1dTicket 103: shared drive is slow. Moved the user to the new server.Ticket 104: laptop will not charge. Replaced the power adapter.Git wrote four marker lines around the conflict. The lines outside the markers merged without a problem.
<<<<<<< HEADstarts ours: Amira’s version of the line.||||||| 05e00dfstarts the base: the line in05e00df, before either change.=======starts theirs: Sam’s version of the line.>>>>>>> 7e16da6…ends the conflict. The hash is Sam’s commit.
Compare each side with the base. Amira added Sent a new reset link. Sam added The email was in the spam folder. Without the base, it is harder to see that each person only added text.
Find out what each person meant
Section titled “Find out what each person meant”The text of a line shows what changed. The commit message tells why. git log --merge lists the commits on each side that changed the files with conflicts:
git log --merge --oneline9e1b9df (HEAD -> main) Add the action for ticket 1027e16da6 (origin/main, origin/HEAD) Add the cause of ticket 102To see Sam’s full change, she uses git show with his hash:
git show 7e16da6commit 7e16da60a8af11e14488d6be51aeff28dadcfc1dAuthor: Sam Okafor <sam@example.com>Date: Tue Oct 6 09:10:00 2026 -0400
Add the cause of ticket 102
diff --git a/tickets.txt b/tickets.txtindex 76ca965..ae59119 100644--- a/tickets.txt+++ b/tickets.txt@@ -1,4 +1,4 @@ Ticket 101: printer offline. Restarted the spooler.Ticket 102: password reset email not received.Ticket 102: password reset email not received. The email was in the spam folder. Ticket 103: shared drive is slow. Moved the user to the new server. Ticket 104: laptop will not charge. Replaced the power adapter.Sam recorded the cause. Amira recorded the action. Both facts are true and useful, so the correct line keeps both. If she is not sure what a change means, she asks the author before she decides.
Resolve, test and commit
Section titled “Resolve, test and commit”To resolve a conflict, you edit the file until it has the correct text and no markers. Amira replaces the seven lines from <<<<<<< to >>>>>>> with one line. The cause comes first, then the action:
Ticket 101: printer offline. Restarted the spooler.Ticket 102: password reset email not received. The email was in the spam folder. Sent a new reset link.Ticket 103: shared drive is slow. Moved the user to the new server.Ticket 104: laptop will not charge. Replaced the power adapter.She saves the file. Then she checks for markers that she forgot. git diff --check reports leftover conflict markers. If it finds none, it prints nothing:
git diff --checkNext, she runs the team’s test. check.sh is a small script in the repository. It checks that each line of tickets.txt starts with Ticket <number>: :
sh check.shPASS: 4 ticket lines checkedA file can merge with no markers and still be wrong. The test is how you find out before your team does.
Now she tells Git that the conflict is resolved. git add marks the file as resolved:
git add tickets.txtgit statusOn branch mainYour branch and 'origin/main' have diverged,and have 1 and 1 different commits each, respectively. (use "git pull" if you want to integrate the remote branch with yours)
All conflicts fixed but you are still merging. (use "git commit" to conclude merge)
Changes to be committed: modified: tickets.txtShe completes the merge with a commit. --no-edit keeps the message that Git prepared:
git commit --no-editgit log --oneline --graph -4[main 5c84cbf] Merge branch 'main' of /Users/amira/projects/c14-lab/team-remote* 5c84cbf (HEAD -> main) Merge branch 'main' of /Users/amira/projects/c14-lab/team-remote|\| * 7e16da6 (origin/main, origin/HEAD) Add the cause of ticket 102* | 9e1b9df Add the action for ticket 102|/* 05e00df Add ticket 104The merge commit has two parents: her commit and Sam’s commit. Your merge commit has a different hash. Now main contains Sam’s work, so the push is safe:
git pushTo /Users/amira/projects/c14-lab/team-remote.git 7e16da6..5c84cbf main -> mainStop a merge and start again
Section titled “Stop a merge and start again”Sometimes you cannot finish a conflict now. You may need to ask the other author, or you may have made a mess of the file. git merge --abort stops the merge. It puts your branch, the staging area and the files back to the state before the merge started.
Amira tried this before she resolved the conflict. She ran it while git status still showed You have unmerged paths:
git merge --abortgit statusgit log --oneline -1On branch mainYour branch and 'origin/main' have diverged,and have 1 and 1 different commits each, respectively. (use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree clean9e1b9df (HEAD -> main) Add the action for ticket 102main is back on her own commit, and the file has no markers. Sam’s commit is still in origin/main, because the fetch is not undone. When she is ready, she runs git pull --no-rebase again, and the same conflict appears.
Guided practice: a conflict with Sam
Section titled “Guided practice: a conflict with Sam”First, do the worked example in your own ticket-notes, from git push to the last git push. Then make a second conflict yourself. You play Sam in sam-notes and Amira in ticket-notes, and you both change the line for ticket 103.
-
Go to Sam’s clone, and get the merge that you pushed:
Terminal window cd ~/projects/c14-lab/sam-notesgit pullThe output includes
Fast-forward. -
In your editor, open
tickets.txtinsam-notes. Change the ticket 103 line to this text, and save:Ticket 103: shared drive is slow. Moved the user to the new server. The user confirmed the fix. -
Commit and push as Sam:
Terminal window git commit -am "Close ticket 103"git push -
Go to your clone. Change the same line in
ticket-notes/tickets.txtto this text, and save:Ticket 103: shared drive is slow. Moved the user to the new server. Ticket raised by Finance. -
Commit, then try to push:
Terminal window cd ~/projects/c14-lab/ticket-notesgit commit -am "Add who raised ticket 103"git pushThe push shows
! [rejected] main -> main (fetch first). -
Merge Sam’s work:
Terminal window git pull --no-rebaseThe output shows
CONFLICT (content): Merge conflict in tickets.txt. -
Run
git status.tickets.txtshows asboth modified. -
Run
git log --merge --oneline. The output shows your commit and Sam’s commitClose ticket 103. -
Open
tickets.txt. Find the base line between|||||||and=======. Compare each side with it. -
Replace the whole block, from
<<<<<<<to>>>>>>>, with one line that keeps both facts:Ticket 103: shared drive is slow. Moved the user to the new server. Ticket raised by Finance. The user confirmed the fix. -
Check and test:
Terminal window git diff --checksh check.shgit diff --checkprints nothing.check.shprintsPASS: 4 ticket lines checked. -
Mark the file as resolved, complete the merge and push:
Terminal window git add tickets.txtgit commit --no-editgit push -
Run
git log --oneline --graph -4. The top commit is a merge with two parents:Add who raised ticket 103andClose ticket 103.
Your turn: when only one side is right
Section titled “Your turn: when only one side is right”Not every conflict needs both sides. Make this conflict, then decide.
- In
sam-notes, rungit pull. Change the last line ofREADME.mdtoNotes about support tickets for the help desk. Use one line for each ticket.Commit with the messageExplain the ticket line format in the README, and push. - In
ticket-notes, change the same line toNotes about support tickets for the help desk. Write each ticket on one line.Commit with the messageDescribe the ticket format. - Run
git pull --no-rebase, and resolve the conflict inREADME.md. Finish the merge and push.
Before you edit, answer: what did each person want? Must the result keep both sides?
Show a model answer
The conflict looks like this. Your two hashes are different:
# Ticket notes
<<<<<<< HEADNotes about support tickets for the help desk. Write each ticket on one line.||||||| 928f2edNotes about support tickets for the help desk.=======Notes about support tickets for the help desk. Use one line for each ticket.>>>>>>> 555dcc3fcf504b021d15a76f90f351179bc04655git log --merge --oneline shows the two messages: Describe the ticket format and Explain the ticket line format in the README. Both people wanted the same thing, and wrote it in different words. If you keep both sides, the README says the same rule twice. So keep one side. Here, keep Sam’s line, because his version is already on the remote:
# Ticket notes
Notes about support tickets for the help desk. Use one line for each ticket.Then finish as before:
git diff --checksh check.shgit add README.mdgit commit --no-editgit pushgit diff HEAD^1 HEAD shows how the merge changed your side: your sentence went out, and Sam’s sentence came in. Your sentence is still in your commit Describe the ticket format, so the history keeps both.
Common mistake
Conflict markers in a commit
Symptom: after a merge, sh check.sh fails and lists the marker lines:
FAIL: these lines in tickets.txt do not start with 'Ticket <number>: '2:<<<<<<< HEAD4:||||||| 05e00df6:=======8:>>>>>>> 7e16da60a8af11e14488d6be51aeff28dadcfc1dgit diff --check HEAD^ HEAD shows tickets.txt:2: leftover conflict marker, and the same for lines 4, 6 and 8.
Cause: you ran git add and git commit before you removed the markers. git add does not check the contents of a file. It only tells Git that you finished.
Fix: do not change the merge commit. Fix the file in a new commit.
-
Open the file, and edit the block until it has the correct text and no markers.
-
Run
git diff --checkandsh check.sh. The test must printPASS. -
Commit the fix:
Terminal window git commit -am "Remove leftover conflict markers"Output [main 9a2eb8f] Remove leftover conflict markers1 file changed, 1 insertion(+), 7 deletions(-) -
Push. Next time, run
git diff --checkand the test beforegit add.
Common mistake
Committing is not possible because you have unmerged files
Symptom: you edited the file and removed the markers. git commit --no-edit shows:
error: Committing is not possible because you have unmerged files.hint: Fix them up in the work tree, and then use 'git add/rm <file>'hint: as appropriate to mark resolution and make a commit.fatal: Exiting because of an unresolved conflict.U tickets.txtCause: Git does not read the file to decide if you finished. It waits for git add. U means that the file is still unmerged.
Fix: run git add tickets.txt, then git commit --no-edit again. git status shows All conflicts fixed but you are still merging when the file is marked as resolved.
Check your understanding
Predict: Amira's commit changes line 2 of tickets.txt. Sam's commit adds a new line at the end of the file. Does git pull --no-rebase stop with a conflict?
Show the answer
No. The two changes are in different places, and line 2 and the last line are not next to each other. Git takes both changes and makes the merge commit. A conflict happens only when both sides changed the same lines, or lines next to each other, since the base. Even so, run the test after every merge. A clean merge can still give a wrong result.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Practical check: after git pull --no-rebase stops with a conflict, list the commands, in order, that complete the merge safely. Then name the command that cancels the merge instead.
Show the answer
git status, to find the files that showboth modified.git log --merge --onelineandgit show <hash>, to see what each person changed and why.- Edit each file, keep the correct text and remove all markers.
git diff --check(prints nothing) and the test,sh check.sh(printsPASS).git add <file>for each resolved file.git commit --no-edit, thengit push.
git merge --abort cancels the merge and returns to the state before it started.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Recap
- A conflict happens when both sides changed the same lines since the base. Git stops and lets you decide.
- In a merge, ours is your current branch (
HEAD) and theirs is the commit that you merge in. Withzdiff3, the base shows between them. - Read the commit messages with
git log --mergeandgit showto learn what each person meant. Then keep both changes, or only the correct one. - Before you commit, run
git diff --checkand the tests. Thengit addmarks the file as resolved, andgit commitcompletes the merge. git merge --abortstops a merge and returns to the state before it started.
Next: Undo safely