Module 04Version Control~1 hour

Collaborating with Git

Learn to work with branches, remotes, and pull requests — the everyday workflow of every professional software team.

Covers:MLO3MLO4MLO7
37

Why branches?

Everything committed so far has been on a single line of history called main. This works for solo projects, but consider a team scenario:

  • Alice is building the login system
  • Bob is fixing a security bug in the API
  • Charlie is experimenting with a new caching layer

If they all commit to main simultaneously, their changes mix together. Alice cannot test her login system without also getting Bob's half-finished security fix and Charlie's experimental cache. When something breaks, it is impossible to tell whose change caused it.

Branches solve this by giving each person (or each feature) a separate line of history. Alice's login work exists independently from Bob's bug fix. When each is finished and tested, it can be merged into main.

38

Creating and switching branches

bash — branch operations
# List all local branches (* = current)
git branch
* main

# Create a new branch
git branch feature/login

# Switch to it
git switch feature/login
Switched to branch 'feature/login'

# Create and switch in one step (preferred)
git switch -c feature/login

# Rename the current branch
git branch -m feature/user-login

# Delete a branch (must not be current branch)
git branch -d feature/login
   # safe delete (only if merged)
git branch -D feature/login
   # force delete

After creating a branch, any commits you make are on that branch. The main branch is unaffected until you merge.

bash — visualising branches
main:          A --- B --- C
                           \
feature/login:              D --- E

# HEAD points to the current commit on the current branch
git log --oneline --graph --all
* f4e5d6c (HEAD -> feature/login) Add login form
* a1b2c3d (main) Initial commit
39

Merging strategies

When a feature branch is ready, you merge it back into the target branch (usually main). Git uses different strategies depending on the history:

Fast-forward merge

If main has not moved since the branch was created, Git can simply move the main pointer forward. The history stays linear.

bash — fast-forward merge
git switch main
git merge feature/login
Updating a1b2c3d..f4e5d6c
Fast-forward
 login.py | 42 ++++

Three-way merge

If both branches have new commits, Git creates a merge commit that has two parents. The history records that both lines were combined.

bash — three-way merge
git merge feature/login
Merge made by the 'ort' strategy.
 login.py | 42 ++++
40

Remote repositories in depth

A remote repository is a copy of your repository hosted elsewhere — usually GitHub. The default remote is named origin by convention.

bash — working with remotes
# Clone an existing repository
git clone https://github.com/user/repo.git

# See configured remotes
git remote -v
origin  https://github.com/user/repo.git (fetch)
origin  https://github.com/user/repo.git (push)

# Download changes without merging (safe)
git fetch origin

# Download and merge in one step
git pull

# Push your current branch to origin
git push origin feature/login

# Push and set tracking (first push of a new branch)
git push -u origin feature/login
ℹ fetch vs pull

git fetch downloads changes from the remote but does NOT modify your local branches. It is always safe. git pull fetches and then immediately merges. Prefer fetch followed by git merge origin/main so you can inspect what is coming before merging.

41

The pull request workflow

A pull request (PR) is a GitHub feature (not a Git concept) that wraps a branch merge in a review process. It is the standard collaborative workflow in most professional teams.

  1. Create a branch for your work: git switch -c feature/add-search
  2. Make commits on the branch as you work.
  3. Push the branch to GitHub: git push -u origin feature/add-search
  4. Open a PR on GitHub. Write a clear description: what does this change do? Why? Any testing notes?
  5. Review happens — teammates comment on the code, suggest changes, ask questions. You can push more commits to the branch in response.
  6. CI checks run automatically — the pipeline you will build in Module 6 triggers and reports pass/fail.
  7. Approval and merge — once approved and all checks pass, the PR is merged. The branch can then be deleted.
◆ Good PR descriptions

A good pull request description answers three questions:

  • What — what exactly does this change?
  • Why — what problem does it solve? Link to any relevant issue or ticket.
  • How — any notes on implementation decisions, trade-offs, or things reviewers should pay attention to.

A bad description is: 'fix bug'. A good description is: 'Fix race condition in cache invalidation that caused stale data to appear for up to 30 seconds after profile updates. Reproduced locally, added regression test in test_cache.py'.

42

Merge conflicts step by step

A merge conflict occurs when two branches have changed the same part of the same file in incompatible ways. Git cannot decide which version to keep, so it puts both options into the file and asks you to choose.

bash — creating a conflict scenario
# On main: someone changes a function
# main: app.py line 15: return user.name.upper()

