Better_Software_Header_Mobile Better_Software_Header_Web

Find what you need - explore our website and developer resources

Working on Multiple Git Branches without Switching

A Git workflow with --rebase-merges and --update-refs

# To make hashes reproducible, set the same author and date to all commits.
export GIT_AUTHOR_NAME="KDAB's Git rebase-merges demo"
export GIT_AUTHOR_EMAIL="info@kdab.com"
export GIT_AUTHOR_DATE="2025-01-22T15:00+0100"
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"

# Create a bare repository to be our `origin`
mkdir git-remote
pushd git-remote
git init --bare --initial-branch=main
popd

# Clone and simulate some pre-existing work
git clone git-remote git-local && cd git-local
git commit --allow-empty -m "Root commit"
# [main (root commit) 3657733] Root commit
echo "Feature 0" > feature0
git add feature0
git commit -m "Let's pretend some work happened upstream already"
# [main 4010570] Let's pretend some work happened upstream already
git push

# Also create a second clone so we can simulate coworkers
pushd ..
git clone git-remote git-coworker
popd

# Now your repository should look like this:
git log --all --graph --oneline
# * 4010570 (HEAD -> main, origin/main) Let's pretend some work happened upstream already
# * 3657733 Root commit
git switch -c integration --track origin/main
# branch 'integration' set up to track 'origin/main'.
# Switched to a new branch 'integration'
echo 'feat 1' > feature1
git add feature1
git commit -m "Feature 1"
# [integration 4f73e16] Feature 1

echo 'bugfix1' >> feature0
git add feature0
git commit -m "Bugfix 1"
# [integration 26aafff] Bugfix 1

echo 'feat 2' > feature2
git add feature2
git commit -m "Feature 2"
# [integration 9fb582c] Feature 2

echo 'feature 1' > feature1
git add feature1
git commit --fixup=4f73e16
# [integration 3ae7dd2] fixup! Feature 1

echo 'feature 2' > feature2
git add feature2
git commit --fixup=9fb582c
# [integration 1b6577f] fixup! Feature 2

echo 'feature 3' > feature3
git add feature3
git commit -m "Feature 3 depends on Feature 2"
# [integration edc0fae] Feature 3 depends on Feature 2

echo 'feature 2 for real' > feature2
git add feature2
git commit --fixup=9fb582c
# [integration 77c318b] fixup! Feature 2

echo 'feature 3 for real' > feature3
git add feature3
git commit --fixup=edc0fae
# [integration 46acd6d] fixup! Feature 3 depends on Feature 2

echo 'feature 1 improved' > feature1
git add feature1
git commit -m "Feature 1 improvement"
# [integration cee864f] Feature 1 improvement
git log --graph --oneline
# * cee864f (HEAD -> integration) Feature 1 improvement
# * 46acd6d fixup! Feature 3 depends on Feature 2
# * 77c318b fixup! Feature 2
# * edc0fae Feature 3 depends on Feature 2
# * 1b6577f fixup! Feature 2
# * 3ae7dd2 fixup! Feature 1
# * 9fb582c Feature 2
# * 26aafff Bugfix 1
# * 4f73e16 Feature 1
# * 4010570 (origin/main, main) Let's pretend some work happened upstream already
# * 3657733 Root commit
git rebase -i --autosquash --rebase-merges --update-refs
label onto

reset onto
pick 4f73e16 Feature 1
fixup 3ae7dd2 fixup! Feature 1
pick 26aafff Bugfix 1
pick 9fb582c Feature 2
fixup 1b6577f fixup! Feature 2
fixup 77c318b fixup! Feature 2
pick edc0fae Feature 3 depends on Feature 2
fixup 46acd6d fixup! Feature 3 depends on Feature 2
pick cee864f Feature 1 improvement
reset fork-point # where fork-point is a previously-defined label

