Branching and Rebase Lab¶
Practise the branching and merging workflow end to end: create branches, fast-forward and three-way merges, rebase, interactive squash, conflict resolution, cherry-pick, and cleaning up a branch before review. Run every task in a throwaway repository.
Setup¶
Create a fresh sandbox and one starting commit.
mkdir -p /tmp/git-lab && cd /tmp/git-lab
rm -rf sandbox && git init sandbox && cd sandbox
git config user.name "Amina Yusuf"
git config user.email "amina@example.com"
printf 'name: shop\n' > service.yml
git add service.yml
git commit -m "Add service manifest"
Branches¶
1. Create and Switch¶
Create a branch feature/login from main and switch to it in one command. Confirm you are on it.
Solution
git switch -c feature/login
git branch --show-current # expect: feature/login
2. Commit on the Branch¶
Add a file auth.py with any content and commit it. Then confirm main has not moved.
Solution
printf 'def login(): ...\n' > auth.py
git add auth.py && git commit -m "Add login function"
git log --oneline main -1 # still at the first commit
Merging¶
3. Fast-Forward Merge¶
Switch to main and merge feature/login. Because main has no new commits, this fast-forwards. Confirm the history is linear.
Solution
git switch main
git merge feature/login # prints: Fast-forward
git log --oneline --graph -3
4. Force a Merge Commit¶
Create feature/cart, add a commit, switch to main, and merge with a merge commit even though a fast-forward is possible.
Solution
git switch -c feature/cart
printf 'def add(): ...\n' > cart.py
git add cart.py && git commit -m "Add cart add"
git switch main
git merge --no-ff --no-edit feature/cart
git log --oneline --graph -3 # a two-parent merge commit at the top
Rebasing¶
5. Rebase a Feature onto main¶
Start feature/search from main, add two commits, then add a commit to main so the branches diverge. Rebase the feature onto main and confirm the history is linear with new SHAs.
Solution
git switch -c feature/search
printf 'def find(): ...\n' > search.py
git add search.py && git commit -m "Add search"
printf 'def rank(): ...\n' >> search.py
git commit -am "Add ranking"
git switch main
printf '# Shop\n' > README.md
git add README.md && git commit -m "Document setup"
git switch feature/search
git rebase main # prints: Successfully rebased ...
git log --oneline --graph -4 # one straight line, no merge commit
6. Squash Before a Pull Request¶
On feature/search, squash the two feature commits into one with an interactive rebase.
Solution
git rebase -i HEAD~2
# in the editor: keep the first as 'pick', change the second to 'fixup', save
git log --oneline -2 # the two commits are now one
Conflict Resolution¶
7. Create and Resolve a Conflict¶
Make main and a new branch feature/tls each change the same line of a file, then merge and resolve.
Solution
printf 'port: 80\n' > config.yml
git add config.yml && git commit -m "Set port 80"
git switch -c feature/tls
printf 'port: 443\n' > config.yml
git commit -am "Serve on 443"
git switch main
printf 'port: 8080\n' > config.yml
git commit -am "Serve on 8080"
git merge --no-edit feature/tls # CONFLICT in config.yml
Resolve it, then verify and commit:
git status -s # UU config.yml
printf 'port: 443\n' > config.yml # decide the final value, remove markers
git add config.yml
git diff --check # no leftover markers
git commit --no-edit
8. Abort Instead of Resolving¶
Trigger the same conflict again on a fresh branch, then abort the merge and confirm the tree is clean.
Solution
git switch -c feature/tls2 HEAD~1
printf 'port: 9090\n' > config.yml
git commit -am "Serve on 9090"
git switch main
git merge --no-edit feature/tls2 # CONFLICT
git merge --abort
git status # clean, back at the pre-merge commit
Cherry-Pick¶
9. Backport One Commit¶
On a branch develop, make two commits. Cherry-pick only the second onto main, recording the source.
Solution
git switch -c develop
printf 'f1\n' > feature.py && git add feature.py && git commit -m "Add feature one"
printf 'patch\n' > hotfix.py && git add hotfix.py && git commit -m "Fix parser"
git switch main
git cherry-pick -x develop # copies only the tip commit
git log -1 --format=%b # shows (cherry picked from commit ...)
Preparing for Review¶
10. Squash a Messy Branch into One Commit¶
On a new branch feature/wishlist, make three noisy commits (wip, more, fix typo), then squash them into one atomic, conventionally-typed commit with an interactive rebase.
Solution
git switch main
git switch -c feature/wishlist
printf 'wish\n' > wishlist.py && git add wishlist.py && git commit -m "wip"
printf 'wish2\n' >> wishlist.py && git commit -am "more"
printf 'wish3\n' >> wishlist.py && git commit -am "fix typo"
git rebase -i main
# keep the first as 'pick', mark the other two 'fixup', save
git commit --amend -m "feat(wishlist): add wishlist"
git log --oneline main..feature/wishlist # one commit
git diff --stat main...feature/wishlist # net change unchanged
11. Autosquash a Fixup¶
Add a commit, then a fixup targeting it, and let --autosquash fold the fixup in automatically.
Solution
printf 'price\n' > price.py && git add price.py && git commit -m "feat(wishlist): add pricing"
printf 'price2\n' >> price.py && git add price.py
git commit --fixup=HEAD # creates "fixup! feat(wishlist): add pricing"
git rebase -i --autosquash main # fixup is pre-placed under its target; save
git log --oneline main..feature/wishlist
Cleanup¶
cd /tmp && rm -rf /tmp/git-lab