# On feature/login: you change the same line
# feature: app.py line 15: return user.name.strip()

# Merging triggers a conflict
git merge feature/login
CONFLICT (content): Merge conflict in app.py
Automatic merge failed; fix conflicts and then commit.
plain — what a conflict looks like
<<<<<<< HEAD
    return user.name.upper()
=======
    return user.name.strip()
>>>>>>> feature/login
  1. Open the conflicted file in your editor.
  2. Understand both sidesHEAD is the current branch (main), the other marker is the incoming branch.
  3. Edit the file to the correct final state — delete all four marker lines and keep what you want.
  4. Stage the file: git add app.py
  5. Complete the merge: git commit (Git pre-fills a merge commit message)
⚠ Conflicts require thought

Do not just pick one side or the other automatically. Sometimes the correct resolution combines both changes:

python
    return user.name.strip().upper()  # both: strip whitespace AND uppercase

Read the context carefully. If you are unsure what the correct result should be, talk to the author of the conflicting change.

43

Branch strategies in teams

Teams adopt conventions for how branches are named and how work flows. Two common models:

Feature branch workflow

Every piece of work gets its own branch. All work flows through PRs into main. Main is always deployable. This is the most common model for teams of any size.

plain — feature branch naming
feature/user-login
feature/JIRA-1234-search-api
fix/login-null-check
hotfix/critical-security-patch
chore/update-dependencies

Trunk-based development

Developers commit directly to main (the trunk) very frequently — multiple times per day. Features are hidden behind feature flags until ready. This requires strong CI and a culture of very small commits. Used by high-frequency deployment organisations like Google.

44

git stash for context switching

You are halfway through a feature when an urgent bug fix arrives. You cannot commit half-finished work. git stash saves your changes temporarily so you can switch context.

bash — git stash
# Save all uncommitted changes
git stash

# Save with a descriptive name
git stash push -m "half-built login form"

# List stashed changes
git stash list
stash@{0}: On feature/login: half-built login form
stash@{1}: WIP on main: ...

# Restore the most recent stash
git stash pop

# Apply without removing from the list
git stash apply

# Apply a specific stash
git stash apply stash@{1}

# Discard a stash
git stash drop
45

Key terms

branch
A separate line of development within a repository.
merge
Combining the history of one branch into another.
fast-forward
A merge where the target branch just moves its pointer forward — no merge commit created.
merge commit
A commit with two parents, created when two diverged branches are combined.
remote
A copy of the repository hosted elsewhere. Default is called origin.
origin
The conventional name for the remote repository, typically on GitHub.
git fetch
Download changes from the remote without merging. Safe to run anytime.
git pull
Fetch plus merge. Equivalent to git fetch followed by git merge.
pull request
A GitHub feature for proposing, reviewing, and merging a branch.
merge conflict
Two branches changed the same part of a file; manual resolution required.
git stash
Temporarily save uncommitted changes so you can switch context.
tracking branch
A local branch linked to a remote branch. Set with git push -u.
46

Exercises

✎ Lab exercises — approximately 50 minutes

Part A: Branching

  1. Fork the provided starter repository on GitHub and clone it locally.
  2. Create a branch called feature/your-name and switch to it.
  3. Make two commits on this branch — add a new function and a docstring.
  4. Use git log --oneline --graph --all to visualise the branch structure.
  5. Switch back to main. Notice that your changes are not there.

Part B: Remotes and pull requests

  1. Push your branch to GitHub with git push -u origin feature/your-name.
  2. Go to GitHub and open a pull request with a clear description.
  3. Ask a classmate to leave a code review comment.
  4. Make one more commit in response to the comment and push it.
  5. The reviewer approves. Merge the PR on GitHub.
  6. Back locally, switch to main and run git pull to get the merged changes.
  7. Delete your feature branch: git branch -d feature/your-name.

Part C: Conflict resolution

  1. You and your tutor will both be given the same file to edit. Both of you change line 5 to different things.
  2. Your tutor pushes first. When you try to push, Git will reject it.
  3. Fetch the changes, then merge. Resolve the conflict thoughtfully (read both sides).
  4. Stage and complete the merge commit.
  5. Push the resolved version.

Part D: git stash

  1. Start editing a file but do not commit.
  2. Use git stash to save your changes. Verify with git status that the working directory is clean.
  3. Make and commit a different change. Then use git stash pop to restore your original changes.
  4. Verify both changes are now present.