The Power of Rebasing Before Merging

Raul NaupariRaul Naupari
6 min read

When using Git, understanding how to rebase our feature branch onto a target branch before merging is a must, particularly in teams that prefer to maintain a linear commit history. Let's explore the common situations we might encounter when using the git rebase command.

Rebasing is like saying: "Take all the work I did on my feature branch, pretend I started it today based on the latest version of the main code, and put my changes on top of that." It cleans up the history by making it look like development happened sequentially, even when it didn't.

As the first step, we will clone a GitHub repository by running the commands:

git clone <MY_REPO_URL>
cd <MY_REPO_DIR>

Before creating any text files, run the following command to set UTF-8 as the default encoding (PowerShell):

$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'

No Conflicts

In this scenario, we will add changes to the main and feature branches that will not cause conflicts during the rebase. Add a file in the main branch:

echo "Line 1 from main" > file.txt
git add file.txt
git commit -m "Commit 1 from main"
git push origin main

Create the feature branch from the main branch:

git checkout -b feature

Go back to the main branch and add a second file:

git checkout main
echo "Line 1 from main" > another_file.txt
git add another_file.txt
git commit -m "Commit 2 from main"
git push origin main

Modify the file.txt file in the feature branch:

git checkout feature
echo "Line 2 from feature" >> file.txt
git add file.txt
git commit -m "Commit 1 from feature"

Run git log --oneline --graph --all to see the current commit history:

* d956da2 (HEAD -> feature) Commit 1 from feature
| * 7e59e9b (origin/main, origin/HEAD, main) Commit 2 from main
|/
* a282a9d Commit 1 from main
* e315dd5 Initial commit

Always pull the latest remote changes with git fetch origin before rebasing.

Rebase the feature branch:

git rebase origin/main

As a result, the rebase is executed successfully. Finally, push the rebased branch:

git push origin feature --force-with-lease

To simulate a pull request, run:

git checkout main
git merge feature
git push origin main

Check the commit history:

* caa0728 (HEAD -> main, origin/main, origin/feature, origin/HEAD, feature) Commit 1 from feature
* 7e59e9b Commit 2 from main
* a282a9d Commit 1 from main
* e315dd5 Initial commit

Modify Conflict

In this second scenario, we will update the same file in both branches. Go back to the main branch and modify the file.txt file:

git checkout main
echo "Line 2 from main" >> file.txt
git add file.txt
git commit -m "Commit 3 from main"
git push origin main

Modify the file.txt file in the feature branch:

git checkout feature
echo "Line 3 from feature" >> file.txt
git add file.txt
git commit -m "Commit 2 from feature"

See the current commit history:

* 7083f03 (HEAD -> feature) Commit 2 from feature
| * 0825d7e (origin/main, origin/HEAD, main) Commit 3 from main
|/
* caa0728 (origin/feature) Commit 1 from feature
* 7e59e9b Commit 2 from main
* a282a9d Commit 1 from main
* e315dd5 Initial commit

Rebase the feature branch by running git rebase origin/main:

Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
error: could not apply 7083f03... Commit 2 from feature
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".
Could not apply 7083f03... Commit 2 from feature

Open the file.txt file in your favorite editor:

Line 1 from main
Line 2 from feature
<<<<<<< HEAD
Line 2 from main
=======
Line 3 from feature
>>>>>>> 7083f03 (Commit 2 from feature)

To resolve the conflict, in this case, we will keep the feature branch change:

Line 1 from main
Line 2 from feature
Line 3 from feature

Save the file and run git add file.txt. Since this is the only conflict, we can continue with the rebase by running git rebase --continue. This will open a text editor where we can update the commit message based on the changes we have made. Save and close the editor by pressing ESC, then typing :wq, and pressing ENTER. Push the rebased branch:

git push origin feature --force-with-lease

Let’s simulate a pull request:

git checkout main
git merge feature
git push origin main

Check the commit history:

* 2c5e5d5 (HEAD -> main, origin/main, origin/feature, origin/HEAD, feature) Commit 2 from feature
* 0825d7e Commit 3 from main
* caa0728 Commit 1 from feature
* 7e59e9b Commit 2 from main
* a282a9d Commit 1 from main
* e315dd5 Initial commit

Delete Conflict

In the final scenario, we will update a file in one branch and delete it in the other. Go back to the main branch and delete the another_file.txt file:

git checkout main
rm another_file.txt
git rm another_file.txt
git commit -m "Commit 4 from main"
git push origin main

The rm command is not need . The command git rm is is sufficient on its own, as it removes the file from the working directory and stages the deletion.

Modify the another_file.txt file in the feature branch:

git checkout feature
echo "Line 3 from feature" >> another_file.txt
git add another_file.txt
git commit -m "Commit 3 from feature"

See the current commit history:

* 9cc564d (HEAD -> feature) Commit 3 from feature
| * f7bf8a3 (origin/main, origin/HEAD, main) Commit 4 from main
|/
* 2c5e5d5 (origin/feature) Commit 2 from feature
* 0825d7e Commit 3 from main
* caa0728 Commit 1 from feature
* 7e59e9b Commit 2 from main
* a282a9d Commit 1 from main
* e315dd5 Initial commit

Rebase the feature branch by running git rebase origin/main:

CONFLICT (modify/delete): another_file.txt deleted in HEAD and modified in 9cc564d (Commit 3 from feature).  Version 9cc564d (Commit 3 from feature) of another_file.txt left in tree.
error: could not apply 9cc564d... Commit 3 from feature
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".
Could not apply 9cc564d... Commit 3 from feature

Resolve the conflict, in this case by deleting the file:

rm another_file.txt
git rm another_file.txt

Continue the rebase by running git rebase --continue. Then push the rebased branch by running git push origin feature --force-with-lease. Check the commit history:

* f7bf8a3 (HEAD -> feature, origin/main, origin/feature, origin/HEAD, main) Commit 4 from main
* 2c5e5d5 Commit 2 from feature
* 0825d7e Commit 3 from main
* caa0728 Commit 1 from feature
* 7e59e9b Commit 2 from main
* a282a9d Commit 1 from main
* e315dd5 Initial commit

Note that Commit 3 from feature is no longer shown as a separate commit because the file it modified was removed during conflict resolution, making its changes empty. If things go wrong or we get confused, we can always stop and reset:

git rebase --abort

Thanks, and happy coding.

0
Subscribe to my newsletter

Read articles from Raul Naupari directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Raul Naupari
Raul Naupari

Somebody who likes to code