| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | git 2.52.0 |
| Applies to | Any host with Git installed |
| Privilege | Normal user |
| Scope | Rebase a feature branch onto main or origin/main, resolve conflicts, merge or fast-forward when needed, abort or undo a rebase, and use interactive rebase on local commits. Worked examples state a starting branch situation and the commands for that case — not a from-scratch lab. Does not cover rebase --onto, autosquash, or rebasing shared public history without coordination. |
| Related guides | Git merge vs rebase Git merge examples Git reflog tutorial Git workflow Git branch examples |
git rebase moves your branch commits so they start from the tip of another branch — usually main. Your changes replay one commit at a time on top of the new base, which rewrites commit hashes on the rebased branch.
Each section opens with a starting state — enough context to understand the next command, not a lab you must reproduce from an empty repository. The worked examples are independent: read the one that matches your situation. You do not need to run them in order or build artificial commits first.
What is Git Rebase?
Rebase solves a common problem: you branched off main, made commits, and meanwhile main moved forward. Your feature branch still starts from the old point.
Before rebase:
A---B---E main
\
C---D my-feature-branchCandDare commits you made on the feature branch after branching fromB.Elanded onmainafter you created the branch —mainmoved forward while you worked.
Commands:
git checkout my-feature-branch
git rebase mainRead that as: take the commits that belong to my-feature-branch and replay them on top of main.
After rebase:
A---B---E main
\
C'---D' my-feature-branch- Git takes the changes introduced by
CandDand reapplies them afterE. - Git creates new commits
C'andD', so their hashes change. mainitself does not move duringgit rebase main. Only the current feature branch is rewritten.
Rebase does not add a merge commit while replaying your branch. For a side-by-side decision guide, see Git merge vs rebase.
git merge or coordinate a force-push after rebase.
Which Branch You Rebase
Run git rebase <target> while checked out on the branch whose commits you want Git to replay:
git checkout my-feature-branch
git rebase mainThat means: replay my-feature-branch on top of main. The target (main, origin/main, or another branch) is the new base. Your current branch is the one Git rewrites.
If you run the command from the wrong branch, Git rebases the wrong history:
git checkout main
git rebase my-feature-branchHere Git would try to replay main's commits on top of my-feature-branch — usually not what you want.
Scenario 1: Clean Rebase onto Main
Starting state
You are on my-feature-branch with one commit that changed git_rebase/script2.sh. While you worked, main picked up a commit that only touched git_rebase/script1.sh. The branches diverged; your feature commit is not on main yet.
| Branch | Latest commit |
|---|---|
my-feature-branch (HEAD) |
7a31e67 Added some comment in script2 |
main |
b6d8dfc Added name in script1.sh |
Goal: replay your feature commit on top of main, then fast-forward main to the rebased tip.
Step 1: Check out the branch to rebase
If you are already on my-feature-branch, skip this step.
git checkout my-feature-branchStep 2: Rebase onto main
Replay your branch commits on top of the current main tip:
git rebase mainSuccessfully rebased and updated refs/heads/my-feature-branch.Different files changed on each branch, so Git finishes without stopping.
Step 3: Check the history after rebase
Your feature commit should now sit directly on top of the latest main commit:
git log --oneline68439d1 (HEAD -> my-feature-branch) Added some comment in script2
b6d8dfc (main) Added name in script1.sh
5589053 Initial commitNotice that main still points to b6d8dfc. Rebase did not merge the feature branch into main — it only rebuilt my-feature-branch on top of main. Step 4 moves main forward to the rebased feature tip.
The feature commit hash changed (7a31e67 became 68439d1) because rebase created a new commit object.
Step 4: Fast-forward main to the rebased tip
Switch to main and merge the rebased branch:
git checkout main
git merge my-feature-branchReview the integrated history:
git log --onelineUpdating b6d8dfc..68439d1
Fast-forward
git_rebase/script2.sh | 1 +
1 file changed, 1 insertion(+)
68439d1 (HEAD -> main, my-feature-branch) Added some comment in script2
b6d8dfc Added name in script1.sh
5589053 Initial commitBecause main was an ancestor of the rebased tip, Git fast-forwards instead of creating a merge commit.
Scenario 2: Resolve Git Rebase Conflicts
Starting state
You are on conflict-feature with one commit that appended a line to git_rebase/script2.sh. main also has a new commit that appended a different line to the same file. The file on main already contains ## Some comment from an earlier change — unrelated to your new line.
| Branch | What changed in git_rebase/script2.sh |
|---|---|
conflict-feature (HEAD) |
Added ##Adding in script2 under my-feature-branch |
main |
Added ##Adding in script2 under main-branch (plus ## Some comment from before) |
Goal: rebase conflict-feature onto main, resolve the conflict in script2.sh, then fast-forward main.
Step 1: Start the rebase
You are already on the branch to rebase. Replay its commit on top of main:
git rebase mainAuto-merging git_rebase/script2.sh
CONFLICT (content): Merge conflict in git_rebase/script2.sh
error: could not apply 7b64423... Updated script2.sh
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 7b64423... # Updated script2.shGit pauses until you fix the file and run git rebase --continue. Check which files still need work:
git statusinteractive rebase in progress; onto 4f80a92
Last command done (1 command done):
pick 7b64423 # Updated script2.sh
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
both modified: git_rebase/script2.shgit status lists unmerged files and reminds you whether to continue, skip, or abort. Git may label the state interactive rebase in progress even after a plain git rebase main — that is normal on modern Git. Run git status before you edit conflict markers.
Step 2: Understand the conflict markers
Open the conflicted file and read the full block Git wrote:
cat git_rebase/script2.sh#!/bin/bash
echo "This is script2"
echo "Date: $(date)"
## Some comment
<<<<<<< HEAD
##Adding in script2 under main-branch
=======
##Adding in script2 under my-feature-branch
>>>>>>> 7b64423 (Updated script2.sh)Lines above <<<<<<< HEAD are unchanged — both sides already agreed on them. Here ## Some comment was already on main and is not part of the conflict.
In plain terms, Git splits the file into two competing versions:
| Part of the file | Plain name | What it is in this example |
|---|---|---|
Top block — from <<<<<<< HEAD down to ======= |
From main |
The line already on main: ##Adding in script2 under main-branch |
Bottom block — from ======= down to >>>>>>> |
Your commit | The line from your replayed commit on conflict-feature: ##Adding in script2 under my-feature-branch |
You are rebasing your branch onto main, so Git already applied main's content first, then tried to replay your commit on top. The conflict appears where both sides edited the same place differently.
Read the markers top to bottom:
1. Top block — from main (already on the branch you rebase onto)
<<<<<<< HEAD
##Adding in script2 under main-branch<<<<<<< HEADmarks the start of main's side.- This is the version currently present on the rebased HEAD during the replay. You still decide which content survives when resolving the conflict.
HEADmeans the tip ofmainduring the replay — not your feature branch name. During a rebase, upstream plus already-replayed commits form one side and the commit being replayed forms the other.
2. Separator
==============is not part of either version — it only separates the two blocks.
3. Bottom block — your commit (being replayed)
##Adding in script2 under my-feature-branch
>>>>>>> 7b64423 (Updated script2.sh)- Everything after
=======until>>>>>>>is your side — the change from the commit Git is trying to replay. 7b64423is that commit's short hash. The messageUpdated script2.shmatches the commit you made onconflict-feature.
Step 3: Resolve the conflict and continue the rebase
Remove every marker line and keep the content you need from the top block, the bottom block, or both:
| What you need | Keep from the file | Resolved content (conflict section only) |
|---|---|---|
main is correct |
Top block only — from main |
##Adding in script2 under main-branch |
| Your commit is correct | Bottom block only — your replayed commit | ##Adding in script2 under my-feature-branch |
| Both are valid | Both blocks — combine the lines | Both lines, in any sensible order |
Lines above the conflict (here ## Some comment) stay as they are in every case.
This walkthrough uses the third case — both additions should remain. The resolved file looks like this:
#!/bin/bash
echo "This is script2"
echo "Date: $(date)"
## Some comment
##Adding in script2 under main-branch
##Adding in script2 under my-feature-branchSave the file, stage it, and continue — do not run git commit during a rebase:
git add git_rebase/script2.sh
git rebase --continueGit may open your editor to confirm the commit message; save and close to finish the replay.
Successfully rebased and updated refs/heads/conflict-feature.After --continue, Git moves to the next commit that needs replaying. If that commit also conflicts, Git stops again. Resolve the new conflict, stage it, and run git rebase --continue again. This continues until every commit has been replayed. If multiple files conflict in one commit, resolve and stage each file before you continue.
Step 4: Fast-forward main to the rebased tip
Switch to main and merge the rebased branch:
git checkout main
git merge conflict-featureUpdating 4f80a92..605a5ee
Fast-forward
git_rebase/script2.sh | 1 +
1 file changed, 1 insertion(+)main now contains all three lines in script2.sh: the pre-existing comment plus both branch-specific additions.
Continue, Abort, or Skip a Rebase
Starting state: a rebase is in progress — Git stopped after a conflict or while replaying a commit.
| Command | Use |
|---|---|
git rebase --continue |
Continue after resolving and staging conflicts |
git rebase --abort |
Cancel the entire in-progress rebase and restore the branch to its pre-rebase state |
git rebase --skip |
Drop the currently replayed commit and continue |
Use --skip only when you intentionally do not want the conflicted commit's changes. Skipping excludes that commit's patch entirely, which is rarely what you want for normal feature work.
Undo a Completed Git Rebase
Starting state while rebase is still running: run git rebase --abort to return your branch to exactly where it was before git rebase started.
Starting state after rebase finished: the replay already completed and the result is wrong. Rebase replaces your old commits with newly created ones, but Git's reflog still records where the branch pointed before the rewrite. You can use that old commit ID to create a recovery branch or move the current branch back.
Check out the rebased branch and read its reflog:
git checkout conflict-feature
git reflog show conflict-feature605a5ee conflict-feature@{0}: rebase (finish): refs/heads/conflict-feature onto 4f80a92
7b64423 conflict-feature@{1}: commit: Updated script2.sh
68439d1 conflict-feature@{2}: branch: Created from HEADconflict-feature@{1} is where the branch pointed before the completed rebase. Keep that tip on a recovery branch:
git branch before-rebase conflict-feature@{1}To move the current branch back instead:
git reset --hard conflict-feature@{1}git reset --hard also replaces uncommitted working-tree changes, so stash or commit anything you still need before running it.
For deeper reflog workflows, see Git reflog tutorial.
Rebase onto the Latest Remote Main
Starting state: you are on my-feature-branch. origin/main moved ahead on the server while you committed locally. Your local main may still be stale.
Fetch the remote tip without switching branches:
git fetch originCheck out the branch you want to replay (skip if you are already on it):
git checkout my-feature-branchRebase onto the fetched remote tip:
git rebase origin/maingit fetch updates origin/main. git rebase origin/main replays your feature-branch commits on top of that tip — the same replay as git rebase main, but against the remote state.
Starting state after rebase: you already pushed my-feature-branch before rebasing, so the remote still has the old commit IDs. A normal push is rejected:
! [rejected] my-feature-branch -> my-feature-branch (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.--force-with-lease allows the rewritten push only when the remote branch still matches the state Git expects, making it safer than --force:
git push --force-with-lease origin my-feature-branch+ 5e70a5e...091b79a my-feature-branch -> my-feature-branch (forced update)Coordinate with teammates before force-pushing a shared branch.
Interactive Git Rebase
Starting state: you are on a feature branch with several local commits you have not shared yet, and you want to squash, reword, or drop some of them before opening a merge request.
Confirm the commits you are about to rewrite:
git log --oneline -3When those three lines are the ones you want to edit, start the interactive rebase:
git rebase -i HEAD~3Git opens an editor with a todo list like this:
pick a12bc34 Add login form
pick b23cd45 Fix login validation
pick c34de56 Update login error messageChange the action words at the start of each line. For example:
pick a12bc34 Add login form
squash b23cd45 Fix login validation
reword c34de56 Update login error messagesquashcombines the second commit into the first.rewordkeeps the third commit's changes but lets you edit its message.
Save and close the editor. Git performs those operations and may open the editor again for combined or reworded commit messages.
Common actions:
| Action | What it does |
|---|---|
pick |
Keep the commit |
reword |
Change its commit message |
squash |
Combine it with the previous commit |
edit |
Pause so you can modify it |
drop |
Remove the commit |
Interactive rebase is useful when you want to clean up local work before sharing the branch. Like a normal rebase, changing commits creates new commit IDs — push with --force-with-lease if the branch was already on the remote.
When Should You Use Git Rebase Instead of Merge?
Use rebase when:
- Your private feature branch is behind
mainand you want your changes replayed after the latestmaincommits. - You want to clean up your own commits before review — squash fixups, reword messages, or drop WIP commits with
git rebase -i. - You want a linear feature history without a merge commit whose only job was to bring
maininto your branch.
Prefer merge when:
- Other developers already based work on the commits you would rewrite.
- Preserving the actual branch and merge timing in history matters for your team or release process.
| Topic | git merge |
git rebase |
|---|---|---|
| History shape | Preserves branch topology; may add a merge commit | Linear replay on the target tip |
| Commit hashes on your branch | Unchanged | Rewritten for replayed commits |
| Conflict handling | Usually one resolution pass | Can repeat per replayed commit |
| Shared branches | Safe default | Avoid without team agreement |
For a longer comparison, see Git merge vs rebase.
Troubleshooting
| Problem | What to do |
|---|---|
cannot rebase: You have unstaged changes |
Commit or stash the changes before starting |
CONFLICT (content) |
Run git status, resolve markers, git add, then git rebase --continue |
fatal: invalid upstream 'main' |
Check the actual branch name with git branch -a; it may be master or only origin/main exists locally |
| Push rejected after rebase | If this is your rewritten feature branch, update it with git push --force-with-lease origin <branch> |
| Want to cancel current rebase | git rebase --abort |
References
Git documentation: git-rebase
Pro Git: Rebasing
GitLab Docs: Rebase and resolve merge conflicts
Git documentation: git-reflog
Git documentation: git-push
Summary
Rebase replays your feature-branch commits on top of a newer base such as main or origin/main. The before/after diagram in What is Git Rebase? shows the core idea: C' and D' replace C and D, while main does not move until you merge or fast-forward it afterward.
The clean-rebase worked example covers a replay when each branch touched different files. The conflict example covers git status after a stop, marker blocks from main versus your replayed commit, and how --continue may pause again for the next replayed commit. Neither example depends on the other.
For remote work, git fetch origin followed by git rebase origin/main refreshes your branch against the server tip. If you already pushed the branch, use git push --force-with-lease because rebase rewrites commit IDs. Use git rebase --abort while a rebase is in progress, or git reflog show <branch> and conflict-feature@{1} plus git branch or git reset --hard if the rebase already finished and you need the old tip back.
Rebase suits local or private feature branches before review. For shared branches, merge remains the safer default — see When Should You Use Git Rebase Instead of Merge? above.

