Zum Inhalt springen

Git Rebase und interaktives Rebase meistern

Praktischer Leitfaden zu git rebase und interaktivem Rebase: rebase statt merge, Commits squashen, Historie sicher umschreiben, saubere Workflows.

5 Min. Lesezeit
Visualisierung eines Git-Verlaufs, bei der ein unübersichtlicher Branch per interaktivem Rebase zu sauberen, linearen Commits bereinigt wird

Git rebase schreibt den Commit-Verlauf um. Dieser Satz begeistert dich oder er versetzt dich in Panik. Beide Reaktionen sind berechtigt. Rebase ist das mächtigste Werkzeug zur Verlaufsbearbeitung in Git, und wie jedes mächtige Werkzeug erfordert es Verständnis, bevor man es einsetzt.

Die zentrale Frage, über die Teams streiten, lautet: rebase oder merge? Merge bewahrt den exakten Verlauf, wie sich Branches aufgeteilt und wieder vereinigt haben. Rebase erzeugt einen sauberen, linearen Verlauf, der leichter zu lesen und mit bisect zu durchsuchen ist. Keines von beiden ist grundsätzlich besser — die richtige Wahl hängt vom Workflow deines Teams ab und davon, was euch im Commit-Verlauf wichtig ist.

Rebase vs. Merge: Der Trade-off

shbash
# Starting state:
# main:    A --- B --- C
# feature:      \--- D --- E
 
# After merge:
# main:    A --- B --- C ------- M
# feature:      \--- D --- E ---/
# Creates a merge commit (M). Full history preserved.
 
# After rebase:
# main:    A --- B --- C
# feature:                --- D' --- E'
# Moves feature commits to tip of main. Linear history.
# D' and E' are NEW commits (different hashes than D and E)
tstypescript
// When to use each approach
const guidelines = {
  rebase: {
    use: [
      "Updating feature branch with latest main changes",
      "Cleaning up local commits before opening a PR",
      "Squashing WIP commits into logical units",
      "When you want a linear, readable git log",
    ],
    avoid: [
      "On commits that others have already pulled",
      "On the main/release branch directly",
      "When you need to preserve exact merge history",
    ],
  },
  merge: {
    use: [
      "Merging feature branches into main (via PR)",
      "When you need to know when branches diverged",
      "When preserving contributor attribution matters",
      "On shared branches with multiple contributors",
    ],
    avoid: [
      "For keeping feature branches up-to-date (creates noise)",
    ],
  },
};

Grundlegender Rebase-Workflow

Der häufigste Einsatz von rebase: den eigenen Feature-Branch vor dem Öffnen eines Pull Requests mit den Änderungen von main aktualisieren.

shbash
# You're working on feature/auth and main has moved ahead
git checkout feature/auth
git fetch origin
 
# ❌ Merge creates unnecessary merge commits in your branch
git merge origin/main
# "Merge branch 'main' into feature/auth" — adds noise to history
 
# ✅ Rebase moves your commits to the tip of main
git rebase origin/main
# Your commits are replayed on top of the latest main
# No merge commit, clean linear history
 
# If conflicts occur during rebase:
# 1. Fix conflicts in the files
# 2. git add <resolved-files>
# 3. git rebase --continue
# Repeat for each conflicting commit
 
# If rebase goes wrong, abort and return to original state:
git rebase --abort
shbash
# After rebase, your branch has rewritten commits (new hashes)
# Force-push is required to update the remote branch
git push --force-with-lease origin feature/auth
 
# --force-with-lease is safer than --force:
# It fails if someone else pushed to the branch since your last fetch
# Prevents accidentally overwriting a teammate's work

Interaktives Rebase: Den Verlauf umschreiben

Mit interaktivem Rebase (git rebase -i) kannst du Commits bearbeiten, squashen, neu anordnen und entfernen. So verwandelst du unübersichtliche Work-in-Progress-Commits in einen sauberen, gut überprüfbaren Verlauf.

shbash
# Rewrite the last 5 commits
git rebase -i HEAD~5
 
# This opens your editor with:
pick abc1234 Add user authentication endpoint
pick def5678 WIP: fix typo in auth
pick ghi9012 WIP: forgot to add test
pick jkl3456 Add authorization middleware
pick mno7890 Fix lint errors in auth module
 
# Commands:
# pick   = use commit as-is
# reword = use commit but edit the message
# edit   = pause at this commit for amending
# squash = merge into previous commit (keep both messages)
# fixup  = merge into previous commit (discard this message)
# drop   = remove this commit entirely
shbash
# Clean up the history:
pick abc1234 Add user authentication endpoint
fixup def5678 WIP: fix typo in auth
fixup ghi9012 WIP: forgot to add test
pick jkl3456 Add authorization middleware
fixup mno7890 Fix lint errors in auth module
 
# Result: 2 clean commits instead of 5 messy ones:
# abc1234' Add user authentication endpoint
# jkl3456' Add authorization middleware
# The WIP and lint-fix commits are absorbed into their parent commits

Praktische Muster für interaktives Rebase

