Home CS Fundamentals Git Advanced: Rebase, Cherry-Pick, Bisect, Submodules
Intermediate 5 min · July 13, 2026

Git Advanced: Rebase, Cherry-Pick, Bisect, Submodules

Master Git advanced workflows: rebase, cherry-pick, bisect, and submodules.

N
Naren Founder & Principal Engineer

20+ years shipping production systems from the metal up. Everything here is grounded in real deployments.

Follow
Production
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic Git knowledge: commit, push, pull, merge, branching.
  • Familiarity with command line interface.
  • Understanding of Git's object model (commits, trees, blobs) is helpful but not required.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Rebase rewrites commit history by moving commits to a new base, creating a linear history.
  • Cherry-pick applies specific commits from one branch to another without merging the whole branch.
  • Bisect uses binary search to find the commit that introduced a bug.
  • Submodules allow embedding one Git repository inside another as a dependency.
✦ Definition~90s read
What is Git Advanced?

Git advanced workflows like rebase, cherry-pick, bisect, and submodules give you precise control over your commit history, debugging, and dependency management.

Think of Git commits like chapters in a book.
Plain-English First

Think of Git commits like chapters in a book. Rebase is like rewriting the order of chapters to make the story flow better. Cherry-pick is like taking a single chapter from one book and inserting it into another. Bisect is like playing a guessing game where you split the book in half to find where a mistake was introduced. Submodules are like having a separate mini-book inside your main book that you can update independently.

You've mastered the basics of Git: commit, push, pull, merge. But as your projects grow, you'll encounter scenarios where simple merges create messy histories, or you need to selectively apply changes, or you're hunting down a bug that appeared weeks ago. This is where Git's advanced features come in. Rebase lets you craft a clean, linear history. Cherry-pick gives you surgical precision to apply only the commits you need. Bisect turns debugging into a binary search. Submodules help you manage dependencies without copying code. In this tutorial, you'll learn each of these tools through practical examples, see how they can go wrong, and discover best practices to avoid common pitfalls. By the end, you'll be able to maintain a pristine commit history, debug efficiently, and manage complex multi-repo projects with confidence.

1. Rebase: Rewriting History with Purpose

Rebase is one of Git's most powerful yet misunderstood features. Unlike merge, which creates a new commit that ties two branches together, rebase moves an entire branch to a new base commit, replaying each commit on top. This results in a linear history that is easier to read and bisect. However, rebasing rewrites history, which can cause problems if you've already shared your branch with others.

When to rebase: - To integrate changes from main into your feature branch before merging. - To clean up a series of commits before merging (using interactive rebase). - To maintain a linear project history.

When NOT to rebase: - On public branches that others have based work on. - On long-lived feature branches (prefer merge).

Let's see an example. Suppose you have a feature branch 'feature' that branched off 'main' at commit A. Meanwhile, main has moved forward with commits B and C. To rebase feature onto main:

``bash git checkout feature git rebase main ``

This will replay your feature commits on top of main's latest commit. If conflicts occur, Git pauses and lets you resolve them. After resolving, use git rebase --continue. To skip a commit, use git rebase --skip. To abort, use git rebase --abort.

Interactive rebase allows you to squash, reorder, or edit commits. For example, to squash the last three commits into one:

``bash git rebase -i HEAD~3 ``

This opens an editor where you can change 'pick' to 'squash' for the commits you want to combine.

Best practice: Always rebase onto a stable base. If you're rebasing onto a moving target (like a frequently updated main), you may need to rebase multiple times. Consider using git pull --rebase instead of git pull to keep your local commits on top of remote changes.

rebase-example.shBASH
1
2
3
4
5
6
7
8
9
# Start on feature branch
git checkout feature
# Rebase onto main
git rebase main
# If conflicts occur, resolve them, then:
git add resolved-file.txt
git rebase --continue
# To abort rebase:
git rebase --abort
Output
Successfully rebased and updated refs/heads/feature.
⚠ Never rebase public branches
📊 Production Insight
In production, rebasing long-lived branches often leads to repeated conflict resolution. A better approach is to merge main into feature periodically, then rebase only before the final merge to main.
🎯 Key Takeaway
Rebase creates a linear history but rewrites commits; use it for private branches and interactive cleanup.

2. Cherry-Pick: Selective Commit Application

Cherry-pick allows you to apply a specific commit from one branch to another. This is useful when you need a bug fix that was committed on a different branch but you don't want to merge the entire branch. For example, if a hotfix was applied to main and you need it in your release branch, you can cherry-pick that commit.

Syntax: git cherry-pick <commit-hash>