# picks, rewords, fixups and squashes
# pick    just puts the commit in that branch
# reword  picks the commit and opens the editor to edit the message
# fixup   amends the last commit
# squash  amends the last commit, concatenates both messages and opens the editor

update-ref refs/heads/<branch-name> # update (or create!) a branch <branch-name>
label <label-name>                  # name for use in a later reset or merge in the same rebase
label onto

# Block for the feat/feature1 branch forking from origin/main
reset onto
pick 4f73e16 Feature 1
fixup 3ae7dd2 fixup! Feature 1
pick cee864f Feature 1 improvement
update-ref refs/heads/feat/feature1
label feat1

# Block for the feat/feature2 branch forking from origin/main
reset onto
pick 9fb582c Feature 2
fixup 1b6577f fixup! Feature 2
fixup 77c318b fixup! Feature 2
update-ref refs/heads/feat/feature2
label feat2

# Block for the feat/feature3 branch forking from feat/feature2
reset feat2
pick edc0fae WIP Feature 3 depends on Feature 2
fixup 46acd6d fixup! Feature 3 depends on Feature 2
update-ref refs/heads/feat/feature3
label feat3

# Block for the fix/bugfix1 branch forking from origin/main
reset onto
pick 26aafff WIP Bugfix 1
update-ref refs/heads/fix/bugfix1
label fix1

# Block for the integration branch, merging everything back together
reset onto
merge feat1 feat2 feat3 fix1 # Integration merge commit
# update-ref refs/heads/integration is implicit because it's the current branch
git log --graph --oneline
# *---.   acd3986 (HEAD -> integration) Integration merge commit
# |\ \ \
# | | | * 0a7b18e (fix/bugfix1) Bugfix 1
# | |_|/
# |/| |
# | | * 4b12f39 (feat/feature3) Feature 3 depends on Feature 2
# | | * fb102ce (feat/feature2) Feature 2
# | |/
# |/|
# | * 901b6a9 (feat/feature1) Feature 1 improvement
# | * efd7697 Feature 1
# |/
# * 4010570 (origin/main, main) Let's pretend some work happened upstream already
# * 3657733 Root commit
git push origin feat/feature1 feat/feature2 feat/feature3 fix/bugfix1
# To git-remote
#  * [new branch]      feat/feature1 -> feat/feature1
#  * [new branch]      feat/feature2 -> feat/feature2
#  * [new branch]      feat/feature3 -> feat/feature3
#  * [new branch]      fix/bugfix1 -> fix/bugfix1
# Bugfix 1 was merged upstream, some other work happened
pushd ../git-coworker
git pull
echo 'Feature 4' > feature4
git add feature4
git commit -m "Your teammates write and merge code too"
git cherry-pick origin/fix/bugfix1
git push
popd
sed 's/improved/enhanced/' -i feature1
git add feature1
git commit --fixup=901b6a9
# [integration 507b321] fixup! Feature 1 improvement

echo 'Extra button!' >> feature2
git add feature2
git commit -m "Add button for Feature 2"
# [integration 7cb7e39] Add button for Feature 2
git fetch
# From git-remote
#    4010570..0c0b483  main       -> origin/main
git rebase -i --autosquash --rebase-merges --update-refs
# warning: skipped previously applied commit 0a7b18e
label onto

# Branch feat-feature1
reset onto
pick efd7697 Feature 1
pick 901b6a9 Feature 1 improvement
fixup 507b321 fixup! Feature 1 improvement
update-ref refs/heads/feat/feature1

label feat-feature1

# Branch feat-feature3
reset onto
pick fb102ce Feature 2
update-ref refs/heads/feat/feature2

pick 4b12f39 Feature 3 depends on Feature 2
update-ref refs/heads/feat/feature3

label feat-feature3

# Branch Integration-merge-commit
reset onto
label Integration-merge-commit

reset onto
merge -C acd3986 feat-feature1 feat-feature3 Integration-merge-commit # Integration merge commit
pick 7cb7e39 Add button for Feature 2
label onto

