Diverging Branches
Last updated on 2026-03-02 | Edit this page
Overview
Questions
- What are diverged branches?
- How do I keep my development branch up-to-date with
main? - What is merging?
- What is rebasing?
Objectives
- How to keep a development branch up-to-date using
git mergegit rebase
- Differences between and when to use merge and rebase.
Diverging Branches
As you and your collaborator(s) work on your repository you may find
that changes others have made get merged into the main
before you have finished your work. This has in fact just happened, the
work to add a Zero Division exception has been merged via a Pull
Request, but the work to address the Square Root function hasn’t and is
in effect behind the main branch. The following is a
representation of the current state, albeit from a single developer.
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Simple Git Branches
accDescr {Diverging branches in python-maths ns-rse/2-square-root is now behind the main branch which has incorporated the changes from ns-rse/1-zero-division.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
branch "ns-rse/2-square-root"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "Merge zero division"
commit id: "HEAD"
In this example the main branch now includes the commits
3-8c52dce and 5-2315fa0 from the
ns-rse/1-zero-division branch as well as the commit
7-bc43901 which was made when the
ns-rse/1-zero-division branch was merged in. The
ns-rse/2-square-root branch does not contain these
commits.
Sometimes this won’t be a problem, the two features/issues are
completely independent and have not made changes to the same file. In
such cases it is possible to merge the branches into main
without any “merge conflicts” because neither have modified the same
files in the same location.
That will not always be the case though, sometimes merge conflicts
might arise if the second branch is changing the same files in the same
place as the first branch. Another scenario might be that whilst work
was being done on adding a new feature branch, a critical bug was fixed
that the new feature depends on and the changes now in main
need incorporating in the feature branch. That is in fact the situation
we have with our ns-rse/4-square-root branch, it conflicts
with main because of the changes we merged from the
ns-rse/3-zero-division branch.
There are two approaches to solving this: merging
(git merge) and rebasing (git rebase).
Merging
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Diverging Branches
accDescr {Before - diverged branches in python-maths. The branch ns-rse/2-square-root is now behind the main
branch which has incorporated the changes from the ns-rse/1-zero-division branch.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
branch "ns-rse/2-square-root"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit id: "HEAD"
checkout "main"
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Merging Diverged Branches
accDescr {After - main, which has already had the work in ns-rse/1-zero-division merged into it, is
merged into ns-rse/2-square-root. Development is completed on ns-rse/2-square-root and the feature merged
into main.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
branch "ns-rse/2-square-root"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit id: "HEAD"
checkout "ns-rse/2-square-root"
merge "main" id: "8-a80cef8" tag: "Merge main"
commit id: "9-cb839a0"
commit id: "10-1ae54c3"
checkout "main"
merge "ns-rse/2-square-root" id: "11-a8c9932" tag: "v0.1.2"
The syntax of git merge is
Where <ref> is one of a commit hash, branch name
or tag (both of which are references to commit hashes). There is an
option for how the merge is made known as fast-forward.
Fast-forward is the default and it is used when a branch is merged with
its ancestor which itself has no new commits.
Often though, as in this example, the main branch
contains work from someone else’s branch and your feature branch and
main have therefore diverged, and we want to incorporate
the changes that have been introduced to main into the
feature branch that is being worked on.
3. Switch back to main
Check the contents of README.md (there is no such file
as the it exists on branch1).
5. Merge branch1 into main
Switch back to main and merge branch1 (this
is equivalent to merging a Pull Request). The file
README.md now exists on the main branch.
6. Merge main, which now contains
README.md, into branch2
Switch to branch2 which has now diverged as it contains
changes of its own and main contains the changes
made on branch1. We want to merge the changes on
main and “fast-forward” if possible.
BASH
git switch branch2
git merge --ff main # Merge changes merged into main from branch1 into branch2
git lol
* d914fee - (HEAD -> branch2) Merge branch 'main' into branch2 (2024-03-01 12:02:08 +0000) <Neil
|\
| * 7817070 - (main, branch1) Adding a README.md (2024-03-01 11:57:35 +0000) <Neil Shephard>
* | a14a643 - Adding a LICENSE (2024-03-01 12:00:39 +0000) <Neil Shephard>
|/
* 1bd6bb8 - Initial commit (2024-03-01 11:57:06 +0000) <Neil Shephard>
It might seem strange that commit 7817070 is listed
after a14a643 when the README.md was
created and committed first but this is just an artefact of how the
graph is drawn as the timestamps show.
7. Merge branch2 into main
We now have the changes from branch1 included in
branch2 by virtue of having merged main. If we
switch back to main we can merge the changes from
branch2.
BASH
git switch main
git merge branch2
git lol
* d914fee - (HEAD -> main, branch2) Merge branch 'main' into branch2 (2024-03-01 12:02:08 +0000) <Neil Shephard>
|\
| * 7817070 - (branch1) Adding a README.md (2024-03-01 11:57:35 +0000) <Neil Shephard>
* | a14a643 - Adding a LICENSE (2024-03-01 12:00:39 +0000) <Neil Shephard>
|/
* 1bd6bb8 - Initial commit (2024-03-01 11:57:06 +0000) <Neil Shephard>
ls -lh
.rw-r--r-- neil neil 40 B Fri Mar 01 11:57:21 LICENSE
.rw-r--r-- neil neil 14 B Fri Mar 01 11:58:03 README.md
8. Delete branch1 and branch2
As we’re done with branch1 and branch2 we
can delete them.
BASH
# Delete the two branches
git branch -d branch{1,2}
git lol
* d914fee - (HEAD -> main) Merge branch 'main' into branch2 (2024-03-01 12:02:08 +0000) <Neil Shephard>
|\
| * 7817070 - Adding a README.md (2024-03-01 11:57:35 +0000) <Neil Shephard>
* | a14a643 - Adding a LICENSE (2024-03-01 12:00:39 +0000) <Neil Shephard>
|/
* 1bd6bb8 - Initial commit (2024-03-01 11:57:06 +0000) <Neil Shephard>
Having used git merge we couldn’t perform a simple
fast-forward because the history of main now contained
changes that were made on branch1 and so a separate commit
(d914fee) was made to merge the main branch
into branch2 (commits are denoted by * and so
you can see the commits were made on separate branches). We can see from
the graph that README.md was added from a separate
branches, although after deleting the branches they are no longer shown
by name in the output.
Rebasing
Rebasing moves the point at which the branch diverged from its
original position to another, in this case the HEAD of the
main branch. You are changing the base commit,
hence the name git rebase.
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Diverged Branches
accDescr {Before - diverged branches in python-maths. The branch ns-rse/2-square-root is now behind the main
branch which has incorporated the changes from the ns-rse/1-zero-division branch.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
branch "ns-rse/2-square-root"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit id: "HEAD"
checkout "main"
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Rebasing
accDescr {After - rebase to bring the diverged branch up-to-date with main which includes
ns-rse/1-zero-division. Two more commits are made and ns-rse/2-square-root is then merged into main.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit tag: "Rebase"
branch "ns-rse/2-square-root"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
commit id: "7-cb839a0"
commit id: "8-1ae54c3"
checkout "main"
merge "ns-rse/2-square-root" id: "9-ba93020" tag: "v0.1.2"
git rebase takes a different approach to bringing
branches up-to-date and in effect moves the point at which a branch
diverged from main rather than merging the changes in.
5. Merge branch1 into main (equivalent to
making a Pull Request)
Switch back to main and merge branch1 (this
is equivalent to merging a Pull Request). The file
README.md now exists on the main branch.
6. Rebase branch2 onto main so it includes
the README.md and the point of divergence is updated
Switch to branch2 which has now diverged as it contains
changes of its own and main contains the changes
made on branch1. We want to rebase branch2
onto main so that it appears as if branch2
forked after the changes from branch1 were
merged.
BASH
git switch branch2
git rebase main # Rebase branch2 onto main
git lol
* 12f5202 - (HEAD -> branch2) Adding a LICENSE (2024-03-01 12:19:12 +0000) <Neil Shephard>
* 4e8e933 - (main, branch1) Adding README.md (2024-03-01 12:18:37 +0000) <Neil Shephard>
* 2459609 - Initial commit (2024-03-01 12:18:37 +0000) <Neil Shephard>
NB The commits here are listed in chronological order because there is no branching.
7. Merge branch2 into main
We now have the changes from branch1 included in
branch2 by virtue of having rebased onto main
after the changes in branch1 were merged in. If we
switch back to main we can merge the changes from
branch2.
BASH
git switch main
git merge branch2
git lol
* 12f5202 - (HEAD -> main, branch2) docs: Adding a LICENSE (2024-03-01 12:19:12 +0000) <Neil Shephard>
* 4e8e933 - (branch1) docs: Adding README.md (2024-03-01 12:18:37 +0000) <Neil Shephard>
* 2459609 - Initial commit (2024-03-01 12:18:37 +0000) <Neil Shephard>
8. Delete branch1 and branch2
As we’re done with branch1 and branch2 we
can delete them.
BASH
git branch -d branch{1,2}
git lol
* 12f5202 - (HEAD -> main) Adding a LICENSE (2024-03-01 12:19:12 +0000) <Neil Shephard>
* 4e8e933 - Adding README.md (2024-03-01 12:18:37 +0000) <Neil Shephard>
* 2459609 - Initial commit (2024-03-01 12:18:37 +0000) <Neil Shephard>
As you can see the history of the main branch is now
linear.
Challenge 1: Diverging Branches
In your pairs bring the square-root branch up-to-date
and incorporate the changes that have been merged into main
from the zero-division branch and then create a Pull
Request to merge the updated square-root changes into
main on GitHub, review it and merge it.
The person who has been working on the square-root
issue/branch will be at the helm for this, but work together to come up
with a solution. You can use either of the two strategies
git merge or git rebase to do this.
IMPORTANT - You should encounter a “conflict”, please do not try and solve this yet as we will go through how to do this.
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Diverged Branches
accDescr {Diverged Branches.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
branch "ns-rse/2-square-root"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit id: "HEAD"
checkout "main"
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Merging Diverged Branches
accDescr {After - main, which has already had the work in ns-rse/1-zero-division merged into it, is
merged into ns-rse/2-square-root. Development is completed on ns-rse/2-square-root and the feature merged
into main.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
branch "ns-rse/2-square-root"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit id: "HEAD"
checkout "ns-rse/2-square-root"
merge "main" id: "8-a80cef8" tag: "Merge main"
commit id: "9-cb839a0"
commit id: "10-1ae54c3"
checkout "main"
merge "ns-rse/2-square-root" id: "11-a8c9932" tag: "v0.1.2"
%%{init: {'theme': 'base', 'gitGraph': {'rotateCommitLabel': true}
}
}%%
gitGraph
accTitle: Rebase
accDescr {Rebase.}
commit id: "0-472f101"
commit id: "1-51816d6"
commit id: "2-6769ff2 (base)"
branch "ns-rse/1-zero-division"
checkout "ns-rse/1-zero-division"
commit id: "3-8c52dce"
checkout "ns-rse/1-zero-division"
commit id: "5-2315fa0"
checkout main
merge "ns-rse/1-zero-division" id: "7-bc43901" tag: "v0.1.1"
commit tag: "Rebase"
branch "ns-rse/2-square-root"
checkout "ns-rse/2-square-root"
commit id: "4-8ec389a"
checkout "ns-rse/2-square-root"
commit id: "6-93e787c"
commit id: "7-cb839a0"
commit id: "8-1ae54c3"
checkout "main"
merge "ns-rse/2-square-root" id: "9-ba93020" tag: "v0.1.2"
The first thing to do is make sure main is up-to-date
and has the changes that have been merged from the
zero-division branch locally.
Then you can switch branches to the square-root branch
and merge the main branch in.
You should have received a message about conflicts in the
tests/test_arithmetic.py file.
The first thing to do is make sure main is up-to-date
and has the changes that have been merged from the
zero-division branch locally.
Then you can switch branches to the square-root branch
and merge and rebase onto main.
You should have received a message about conflicts in the
tests/test_arithmetic.py file.
BASH
git rebase main
Auto-merging pythonmaths/arithmetic.py
Auto-merging tests/test_arithmetic.py
CONFLICT (content): Merge conflict in tests/test_arithmetic.py
error: could not apply bb4294a... feature: adds square root function
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Recorded preimage for 'tests/test_arithmetic.py'
Could not apply bb4294a... feature: adds square root function
Oh no I’ve got a merge conflict
Both the git merge and git rebase
strategies in the examples we went through worked because none of the
changes that were made touched the same files. In the Challenge though
there was a conflict because both commits had added code to the end of
tests/test_arithmetic.py and Git didn’t know which should
be kept. This situation is known as “conflict” and resolving it requires
manual intervention.
If you have undertaken the Git & GitHub Through GitKraken - From Zero to Hero! course you will have encountered merge conflicts when working through the “Python Calculator” exercise and have some idea of how to resolve them. We will however now go through resolving the issue when updating diverged branches.
conflictStyle = diff3
There is a useful tweak we can make to our configuration before
working through resolving merge conflicts that makes it a little easier
to see/understand what changes have been made. If we set the
merge.conflictStyle = diff3 then we are shown the parent
commit/state of one of the branches as well as the differences between
the two.
Enable this now…
2. Create branch1 and add a README.md
Again we add a README.md but this time we make two
commits to it, adding an extra line.
BASH
git switch -c branch1
echo -e "# Just a test\n\n" > README.md
git add README.md
git commit -m "docs: Adding README.md"
echo -e "Lets add another line in a separate commit" >> README.md
git add README.md
git commit -m "docs: Ooops, missed a line from the README.md"
git lol
* 59d399c - (HEAD -> branch1) docs: Ooops, missed a line from the README.md (2026-02-24 12:13:18 +0000) <Neil Shephard>
* 4901898 - docs: Adding README.md (2026-02-24 12:13:18 +0000) <Neil Shephard>
* 5750ad9 - (main) Initial commit (2026-02-24 12:12:55 +0000) <Neil Shephard>
(END)
4. Create branch2 and add a README.md
We now set ourselves up for a conflict by creating a
README.md on branch2, knowing full well that
such a file already exists on branch1. We put different
text into it.
5. Merge branch1 into main
Switch to main and merge branch1. As we are
done with this branch we can delete it now. The README.md
has the text from branch1.
6. Switch to branch2 and add another line to
README.md
Switch back to branch2 and add another line to
README.md, stage and commit it. The history now shows that
we have two commits on this branch after the “Initial commit”.
BASH
# Switch to branch2 add more to `README.md` and rebase
git switch branch2
echo -e "Lets add another commit to make things messier" >> README.md
git add README.md
git commit -m "Bulking out README.md with more information"
git lol
* 22c7da7 - (HEAD -> branch2) Bulking out README.md with more information (2026-02-24 12:16:09 +0000) <Neil Shephard>
* bb1b362 - This repo needs a README.md (2026-02-24 12:14:39 +0000) <Neil Shephard>
* 5750ad9 - Initial commit (2026-02-24 12:12:55 +0000) <Neil Shephard>
6. Rebase branch2 onto main
We now want to update branch2 by rebasing onto
main so that we have the new changes from main
(i.e. those merged from branch1). In this instance though
we know both branch1 and branch2 have
modified the file README.md and so we expect to get a
conflict and sure enough we do.
BASH
git rebase main
Auto-merging README.md
CONFLICT (add/add): Merge conflict in README.md
error: could not apply bb1b362... This repo needs a README.md
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Recorded preimage for 'README.md'
Could not apply fcfe2db... This repo needs a README.md
Oh dear we have, as expected, encountered the dreaded “merge
conflict” as both branch1 and branch2 made
changes to README.md in the same place. Lets take
a look at what the file now looks like.
BASH
cat README.md
<<<<<<< HEAD
# Just a test
Lets add another line in a separate commit
||||||| parent of bb1b362 (This repo needs a README.md)
=======
# Just a test
But we're creating a merge conflict
>>>>>>> bb1b362 (This repo needs a README.md)
Here HEAD refers to the current branch
that we have checked out (main) which includes the changes
we made on branch1 and merged into main. The
text that this refers to is delimited by
<<<<<<< and |||||||.
The parent of bb1b362 on is present because we set
merge.conflictStyle = diff3 and so we are shown the state
of the file at this point before commit bb1b362
was made between ||||||| and =======, in this
case it is empty because there was no README.md file. The
last section between ======= and
>>>>>>> bb1b362 (This repo needs a README.md)
shows what was added by the commit bb1b362.
We are given some useful information as to what we could do and there are three options.
Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted files>", then run "git rebase --continue".You can instead skip this commit: run "git rebase --skip".To abort and get back to the state before "git rebase", run "git rebase --abort".
These are really useful messages telling us how we can proceed. In
this instance we want to take option 1, so we should open the
README.md and edit it to leave it in the state we want the
file to be in.
7. Resolve the conflict
You can use the nano editor to open the file with
nano README.md. Edit it to look like this:
Nano
In nano You
can use Ctrl+k to cut a whole line at once
(Ctrl+u inserts the line that was last removed). Save the
file and return to the command prompt (in nano this is
Ctrl+O then Ctrl+X).
A useful bookmark to help whilst developing the muscle memory for the commands is the nano shortcuts cheatsheet.
It is possible that your system may use a different editor than
nano by default, e.g. vim. It does not matter
which text editor you use to edit and save the files.
8. Add the conflicted file and continue with rebase
You can now continue with the advice and add the conflicted files back to Git and continue with the rebase.
BASH
git add README.md
git rebase --continue
Recorded resolution for 'README.md'.
[detached HEAD d041adb] This repo needs a README.md
1 file changed, 4 insertions(+)
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: could not apply 22c7da7... Bulking out README.md with more information
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
error: could not parse conflict hunks in 'README.md'
Could not apply 22c7da7... Bulking out README.md with more information
Hang on, we just resolved the merge conflict why are we being told
there is another? Well the first conflict was with commit
bb1b362 and has been resolved and we are told as much in
the line Recorded resolution for 'README.md', however there
is now a conflict between that and commit 22c7da7. We get
the same advice so lets take a look at the state of
README.md
BASH
# Just a test
Lets add another line in a separate commit
<<<<<<< HEAD
But we're creating a merge conflict
||||||| parent of 22c7da7 (Bulking out README.md with more information)
=======
Lets add another commit to make things messier
>>>>>>> 22c7da7 (Bulking out README.md with more information)
Here we can see it’s the second line that we added to
README.md under branch2 that read
Lets add another commit to make things messier that is
causing the problem. It’s not in the main branch on which
we are rebasing so Git doesn’t know whether it should be and we have to
manually resolve this. Edit the file so that it looks like the
following.
9. Add the conflicted file and continue with the second stage of the rebase
Then add the conflicted file and continue with the rebase.
BASH
git add README.md
git rebase --continue
[detached HEAD 0ccfe91] Bulking out README.md with more information
1 file changed, 1 insertion(+), 1 deletion(-)
Successfully rebased and updated refs/heads/branch2.
We are told that the rebase has been successful and
branch2 now contains all commits from main
(which includes those merged from branch1). If we look at
the contents of README.md it contains all of the lines we
added to both branches as that is how we chose to resolve the conflicts
manually.
BASH
cat README.md
# Just a test
Lets add another line in a separate commit
But we're creating a merge conflict
Lets add another commit to make things messier
The history/graph is linear now and shows that branch2
is two commits ahead of main.
BASH
git lol
* 0a7f5db - (HEAD -> branch2) Bulking out README.md with more information (2026-02-24 12:16:09 +0000) <Neil Shephard>
* ae93af0 - This repo needs a README.md (2026-02-24 12:14:39 +0000) <Neil Shephard>
* 59d399c - (main) docs: Ooops, missed a line from the README.md (2026-02-24 12:13:18 +0000) <Neil Shephard>
* 4901898 - docs: Adding README.md (2026-02-24 12:13:18 +0000) <Neil Shephard>
* 5750ad9 - Initial commit (2026-02-24 12:12:55 +0000) <Neil Shephard>
You may be wondering why when performing git rebase it
mentions git merge. This is because a rebase will
sequentially merge all commits from the branch you are rebasing onto, in
this case main, into the HEAD of your current
checked out branch (branch2).
Repeating yourself
You only had to resolve two merge conflicts here, if the history you are merging has a lot of commits you may end up solving the same merge conflict repeatedly and wonder why you keep on getting asked to resolve the same conflict. There is a way to avoid this though.
Avoid divergence if possible
Bringing a diverged branch up-to-date can get very messy and confusing if there is a large amount of divergence. The best strategy to avoid this complication is two fold.
- Break work down into small chunks and regularly merge them into
main. - If this can not be avoided or lots of others are making changes you
should
git mergeorgit rebaseyour feature branch ontomainfrequently.
Even if you keep your work and branches small you may still encounter
this situation and find that you are repeatedly resolving the same
conflict as you want the finer grained control over
git rebase and one option is to
git rebase --abort and use git merge instead
as you only have to resolve the conflicts once, although there may be a
lot of them. One disadvantage of this is it makes it look like the
commits stem from you and so many people prefer the rebase strategy.
Help is at hand though if you find you are repeatedly being asked to resolve the same conflict as you progress through a rebase in the form of rerere which stands for “reuse recorded resolution” and causes Git to remember how it has resolved merge conflicts at a given point and the next time it is encountered it will use the solution from the first instance.
You can enable this in your global configuration, which is covered in greater detail in the next episode, with the following.
If you only wish to use this strategy on some repositories you can apply it to your local configuration from within the working directory.
You can of course enable globally and disable locally as local configuration variables take precedence over global.
Squash commits before rebasing/merging
If you have lots of small commits you can perform an interactive
rebase (git rebase -i HEAD^^^^^^^^^) to squash these into a
single commit prior to initiating the rebase or merge.
Challenge 2: Diverging Branches
You didn’t complete the merge/rebase in the previous challenge because of the merge conflict. In your pairs do so now.
If we look at the conflict in tests/test_arithmetic.py
we can see at the bottom the sections that are conflicting. They are
de-marked by <<<<<<<,
|||||||, ======= and
>>>>>>> and the HEAD and
<commit-hash> show which branches they originate
from.
The exact solution will depend on the strategy you have chosen
(merge or rebase) to bring the branch
up-to-date.
BASH
cat tests/test/arithmetic.py
<<<<<<< HEAD
@pytest.mark.parametrize(
("x", "target"),
[
pytest.param(4, 2, id="square root of 4"),
pytest.param(9, 3.0, id="square root of 9"),
pytest.param(25, 5.0, id="square root of 25"),
pytest.param(2, 1.4142135623730951, id="square root of 2"),
],
)
def test_square_root(x: int | float, target: int | float) -> None:
"""Test the square_root() function."""
pytest.approx(arithmetic.square_root(x), target)
||||||| 80b95c0
=======
def test_divide_zero_division_exception() -> None:
"""Test that a ZeroDivisionError is raised by the divide() function."""
with pytest.raises(ZeroDivisionError):
arithmetic.divide(2, 0)
>>>>>>> main
We know that we want both of these changes so we can edit the file and remove the delimiters to leave it looking like the following.
Then you can switch branches to the square-root branch
and merge the main branch in.
BASH
@pytest.mark.parametrize(
("x", "target"),
[
pytest.param(4, 2, id="square root of 4"),
pytest.param(9, 3.0, id="square root of 9"),
pytest.param(25, 5.0, id="square root of 25"),
pytest.param(2, 1.4142135623730951, id="square root of 2"),
],
)
def test_square_root(x: int | float, target: int | float) -> None:
"""Test the square_root() function."""
pytest.approx(arithmetic.square_root(x), target)
def test_divide_zero_division_exception() -> None:
"""Test that a ZeroDivisionError is raised by the divide() function."""
with pytest.raises(ZeroDivisionError):
arithmetic.divide(2, 0)
If you check the status you see that you are in the middle of a merge
and that tests/test_arithmetic.py has been modified by
both.
BASH
git status
On branch ns-rse/2-square-root
Your branch is up to date with 'origin/ns-rse/2-square-root'.
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
modified: pythonmaths/arithmetic.py
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: tests/test_arithmetic.py
You now need to stage your changes and commit them.
You can now create a pull request, assign it for review and merge it.
BASH
cat tests/test/arithmetic.py
<<<<<<< HEAD
@pytest.mark.parametrize(
("x", "target"),
[
pytest.param(4, 2, id="square root of 4"),
pytest.param(9, 3.0, id="square root of 9"),
pytest.param(25, 5.0, id="square root of 25"),
pytest.param(2, 1.4142135623730951, id="square root of 2"),
],
)
def test_square_root(x: int | float, target: int | float) -> None:
"""Test the square_root() function."""
pytest.approx(arithmetic.square_root(x), target)
||||||| 80b95c0
=======
def test_divide_zero_division_exception() -> None:
"""Test that a ZeroDivisionError is raised by the divide() function."""
with pytest.raises(ZeroDivisionError):
arithmetic.divide(2, 0)
>>>>>>> main
We know that we want both of these changes so we can edit the file and remove the delimiters to leave it looking like the following.
Then you can switch branches to the square-root branch
and merge the main branch in.
BASH
@pytest.mark.parametrize(
("x", "target"),
[
pytest.param(4, 2, id="square root of 4"),
pytest.param(9, 3.0, id="square root of 9"),
pytest.param(25, 5.0, id="square root of 25"),
pytest.param(2, 1.4142135623730951, id="square root of 2"),
],
)
def test_square_root(x: int | float, target: int | float) -> None:
"""Test the square_root() function."""
pytest.approx(arithmetic.square_root(x), target)
def test_divide_zero_division_exception() -> None:
"""Test that a ZeroDivisionError is raised by the divide() function."""
with pytest.raises(ZeroDivisionError):
arithmetic.divide(2, 0)
If you check the status you see that you are in the middle of a merge
and that tests/test_arithmetic.py has been modified by
both.
BASH
git status
On branch ns-rse/2-square-root
Your branch is up to date with 'origin/ns-rse/2-square-root'.
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
modified: pythonmaths/arithmetic.py
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: tests/test_arithmetic.py
You now need to stage your changes and commit them.
You can now create a pull request, assign it for review and merge it.
Merge or Rebase
Arguments rage online between experienced users as to whether you
should git merge or git rebase. It can often
be a matter of preference and you should agree within your team which
strategy to use and stick with it.
However it is worth noting that if you git merge your
changes from the main branch into your feature branch when
you come to merge your feature branch into main via a Pull
Request then the git diff will show all changes for commits
that have been merged into main since your feature branch
was made and not just the changes you have made in your feature branch
(i.e. the commits that have already been merged into main
also appear in your pull request). This can make reviewing pull requests
considerably harder and is a good case for using git rebase
to keep your feature branches up-to-date when you know they have
diverged.
On the flip side it can be very difficult to recover from a
git rebase that has gone wrong and there is scope that you
might lose work making git merge a safer option.
- Branches can become outdated as work progresses
- Branches can be brought up-to-date with either
git mergeorgit rebase.