You can cherry-pick multiple commits by listing them in order. If conflicts arise, resolve them and use git cherry-pick --continue. To abort, use git cherry-pick --abort.

Example: Suppose commit abc123 on main fixes a critical bug. To apply it to your release branch:

``bash git checkout release git cherry-pick abc123 ``

Important considerations: - Cherry-picking creates a new commit with a different hash, even if the changes are identical. - If the commit depends on previous commits that are not in the target branch, you may get conflicts or incomplete changes. - Cherry-picking can lead to duplicate commits if not tracked carefully.

Best practice: Use cherry-pick sparingly. If you find yourself cherry-picking many commits, consider merging the branch instead. Always test after cherry-picking, as the new commit may behave differently in a different context.

cherry-pick-example.shBASH
1
2
3
4
5
6
7
8
9
# Find the commit hash
git log --oneline main
# Output: abc123 Fix critical bug
# Switch to target branch
git checkout release
git cherry-pick abc123
# If conflict, resolve and continue
git add .
git cherry-pick --continue
Output
[release def456] Fix critical bug
💡Cherry-pick ranges
📊 Production Insight
Cherry-picking a commit that depends on other unapplied commits often leads to subtle bugs. Always review the commit's dependencies before cherry-picking.
🎯 Key Takeaway
Cherry-pick applies individual commits across branches; use it for targeted fixes, not as a regular workflow.

3. Bisect: Binary Search for Bugs

