Git rebase / cherry-pick / worktree Advanced Team Collaboration Guide
Many people have been using Git for five years, but their commands still stay at the "add / commit / push / pull" four-piece set, with at…
Git rebase / cherry-pick / worktree Advanced Team Collaboration Guide

Many people have been using Git for five years, but their commands still stay at the "add / commit / push / pull" four-piece set, with at most a "merge" added. Once they encounter tasks like "squash these five commits into one," "cherry-pick a hotfix to two other branches," or "work on both main and hotfix simultaneously without stashing," they start to panic.
Today, I'll go over the most commonly used advanced Git commands—rebase -i / cherry-pick / worktree / stash / bisect—all at once. Each one comes with real commands and real scenarios, so you can use them immediately after reading.
Companion reading: W7 covered
reflogas a lifesaver, and this article will reference it heavily. Don't panic if rebase goes wrong; reflog is the regret pill.
rebase Mental Model: Moving Commits
Merge merges two lines into a new merge node; rebase moves a series of commits from the original base to a new base.
Before moving:
A---B---C feature
/
D---E---F---G main
After moving (git checkout feature && git rebase main):
A'--B'--C' feature
/
D---E---F---G main
Note the prime marks (A' B' C') — after rebase, commit hashes will always change because the parent has changed. This is the root of all collaboration issues later.
When to use rebase:
- Want history to be a straight line for a clean log
- Want to squash and organize multiple WIP commits before pushing for review
- Want to catch up the feature branch with the latest code on main to avoid a huge merge conflict later
When not to use rebase:
- On branches that have already been pushed to remote and are being used by others — rebasing and pushing would ruin your colleagues' work
- On public release branches (main / release) — history must retain real merge nodes
Interactive Rebase: rebase -i
This is the most frequently used one in daily work. For example, you've made 5 messy commits over the past two days:
git --no-pager log --oneline -5
# a1b2c3d Fix comment pagination bug
# d4e5f6g typo
# h7i8j9k temporary console.log
# k0l1m2n Comment pagination API
# n3o4p5q Comment pagination UI
You want to squash these 5 into a single "Comment pagination feature" commit and push it to your supervisor for review.
git rebase -i HEAD~5 # Enter interactive mode, operate on the last 5 commits
An editor (vim / VSCode) will pop up showing something like:
pick n3o4p5q Comment pagination UI
pick k0l1m2n Comment pagination API
pick h7i8j9k temporary console.log
pick d4e5f6g typo
pick a1b2c3d Fix comment pagination bug
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# d, drop = remove commit
Note: In the list, commits are ordered from oldest to newest from top to bottom, which is the opposite of git log.
Our goal:
- Keep the first UI commit
- Squash the API commit into the UI (keep both commit messages)
- Drop the console.log
- Fixup the typo into the previous commit (discard the typo message)
- Reword the bug fix to a proper description
Change it to:
pick n3o4p5q Comment pagination UI
s k0l1m2n Comment pagination API
d h7i8j9k temporary console.log
f d4e5f6g typo
r a1b2c3d Fix comment pagination bug
Save and exit. Git will pause to let you edit the squashed commit message, then let you reword the last one. After organizing, the result is:
Tip: After
dropremoves that commit, if the next commit isfixup/squash, it will merge into the previous non-dropped commit. In the example,f typowill actually be merged intos Comment pagination API, not the vanished console.log. Keep that in mind when placing drop next to fixup/squash.
git --no-pager log --oneline -2
# x9y8z7w Comment pagination feature (UI + API)
# v6u5t4s feat: Fix comment pagination edge cases
Clean.
Remember these frequently used commands:

Rebase Conflict Resolution Toolkit
Conflicts during rebase are common. The flow differs slightly from merge:
git rebase main
# CONFLICT (content): Merge conflict in src/api.ts
# Resolve conflict (editor / IDE / git mergetool all work)
vim src/api.ts
# Mark as resolved
git add src/api.ts
# Continue rebase (note: not git rebase --commit)
git rebase --continue
If you want to give up midway:
git rebase --abort # Return to state before rebase
If a commit is no longer valid (e.g., main already did the same thing), you can skip it:
git rebase --skip
During rebase, you may need to resolve conflicts multiple times – because each commit is replayed against the new base. So it's normal to resolve the same file conflict three times in a row; don't think Git is malfunctioning.
This is an age-old debate with no standard answer. Here's a convention our team has used for three years:

Our convention:
- Inside a feature branch: free to rebase (your own playground, no one else using it)
- Before merging feature into main: first
git rebase mainto catch up with latest code, then create MR - Main branch merges: use merge (preserves merge point for auditing / rollback)
- Already pushed branches: after rebasing, push must use
--force-with-lease, not-f:
# Safe: if someone else has pushed new commits to remote, it will reject
git push --force-with-lease origin feature-x
# Dangerous: directly overwrites remote, may break colleague's work
git push -f origin feature-x # Use with caution
Don't argue "rebase vs merge" in team chat — just write it into CONTRIBUTING.md and everyone follows it.
cherry-pick: Pick Hotfixes to Multiple Branches
Typical scenario: Two versions running online release-1.x and release-2.x, a bug fixed on main needs to be applied to both releases.
# 1. Find that commit on main
git --no-pager log --oneline -1
# a1b2c3d fix: Comment pagination NPE
# 2. Switch to release-1.x, cherry-pick
git checkout release-1.x
git cherry-pick a1b2c3d
# 3. Same for release-2.x
git checkout release-2.x
git cherry-pick a1b2c3d
Useful parameters:
# -x: Append "(cherry picked from commit a1b2c3d)" at the end of commit message
# Helps audit where this commit came from
git cherry-pick -x a1b2c3d
# -n: Pick but don't auto-commit, review first then commit
git cherry-pick -n a1b2c3d
git diff --cached # Check what was picked
git commit -m "fix: ... (backport from main)"
# Pick a range of consecutive commits (left-exclusive, right-inclusive)
git cherry-pick a1b2c3d..h7i8j9k
# Conflict handling (same as rebase)
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick --skip
Gotcha: After cherry-pick, the commit hash will change (since the parent is different), so the same fix has three different hashes across three branches. The -x parameter is a lifesaver for auditing backports.
worktree: Develop Multiple Branches in Parallel in the Same Repository
This command is not widely used, but once you use it, you'll never go back.
Classic scenario: You're working hard on a feature branch when your boss says, "There's an urgent bug on production."
Old way:
git stash # Stash current changes
git checkout main
git checkout -b hotfix-xxx
# Fix code, commit, merge
git checkout feature-x
git stash pop # Restore
Switching back and forth, stash might cause conflicts. If the hotfix takes hours, you'll have to deal with stash conflicts later.
worktree solution:
# Create another working directory beside current repo, checkout hotfix branch
git worktree add ../proj-hotfix -b hotfix-xxx main
# Now your file system looks like:
# ~/work/proj/ ← Your feature-x in progress, files untouched
# ~/work/proj-hotfix/ ← New hotfix workspace, can independently modify, commit, push
# Open proj-hotfix in another IDE window, finish and push
cd ../proj-hotfix
# ... fix code ...
git push origin hotfix-xxx
# After done, remove worktree
cd ../proj
git worktree remove ../proj-hotfix
Common commands:
git worktree list # See current worktrees
git worktree add <path> <branch> # Add new worktree (branch already exists)
git worktree add <path> -b <new-branch> [<base>] # Create new branch and worktree
git worktree remove <path> # Remove worktree
git worktree prune # Clean up metadata of manually deleted directories
Notes:
- The same branch cannot be checked out in two worktrees at once (will error)
- Share the same
.gitdirectory – all branches/remotes/stash are available - Large directories like
node_modulesneed to be installed in each worktree; make sure you have enough disk space
Our frontend team now uses proj/ (main development) + proj-review/ (dedicated to checking out others' branches for review). No more interrupting current work.
stash Advanced
90% of people only know git stash + git stash pop. But stash is much richer:
# Add a message when stashing for easy identification
git stash push -m "Comment pagination WIP halfway"
# View stash list
git stash list
# stash@{0}: On feature-x: Comment pagination WIP halfway
# stash@{1}: On main: Debug output
# See diff of a specific stash
git stash show -p stash@{0}
# Apply without deleting (apply) vs apply and delete (pop)
git stash apply stash@{0} # stash remains
git stash pop # Apply most recent and delete
# Stash only part of files (interactive)
git stash push -p
# Stash this hunk [y,n,q,a,d,e,?]?
# Restore stash as a branch (useful when base has changed long after stash)
git stash branch stash-restore stash@{0}
# Clear all stashes (careful!)
git stash clear
Practical advice:
- Always name your stash with
-m, otherwise you won't remember what it is after two days - For long-term WIP, use branches instead of stash – stash is for "short-term save"
- Stash does not save untracked files (use
git stash -uto include untracked,-aalso includes ignored)
bisect: Binary Search for Bugs
Scenario: A feature that worked fine two weeks ago is now reported broken by users. There are 200 intermediate commits; rolling back one by one is impractical.
bisect uses binary search to find which commit introduced the bug.
# Start
git bisect start
# Tell Git the current version is bad
git bisect bad
# Tell Git a tag from two weeks ago is good
git bisect good v1.2.0
# Git will automatically checkout the middle commit
# Bisecting: 100 revisions left to test after this (roughly 7 steps)
# [a1b2c3d] feat: xxx
# Manually test this version
npm test # Or run your own reproduction script
# Tell Git the result
git bisect good # This version is good
# Or
git bisect bad # This version is bad
# Git continues binary search, repeat about 7 times to locate the bug-introducing commit
# a1b2c3d is the first bad commit
# When done, end bisect
git bisect reset
Advanced: Fully automated
If you have a script that can determine good/bad (return 0 for good, non-zero for bad), you can run it in one shot:
git bisect start HEAD v1.2.0
git bisect run ./scripts/repro-bug.sh # Auto binary search to the end, find first bad commit
git bisect reset
With 200 commits, 7-8 binary searches will locate the bug, far more efficient than going through git log one by one.
reflog: The Regret Pill for rebase
Most common rebase disaster:
git rebase -i HEAD~10
# Accidentally dropped commits you shouldn't have
# Save and exit
# ... oh no
Don't panic. All commits altered by rebase are still alive in reflog – to be precise: reachable commits are kept for 90 days by default (gc.reflogExpire), and commits that become unreachable after rebase are kept for 30 days by default (gc.reflogExpireUnreachable). So if you find a mistake, act fast:
git reflog
# a1b2c3d HEAD@{0}: rebase finished: returning to refs/heads/feature-x
# d4e5f6g HEAD@{1}: rebase: Comment pagination UI
# h7i8j9k HEAD@{2}: rebase: Comment pagination API
# k0l1m2n HEAD@{3}: rebase (start): checkout main
# n3o4p5q HEAD@{4}: commit: The commit I shouldn't have deleted ← Here it is!
# ...
# Reset the branch to state before rebase
git reset --hard HEAD@{4}
Remember: As long as you've committed it, there's hope. See W7's article for detailed explanation.
Submodule and Subtree Overview
These are less frequently used, so a quick mention:
- submodule: Embed another independent repository inside the main repo. The main repo only stores a pointer (commit hash). Suitable for shared internal libraries, but clone/pull may cause "submodule not updated" pitfalls.
- subtree: Merge the content of another repository directly into a subdirectory of the main repo, no pointer concept. Simple but merge history can be messy.
In practice, more and more teams are moving to monorepo + workspace (npm / pnpm / yarn) or package managers (npm private registry / Maven). Submodule/subtree are not recommended for new projects. If needed, a separate article can be written.
Final Words
Git advanced commands essentially do two things:
- Rewrite history (rebase / commit --amend / cherry-pick)
- Manage workspaces (stash / worktree)
As long as you remember that rewritten commits change hashes, and be cautious with already pushed branches, plus have reflog as a regret pill, you can't really go wrong.
One final suggestion: Always try these Git advanced commands for the first time on your own local toy repo – don't practice on production branches. Once you get the hang of them, you'll save a lot of time every day.
메타데이터
- post_id
- 35f787fa4c51
- slug
- git-rebase-cherry-pick-worktree-advanced-team-collaboration-guide-35f787fa4c51
- url
- https://medium.com/@githubdaily/git-rebase-cherry-pick-worktree-advanced-team-collaboration-guide-35f787fa4c51
- canonical_url
- https://medium.com/@githubdaily/git-rebase-cherry-pick-worktree-advanced-team-collaboration-guide-35f787fa4c51
- author_url
- https://medium.com/@githubdaily
- status
- ok
- fetched_at
- 2026-07-15 14:13:45