# Branch feat-feature1
reset onto
pick efd7697 Feature 1
pick 901b6a9 Feature 1 improvement
fixup 507b321 fixup! Feature 1 improvement
update-ref refs/heads/feat/feature1

label feat-feature1

# Branch feat-feature3
reset onto
pick fb102ce Feature 2
pick 7cb7e39 Add button for Feature 2
update-ref refs/heads/feat/feature2

pick 4b12f39 Feature 3 depends on Feature 2
update-ref refs/heads/feat/feature3

label feat-feature3

# Branch Integration-merge-commit
reset onto
label Integration-merge-commit

reset onto
merge -C acd3986 feat-feature1 feat-feature3 Integration-merge-commit # Integration merge commit
git log --graph --oneline
# *-.   63861c2 (HEAD -> integration) Integration merge commit
# |\ \
# | | * ab3ac3e (feat/feature3) Feature 3 depends on Feature 2
# | | * 3acce21 (feat/feature2) Add button for Feature 2
# | | * 23b7df9 Feature 2
# | |/
# |/|
# | * f3046c5 (feat/feature1) Feature 1 improvement
# | * 7e93e38 Feature 1
# |/
# * 0c0b483 (origin/main) Bugfix 1
# * 57c50bf Your teammates write and merge code too
# * 4010570 (main) Let's pretend some work happened upstream already
# * 3657733 Root commit
git push origin feat/feature1 feat/feature2 feat/feature3 --force-with-lease --force-if-includes
git log --graph --oneline
# * 9c28203 (HEAD -> integration) WIP Feature6
# *---.   f75840e Integration merge commit
# |\ \ \
# | | | * ab3ac3e (feat/feature3) Feature 3 depends on Feature 2
# | | | * 3acce21 (feat/feature2) Add button for Feature 2
# | | | * 23b7df9 Feature 2
# | |_|/
# |/| |
# | | * f3046c5 (feat/feature1) Feature 1 improvement
# | | * 7e93e38 Feature 1
# | |/
# |/|
# | * 0332875 (local-do-not-push) My local editor config
# |/
# * 0c0b483 (origin/main) Bugfix 1
# * 57c50bf Your teammates write and merge code too
# * 4010570 (main) Let's pretend some work happened upstream already
# * 3657733 Root commit
git log --oneline
# 9c28203 (HEAD -> integration) WIP Feature6
# f75840e Integration merge commit
# 0c0b483 (origin/main) Bugfix 1
# 0332875 (local-do-not-push) My local editor config
# f3046c5 (feat/feature1) Feature 1 improvement
# ab3ac3e (feat/feature3) Feature 3 depends on Feature 2
# 57c50bf Your teammates write and merge code too
# 7e93e38 Feature 1
# 3acce21 (feat/feature2) Add button for Feature 2
# 4010570 (main) Let's pretend some work happened upstream already
# 23b7df9 Feature 2
# 3657733 Root commit
git log --topo-order --oneline
# 9c28203 (HEAD -> integration) WIP Feature6
# f75840e Integration merge commit
# ab3ac3e (feat/feature3) Feature 3 depends on Feature 2
# 3acce21 (feat/feature2) Add button for Feature 2
# 23b7df9 Feature 2
# f3046c5 (feat/feature1) Feature 1 improvement
# 7e93e38 Feature 1
# 0332875 (local-do-not-push) My local editor config
# 0c0b483 (origin/main) Bugfix 1
# 57c50bf Your teammates write and merge code too
# 4010570 (main) Let's pretend some work happened upstream already
# 3657733 Root commit
git config --global rebase.rebasemerges true
git config --global rebase.updaterefs true
git config --global rebase.autoSquash true
t onto
u refs/heads/<branch_name>
l <label>
m <label>

About KDAB

NicolasGuichard

Nicolas Guichard

Software Engineer

Sign up for the KDAB Newsletter

Need help with performance issues?

Get in touch