Git bisect is a powerful debugging tool that uses binary search to find the commit that introduced a bug. Instead of manually checking each commit, you tell Git a 'good' commit (where the bug didn't exist) and a 'bad' commit (where the bug exists). Git then checks out a commit halfway between them, and you test it. Based on your result, Git narrows down the range until it finds the first bad commit.

How to use: 1. Start bisect: git bisect start 2. Mark the current commit as bad: git bisect bad (or specify a commit) 3. Mark a known good commit: git bisect good <commit> 4. Git checks out a commit in the middle. Test it. 5. Mark it as good or bad: git bisect good or git bisect bad 6. Repeat until Git identifies the first bad commit. 7. Reset: git bisect reset

Automated bisect: You can automate testing with a script. For example, if you have a test script that returns 0 for success and non-zero for failure:

``bash git bisect start git bisect bad git bisect good v1.0 git bisect run ./test-script.sh ``

This will automatically run the script on each commit and mark accordingly.

Best practice: Ensure your test is reliable and fast. Bisect works best with a linear history; merge commits can complicate things. Use git bisect skip if a commit cannot be tested (e.g., build failure).

bisect-example.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
# Start bisect
git bisect start
git bisect bad                 # current commit is bad
git bisect good v1.0           # v1.0 is good
# Git checks out a commit. Test manually.
# If bug present:
git bisect bad
# If bug absent:
git bisect good
# Continue until found.
# Reset when done:
git bisect reset
Output
Bisecting: 5 revisions left to test after this (roughly 3 steps)
...
abc123 is the first bad commit
🔥Bisect with merge commits
📊 Production Insight
In production, bisect is invaluable for finding regressions in large codebases. Combine it with a CI test suite to automatically identify the culprit commit.
🎯 Key Takeaway
Bisect automates bug hunting by binary search; use it to quickly pinpoint the commit that introduced a regression.

4. Submodules: Managing Dependencies

Submodules allow you to include one Git repository inside another as a subdirectory. This is useful when your project depends on an external library that you want to track at a specific version. Unlike copying the code, submodules maintain a link to the external repo, so you can update it independently.

Adding a submodule: ``bash git submodule add https://github.com/example/lib.git lib ` This clones the repo into the 'lib' directory and creates a .gitmodules` file.

Cloning a project with submodules: ``bash git clone --recurse-submodules <repo-url> ` If you already cloned without submodules, run: `bash git submodule update --init --recursive ``

Updating submodules: ``bash git submodule update --remote `` This pulls the latest commit from the submodule's remote. Then you need to commit the change in the parent repo to record the new submodule commit.

Common pitfalls: - Submodules are often left at a detached HEAD state. Always checkout a branch inside the submodule before making changes. - If you forget to push submodule changes, others will get a broken reference. - Submodules can be confusing for new contributors.

Best practice: Use submodules for stable dependencies that you don't frequently modify. For actively developed dependencies, consider using a package manager instead.

submodule-example.shBASH
1
2
3
4
5
6
7
8
9
# Add a submodule
git submodule add https://github.com/example/lib.git lib
# Clone with submodules
git clone --recurse-submodules https://github.com/myproject.git
# Update submodule to latest remote
git submodule update --remote
# Commit the submodule change in parent
git add lib
git commit -m "Update lib submodule"
Output
Cloning into 'lib'...
Submodule 'lib' (https://github.com/example/lib.git) registered for path 'lib'
⚠ Submodule detached HEAD
📊 Production Insight
In production, submodules can cause build failures if the submodule repository is unavailable. Consider mirroring submodules or using a monorepo for critical dependencies.
🎯 Key Takeaway
Submodules embed external repos; they are great for pinning dependencies but require careful workflow to avoid detached HEAD and broken references.

5. Interactive Rebase: Squashing and Rewording

Interactive rebase is a superpower for cleaning up commit history before merging. You can squash multiple commits into one, reword commit messages, reorder commits, or even drop commits entirely. This is especially useful for feature branches where you have many 'WIP' commits that you want to combine into a logical set.

Starting interactive rebase: ``bash git rebase -i HEAD~n ` where n is the number of commits to go back. Alternatively, you can rebase onto a specific commit: git rebase -i <commit-hash>`.

Common operations: - pick - use the commit as is. - reword - change the commit message. - squash - combine the commit with the previous one, merging messages. - fixup - like squash but discard the commit message. - drop - remove the commit.

Example: Suppose you have three commits: 'WIP', 'Add feature', 'Fix typo'. You want to combine them into one commit 'Add feature'.

``bash git rebase -i HEAD~3 `` In the editor, change the second and third lines from 'pick' to 'squash' or 'fixup'. Save and exit. Then edit the final commit message.

Best practice: Squash commits that are not meaningful individually. Keep the history clean but still logical for code review. Avoid squashing commits that have been reviewed separately.

interactive-rebase.shBASH
1
2
3
4
5
6
7
8
9
10
11
# Start interactive rebase for last 3 commits
git rebase -i HEAD~3
# Editor opens with:
# pick a1b2c3 WIP
# pick d4e5f6 Add feature
# pick g7h8i9 Fix typo
# Change to:
# pick a1b2c3 WIP
# squash d4e5f6 Add feature
# fixup g7h8i9 Fix typo
# Save and exit. Then edit final message.
Output
Successfully rebased and updated refs/heads/feature.
💡Rebase before merging
📊 Production Insight
Over-squashing can hide the evolution of a feature. Strike a balance: keep commits that represent logical steps, but squash trivial fixes.
🎯 Key Takeaway
Interactive rebase lets you rewrite commit history to be clean and logical; use it to squash, reword, and reorder commits before merging.

6. Rebase vs Merge: Choosing the Right Strategy

One of the most debated topics in Git workflows is whether to rebase or merge. Both integrate changes from one branch into another, but they do so differently.

Merge: Creates a new commit that ties together the histories of both branches. It preserves the exact timeline of when commits were made. This is non-destructive and safe for shared branches.

Rebase: Rewrites the commit history of the feature branch to appear as if it was developed on top of the latest main. This results in a linear history but changes commit hashes.

When to use merge: - For public or shared branches. - When you want to preserve the context of when changes were made. - For long-lived feature branches.

When to use rebase: - For local, unpublished branches. - To maintain a clean, linear project history. - Before merging a short-lived feature branch into main.

Best practice: Many teams adopt a 'rebase before merge' policy: rebase your feature branch onto main to keep history linear, then merge (using --no-ff to create a merge commit for visibility). This combines the benefits of both.

Example workflow: ``bash git checkout feature git rebase main # Resolve conflicts if any git checkout main git merge --no-ff feature ``

rebase-vs-merge.shBASH
1
2
3
4
5
6
7
8
9
# Merge workflow
git checkout main
git merge feature

# Rebase workflow
git checkout feature
git rebase main
git checkout main
git merge --no-ff feature
Output
Merge made by the 'recursive' strategy.
# or
Successfully rebased and updated refs/heads/feature.
Merge made by the 'recursive' strategy.
🔥The golden rule of rebasing
📊 Production Insight
In large teams, a consistent workflow is crucial. Document your team's policy on rebase vs merge to avoid confusion and history corruption.
🎯 Key Takeaway
Rebase for clean history on local branches; merge for shared branches. Use 'rebase then merge --no-ff' for a balanced approach.
● Production incidentPOST-MORTEMseverity: high

The Rebase That Broke the Build

Symptom
The CI pipeline failed with merge conflicts that kept reappearing even after resolution.
Assumption
The developer assumed rebasing would be cleaner than merging, and that conflicts would be easy to fix.
Root cause
The feature branch had been diverged for weeks, and rebasing replayed each commit, causing the same conflicts multiple times. The developer resolved conflicts incorrectly in earlier commits, leading to broken code later.
Fix
The team reverted the rebase, used a merge instead, and established a policy to rebase only for short-lived branches and to use merge for long-lived ones.
Key lesson
  • Rebase is not a silver bullet; prefer merge for long-lived branches.
  • Always rebase onto a stable base (e.g., main) frequently to avoid massive conflict resolution.
  • Use interactive rebase to squash commits before rebasing to reduce conflict surface.
  • After rebasing, force-push only if you are the sole contributor to the branch.
  • Consider using 'git rerere' to remember conflict resolutions.
Production debug guideSymptom to Action4 entries
Symptom · 01
Rebase results in repeated conflicts for the same file.
Fix
Use 'git rerere' to record conflict resolutions, or abort rebase and use merge instead.
Symptom · 02
Cherry-pick causes unexpected merge conflicts.
Fix
Check if the commit depends on earlier commits that were not cherry-picked; use 'git log' to find dependencies.
Symptom · 03
Bisect incorrectly identifies a good commit as bad.
Fix
Ensure you mark commits correctly; use 'git bisect log' to review steps, and reset with 'git bisect reset' if needed.
Symptom · 04
Submodule points to a detached HEAD.
Fix
Enter the submodule directory, checkout a branch, commit, and push. Then update the parent repository.
★ Quick Debug Cheat SheetCommon symptoms and immediate actions for Git advanced workflows.
Rebase conflict hell
Immediate action
Abort rebase with 'git rebase --abort'
Commands
git rebase --abort
git merge <branch>
Fix now
Use merge instead of rebase for this branch.
Cherry-pick conflict+
Immediate action
Resolve conflicts, then continue
Commands
git cherry-pick --continue
git cherry-pick --abort
Fix now
Resolve conflicts manually and continue.
Bisect mislabel+
Immediate action
Reset bisect and restart
Commands
git bisect reset
git bisect start
Fix now
Restart bisect with correct good/bad marks.
Submodule detached HEAD+
Immediate action
Checkout a branch in submodule
Commands
cd submodule && git checkout main
cd .. && git add submodule
Fix now
Commit the submodule update in parent repo.
FeatureUse CaseHistory ImpactSafety
RebaseClean up local branch historyRewrites commits (new hashes)Dangerous on shared branches
MergeIntegrate branchesPreserves history (merge commit)Safe for all branches
Cherry-pickApply specific commitsCreates new commitsSafe but can cause duplicates
BisectFind bug-introducing commitNo permanent changesSafe, read-only
SubmodulesManage external dependenciesRecords commit hashRequires careful workflow
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
rebase-example.shgit checkout feature1. Rebase
cherry-pick-example.shgit log --oneline main2. Cherry-Pick
bisect-example.shgit bisect start3. Bisect
submodule-example.shgit submodule add https://github.com/example/lib.git lib4. Submodules
interactive-rebase.shgit rebase -i HEAD~35. Interactive Rebase
rebase-vs-merge.shgit checkout main6. Rebase vs Merge

Key takeaways

1
Rebase rewrites history for a linear commit log; use it on local branches only.
2
Cherry-pick applies specific commits across branches; be mindful of dependencies.
3
Bisect automates bug hunting with binary search; combine with automated tests for efficiency.
4
Submodules manage external dependencies but require careful workflow to avoid detached HEAD and broken references.
5
Interactive rebase allows squashing, rewording, and reordering commits for a clean history.

Common mistakes to avoid

3 patterns
×

Rebasing a public branch that others have based work on.

×

Cherry-picking a commit without checking its dependencies.

×

Forgetting to push submodule changes after updating them.

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the difference between rebase and merge. When would you use each...
Q02SENIOR
How does git bisect work? Describe a scenario where you would use it.
Q03SENIOR
What are the risks of using submodules? How would you mitigate them?
Q01 of 03SENIOR

Explain the difference between rebase and merge. When would you use each?

ANSWER
Rebase rewrites commit history to create a linear sequence, while merge preserves the original timeline with a merge commit. Use rebase for local branches to keep history clean; use merge for shared branches to avoid rewriting public history.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between rebase and merge?
02
How do I undo a rebase?
03
Can cherry-pick cause merge conflicts?
04
How do I update a submodule to the latest commit?
05
What is the best way to use bisect for a bug that appears intermittently?
N
Naren Founder & Principal Engineer

20+ years shipping production systems from the metal up. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
🔥

That's Software Engineering. Mark it forged?

5 min read · try the examples if you haven't

Previous
Domain-Driven Design: Strategic and Tactical Patterns
21 / 23 · Software Engineering
Next
System Design: A Practical Introduction