shbash
# Pattern 1: Squash all feature commits into one
git rebase -i main
# Mark all commits except the first as "squash"
# Write a comprehensive commit message for the whole feature
 
# Pattern 2: Split a commit that does too much
git rebase -i HEAD~3
# Mark the commit as "edit"
# When rebase pauses:
git reset HEAD~1           # Undo the commit but keep changes
git add src/auth.ts        # Stage first logical change
git commit -m "Add auth validation logic"
git add src/middleware.ts   # Stage second logical change
git commit -m "Add auth middleware"
git rebase --continue
# One commit becomes two focused commits
 
# Pattern 3: Reorder commits for better review
git rebase -i HEAD~4
# Move the "add tests" commit right after the "add feature" commit
# So the PR reads: feature → tests → refactor → docs
 
# Pattern 4: Fix a commit message deep in history
git rebase -i HEAD~10
# Mark the target commit as "reword"
# Edit the message when prompted
tstypescript
// ❌ Commit history that's hard to review
const badHistory = [
  "WIP",
  "fix",
  "more fixes",
  "actually fix the thing",
  "oops forgot file",
  "lint",
  "address PR feedback",
  "fix tests",
  "final fix",
];
 
// ✅ Commit history after interactive rebase
const cleanHistory = [
  "feat: add user authentication with JWT",
  "feat: add role-based authorization middleware",
  "test: add auth and authorization test suites",
  "docs: update API documentation for auth endpoints",
];
// Each commit is a logical unit that can be reviewed,
// reverted, or cherry-picked independently

Umgang mit Konflikten während des Rebase

Rebase spielt Commits einen nach dem anderen erneut ab. Wenn ein Commit mit dem Ziel-Branch in Konflikt steht, hält Git an und bittet dich, ihn zu lösen — für jeden widersprüchlichen Commit einzeln.

shbash
# During rebase, Git shows:
# CONFLICT (content): Merge conflict in src/auth.ts
# Fix conflicts and run "git rebase --continue"
 
# Step 1: See which files have conflicts
git status
# Both modified: src/auth.ts
 
# Step 2: Open the file and resolve conflicts
# <<<<<<< HEAD
# const token = jwt.sign(payload, SECRET, { expiresIn: '1h' });
# =======
# const token = jwt.sign(payload, config.secret, { expiresIn: '24h' });
# >>>>>>> Add authentication with configurable secret
 
# Step 3: Choose the right version (or combine both)
# const token = jwt.sign(payload, config.secret, { expiresIn: '1h' });
 
# Step 4: Stage resolved files and continue
git add src/auth.ts
git rebase --continue
# Git moves to the next commit in the rebase
shbash
# If you mess up a conflict resolution:
git rebase --abort  # Return to the exact state before rebase
 
# Pro tip: enable rerere (Reuse Recorded Resolution)
git config --global rerere.enabled true
# Git remembers how you resolved a conflict and auto-applies
# it if the same conflict appears again (common with long-lived branches)

Die goldene Regel des Rebasens

Rebase niemals Commits, die bereits in einen gemeinsamen Branch gepusht und von anderen gepullt wurden. Rebase schreibt die Commit-Hashes um. Wenn ein Teammitglied Commits hat, die auf den alten Hashes basieren, weicht dessen Verlauf von deinem ab.

shbash
# ❌ DANGEROUS: rebasing main after others have pulled
git checkout main
git rebase feature/experiment
git push --force
# Everyone who pulled main now has conflicting history
# They'll see "divergent branches" errors
 
# ❌ DANGEROUS: rebasing a shared feature branch without coordination
git checkout feature/shared-work
git rebase origin/main
git push --force
# Your teammate's local branch now conflicts with remote
 
# ✅ SAFE: rebase your own feature branch before PR
git checkout feature/my-work  # Only you work on this branch
git rebase origin/main
git push --force-with-lease origin feature/my-work
 
# ✅ SAFE: interactive rebase on unpushed commits
git rebase -i HEAD~3  # Only local commits, never pushed
# Free to squash, reorder, reword — nobody has these commits

Die wichtigsten Erkenntnisse

  1. Rebase zum Aktualisieren von Feature-Branches, Merge zum Integrieren in main — rebase hält deinen Branch linear und sauber; merge bewahrt den Integrationspunkt in gemeinsam genutzten Branches
  2. Interaktives Rebase macht aus unübersichtlichen WIP-Commits überprüfbare Commits — squashe Fixup-Commits, formuliere Nachrichten um und ordne Commits neu, bevor du einen PR öffnest
  3. Niemals gemeinsam genutzte Commits rebasen — rebase nur Commits, die ausschließlich in deinem eigenen Branch existieren; das Rebasen gemeinsamen Verlaufs zerstört den lokalen Stand aller anderen
  4. Verwende --force-with-lease statt --force — das verhindert, dass du den Push einer anderen Person überschreibst, der zwischen deinem Fetch und deinem Push stattgefunden hat
  5. Aktiviere rerere für wiederholte Konfliktlösung — wenn du häufig rebasest und dabei auf dieselben Konflikte stößt, zeichnet rerere deine Lösungen auf und wendet sie automatisch an
Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX