DaleSchool

Rebase Basics

Beginner25min

Learning Objectives

  • Visually understand the difference between rebase and merge results
  • Update a branch to the latest state with git rebase
  • Edit or squash commits with interactive rebase

Working Code

Example 1: Creating a behind-the-times branch

git init
echo "v1" > file.txt
git add file.txt
git commit -m "feat: add file"

# Create a feature branch
git checkout -b feature-work
echo "feature work" > feature.txt
git add feature.txt
git commit -m "feat: add feature work"

Add a new commit to main:

git checkout main
echo "main extra work" >> file.txt
git add file.txt
git commit -m "feat: add extra work on main"

Check history:

git log --oneline --graph --all

Output:

* a1b2c3d (HEAD -> main) feat: add extra work on main
| * e4f5g6h (feature-work) feat: add feature work
|/
* f7g8h9i feat: add file

Example 2: Rebasing onto the latest main

git checkout feature-work
git rebase main

Output:

Successfully rebased and updated refs/heads/feature-work.

Check history:

git log --oneline --graph --all

Output:

* j0k1l2m (HEAD -> feature-work) feat: add feature work
* a1b2c3d (main) feat: add extra work on main
* f7g8h9i feat: add file

The history is now linear. The feature commits sit on top of main.

Try It Yourself

merge vs rebase: comparing results

Given the same starting point, here's how merge and rebase differ:

Starting state:
A - B - C (main)
      \
       D - E (feature)

After merge:
A - B - C - M (main, merge commit)
      \   /
       D - E (feature)

After rebase:
A - B - C (main)
         \
          D' - E' (feature, rebased)

Rebase replays the feature commits on top of main. The content is the same but the hashes change (D -> D').

Interactive rebase: tidying up commits

# Create several work-in-progress commits
echo "1" >> work.txt && git add work.txt && git commit -m "wip: work in progress 1"
echo "2" >> work.txt && git add work.txt && git commit -m "wip: work in progress 2"
echo "3" >> work.txt && git add work.txt && git commit -m "feat: feature complete"

Interactively rebase the last 3 commits:

git rebase -i HEAD~3

The editor opens:

pick a1b2c3d wip: work in progress 1
pick e4f5g6h wip: work in progress 2
pick i7j8k9l feat: feature complete

Squash the wip commits together:

pick a1b2c3d wip: work in progress 1
squash e4f5g6h wip: work in progress 2
pick i7j8k9l feat: feature complete

After saving, a message editor opens:

# Combined commit messages
wip: work in progress 1
wip: work in progress 2

Replace with a clean message:

wip: initial implementation

After saving, the 3 commits become 2.

Handling conflicts during rebase

git rebase main   # Conflict occurs

Output:

CONFLICT (content): Merge conflict in file.txt
error: could not apply a1b2c3d...
hint: Resolve all conflicts manually...
# Resolve the conflict
nano file.txt   # Edit

git add file.txt
git rebase --continue   # Move on to the next commit

# Or abort the rebase entirely
git rebase --abort

"Why?" -- rebase vs merge

Both combine branches, but the resulting history differs.

| | merge | rebase | | --------- | -------------------------- | ----------------------------- | | History | Preserves branch structure | Linear and clean | | Safety | Always safe | Risky on shared branches | | Conflicts | Once | Per commit | | Use case | PR merges, collaboration | Cleaning up personal branches |

Golden rule: Never rebase shared branches that have already been pushed (main, develop).

Why? Rebase changes commit hashes. If teammates have already fetched those commits, their history will conflict with yours.

# Safe rebase patterns

# 1. Clean up unpushed local branches
git rebase -i HEAD~5

# 2. Update local feature to latest main (before pushing)
git rebase main

# 3. Merge PRs through GitHub (merge commit or squash merge)

Common Mistakes

Mistake 1: Rebasing a pushed branch

git push origin feature-login   # Already pushed
git rebase main                 # Changes commit hashes
git push                        # Error!
# error: failed to push some refs...

# Force push (causes problems for teammates!)
git push --force   # Never do this on shared branches!

Mistake 2: Rebasing from the wrong branch

# Wrong: rebasing feature onto main FROM main
git checkout main
git rebase feature-login   # main's history gets rewritten!

# Correct: rebase from the feature branch
git checkout feature-login
git rebase main

Mistake 3: Panicking during interactive rebase

git rebase -i HEAD~3
# Accidentally saved something wrong in the editor

# You can always abort
git rebase --abort   # Fully restores the original state

Deep Dive

Full interactive rebase command list

Commands available for each commit in the editor:

| Command | Short | Description | | -------- | ----- | --------------------------------------------- | | pick | p | Keep the commit as-is | | reword | r | Edit the commit message only | | edit | e | Edit the commit content | | squash | s | Merge into previous commit (combine messages) | | fixup | f | Merge into previous commit (discard message) | | drop | d | Delete the commit | | exec | e | Run a shell command between commits |

git pull --rebase: cleaner history
# Default git pull = fetch + merge (creates merge commits)
git pull

# git pull --rebase = fetch + rebase (linear history)
git pull --rebase

# Set as default
git config --global pull.rebase true

If your team prefers clean history, set pull.rebase true.

Squash merge: tidying up when merging PRs

Three ways to merge a PR:

# 1. Merge commit: preserve all commits + merge commit
git merge --no-ff feature-branch

# 2. Squash merge: combine all commits into one on main
git merge --squash feature-branch
git commit -m "feat: integrate feature"

# 3. Rebase merge: linear history on main
git rebase main && git checkout main && git merge feature-branch

GitHub lets you restrict allowed merge methods in PR settings.

  1. Create a feature branch and add 3 commits.
  2. Add a new commit to main as well.
  3. From the feature branch, run git rebase main and check history with git log --oneline --graph --all.
  4. Add 2 wip: commits and 1 feat: commit on feature, then use git rebase -i HEAD~3 to squash the wip commits.

Q1. Why should you not rebase a shared branch (one that's already been pushed)?

  • A) Rebase is slower than merge
  • B) Rebase changes commit hashes, causing history conflicts with others
  • C) Rebase deletes files
  • D) GitHub doesn't support rebase