Git for Collaboration

Branches, Merging, and Pull Requests

Will Chapman

CU Boulder ATOC

2026-01-01

The Collaboration Problem

Last Time: 5 Commands to Survive

You already know these:

# Command What it does
1 git clone Download a project
2 git status What changed? (always safe)
3 git add Stage files to save
4 git commit Save a snapshot
5 git push / git pull Upload / Download

Today we level up. Same repo, new superpowers.

The Problem

Solo work is easy:

You ─── edit ─── commit ─── push

One person, one copy, no conflicts. Life is good.

But science is collaborative:

  • You and a labmate share analysis code
  • Two people edit the same file
  • Someone pushes while you’re still working
  • Things break.

Today’s question: How do two people work on the same code without breaking everything?

Meet Alice & Bob

Alice and Bob are ATOC students working on a weather analysis project together.

  • They share a GitHub repository
  • Alice is writing temperature conversions
  • Bob is writing wind chill calculations
  • They need to work at the same time without stepping on each other’s toes

The disaster scenario: Both edit collaborative_analysis.py on main, both push… 💥

The Disaster, Visualized

Alice:  clone → edit line 10 → commit → push ✅ (she pushed first)

Bob:    clone → edit line 10 → commit → push ❌ REJECTED!
        "Updates were rejected because the remote contains work
         that you do not have locally."

Bob has to git pull, and now Git tries to smash their changes together. If they edited the same lines, Git doesn’t know whose version to keep.

Result: A merge conflict. Confusion. Panic.

There’s a better way. Today you’ll learn it.

Why Not Just Use Dropbox?

Feature Git Dropbox/OneDrive
Track who changed what Yes, line-by-line No
Meaningful change messages Yes, commit messages No
Work on multiple features simultaneously Yes, branches No
Merge conflicting changes Yes, smart merge Creates duplicates
Collaborate with strangers Yes, pull requests Hard
Review before accepting changes Yes, code review No
Undo specific changes Yes, revert commits Version history only

Git is not just version control – it’s a project management system.

Dropbox backs up files. Git tracks what changed, who changed it, when, and why. Today’s tools – branches, merging, and pull requests – are what make that possible.

Today’s New Toolkit

Tool What it solves
git log / git diff See what happened and what changed
Branches Work in parallel without collisions
Merging Combine parallel work back together
Merge conflicts Handle the case where both people edited the same line
Pull requests Propose and review changes before merging
.gitignore Keep junk out of your repo

Same mantra as before: Status. Add. Commit. Status. Just with more tools.

Review & New Safe Commands

Two New Read-Only Commands

Just like git status, these are 100% safe – they never change anything.

git log – See your history

git log --oneline
a3f1d2e Complete wind analysis
b7c9e4a Add my name
1f2a3b4 Initial commit

Each line = one commit. Most recent on top.

git diff – See what changed

git diff
- STUDENT_NAME = ""
+ STUDENT_NAME = "Alice"

Shows exactly which lines you added (+) or removed (-).

Pro tip: Run git diff before git add to review your changes. Catches mistakes early.

git log Options

# Short version (one line per commit)
git log --oneline

# See which files changed in each commit
git log --oneline --stat

# See the last 5 commits
git log --oneline -5

# See a visual branch graph (more on this soon!)
git log --oneline --graph --all

When is this useful?

  • “What did I do yesterday?”
  • “What did my partner change?”
  • “When did this bug get introduced?”

Practice Checkpoint: Log & Diff

In your atoc4815-git-practice repo:

  1. Run git log --oneline – see your past commits from last session
  2. Open any file and make a small change (add a comment)
  3. Run git diff – see your change highlighted
  4. Undo the change (delete the comment) – we’re just exploring

These are your detective tools. Use them whenever you need to understand what happened.

Branches: Parallel Universes

Why Branches?

Without branches:

Alice and Bob both edit main. Every push risks a conflict. They have to take turns.

main: Alice → Bob → Alice → Bob
      (slow, fragile)

With branches:

Alice and Bob each get their own parallel copy. They work independently, then combine when ready.

main:       ─────────────────────
Alice:        ╲── work ── work ──╱
Bob:            ╲── work ──╱

Branches = parallel universes for your code. Changes in one branch don’t affect any other branch until you merge.

What Is a Branch?

A branch is just a named pointer to a commit. That’s it.

main:           A ── B ── C        ← main points here
                          │
alice-temp:               ╰── D    ← alice-temp points here
  • main is the default branch (the “real” version)
  • When Alice creates alice-temp, it starts as a copy of main
  • Her new commits go on alice-temp only
  • main stays untouched until she merges

Think of it like: making a copy of a Google Doc to try out edits, then pasting the good parts back into the original.

Creating a Branch

# See what branch you're on
git branch

# Create a new branch AND switch to it
git checkout -b alice-temperature

# Or using the newer command (same thing)
git switch -c alice-temperature
$ git branch
  main
* alice-temperature    ← you are here

The * shows your current branch.

Naming convention: Use descriptive names with your name or the feature:

  • alice-temperature, bob-windchill
  • fix-unit-conversion, add-heat-index

Working on a Branch

Once you’re on a branch, everything works the same. Status-Add-Commit-Status.

# You're on alice-temperature
git status
# ... edit collaborative_analysis.py ...
git add collaborative_analysis.py
git commit -m "Implement fahrenheit_to_celsius"
git status

Your commits only exist on this branch. If you switch back to main, the file looks like it did before.

git checkout main        # switch to main
# collaborative_analysis.py has the OLD version!

git checkout alice-temperature  # switch back
# collaborative_analysis.py has YOUR version!

Pushing a Branch to GitHub

The first time you push a new branch:

git push -u origin alice-temperature

What does -u origin alice-temperature mean?

  • origin = your GitHub fork (set up when you cloned)
  • alice-temperature = the branch name on GitHub
  • -u = “remember this connection” (next time, just git push)

After the first push, you can just use git push as usual.

Switching Between Branches

# See all branches
git branch

# Switch to an existing branch
git checkout main
git checkout alice-temperature

# Or with the newer command
git switch main
git switch alice-temperature

Important rule: Commit or stash your changes before switching branches!

# This will warn you:
git checkout main
# error: Your local changes would be overwritten...

# Fix: commit first, then switch
git add .
git commit -m "Save work in progress"
git checkout main    # now it works

The Branch Diagram

Here’s what Alice and Bob’s repo looks like with branches:

gitGraph
    commit id: "Initial commit"
    commit id: "Add practice files"
    branch alice-temperature
    checkout alice-temperature
    commit id: "Add F-to-C conversion"
    commit id: "Add C-to-F conversion"
    checkout main
    branch bob-windchill
    commit id: "Add wind chill formula"
    commit id: "Add heat index"
    checkout main

main is untouched. Alice and Bob work in peace. When they’re ready, they’ll merge back.

Practice Checkpoint: Create a Branch

In your atoc4815-git-practice repo:

  1. Make sure you’re on main: git branch (look for the *)
  2. Create and switch to a new branch: git checkout -b my-feature
  3. Verify: git branch – you should see * my-feature
  4. Open collaborative_analysis.py and add a comment at the top: # Edited by [your name]
  5. Save, then: git add collaborative_analysis.py
  6. Commit: git commit -m "Add my name to collaborative analysis"
  7. Switch back to main: git checkout main
  8. Open the file again – your comment is gone! (It’s safe on your branch.)
  9. Switch back: git checkout my-feature – the comment is back!

Merging: Bringing It Together

What Is Merging?

Merging = combining the work from one branch into another.

Before merge:
main:           A ── B
                      ╲
my-feature:            C ── D

After merge:
main:           A ── B ──────── E  (merge commit)
                      ╲        ╱
my-feature:            C ── D

The merge commit E has the combined work from both branches.

How to Merge

Step 1: Switch to the branch you want to merge INTO (usually main):

git checkout main

Step 2: Merge the other branch:

git merge my-feature

That’s it. Git combines the changes automatically (when possible).

Updating b7c9e4a..d3f1a2e
Fast-forward
 collaborative_analysis.py | 5 +++++
 1 file changed, 5 insertions(+)

After merging, you can delete the branch if you’re done with it:

git branch -d my-feature

Fast-Forward vs Merge Commit

Fast-forward (simple case):

No one else committed to main while you were on your branch. Git just moves the pointer forward.

Before:
main:    A ── B
my-feat:      ╰── C ── D

After:
main:    A ── B ── C ── D

Merge commit (parallel work):

Both branches have new commits. Git creates a new commit that combines them.

Before:
main:    A ── B ── X
my-feat:      ╰── C ── D

After:
main:    A ── B ── X ── M
              ╰── C ── D ╱

You don’t need to worry about which one happens. Git picks the right strategy automatically.

The Merge in a Diagram

gitGraph
    commit id: "Initial commit"
    commit id: "Add practice files"
    branch alice-temperature
    checkout alice-temperature
    commit id: "Add F-to-C"
    commit id: "Add C-to-F"
    checkout main
    merge alice-temperature id: "Merge Alice's work"
    commit id: "main continues..."

Alice’s work is now part of main. Clean and traceable.

Practice Checkpoint: Merge Your Branch

Continue from the previous checkpoint:

  1. Make sure you’re on my-feature: git branch
  2. If you don’t have a commit there yet, make one now
  3. Switch to main: git checkout main
  4. Merge: git merge my-feature
  5. Check the file – your changes from the branch are now on main!
  6. Run git log --oneline – see the merge
  7. Clean up: git branch -d my-feature

Congratulations – you just did a real Git merge!

Merge Conflicts: The Scary Part

When Do Conflicts Happen?

Merge conflicts happen when two branches change the same line in the same file.

Alice (on alice-temp):
    def fahrenheit_to_celsius(temp_f):
        return (temp_f - 32) * 5/9       ← changed line 10

Bob (on bob-windchill):
    def fahrenheit_to_celsius(temp_f):
        return (temp_f - 32) * (5/9)     ← also changed line 10!

Git doesn’t know which version to keep. So it asks you to decide.

This is not an error. It’s Git being honest: “I need a human decision here.”

What a Conflict Looks Like

When you try to merge and there’s a conflict, Git puts markers in the file:

def fahrenheit_to_celsius(temp_f):
<<<<<<< HEAD
    return (temp_f - 32) * 5/9
=======
    return (temp_f - 32) * (5/9)
>>>>>>> bob-windchill

Reading the markers:

Marker Meaning
<<<<<<< HEAD Start of YOUR version (the branch you’re on)
======= Divider between the two versions
>>>>>>> bob-windchill End of THEIR version (the branch you’re merging in)

How to Resolve: Step by Step

Step 1: Open the file and find the <<<<<<< markers

Step 2: Decide which version to keep (or combine them)

Step 3: Delete the markers and leave only the code you want:

# Before (with markers):
<<<<<<< HEAD
    return (temp_f - 32) * 5/9
=======
    return (temp_f - 32) * (5/9)
>>>>>>> bob-windchill

# After (resolved -- you chose one version):
    return (temp_f - 32) * 5 / 9

Step 4: Stage and commit the resolved file:

git add collaborative_analysis.py
git commit -m "Resolve merge conflict in fahrenheit_to_celsius"

Conflict Resolution Tips

Do:

  • Read both versions carefully
  • Understand what each person intended
  • Test the code after resolving
  • Ask your partner if you’re unsure
  • Use git status to see which files have conflicts

Don’t:

  • Panic (it’s normal and fixable)
  • Delete the whole file
  • Just pick one version without reading
  • Leave the <<<<<<< markers in the file
  • Forget to git add after resolving

VS Code tip: VS Code highlights conflicts and gives you buttons: “Accept Current”, “Accept Incoming”, “Accept Both”. Super handy!

Avoiding Conflicts

The best merge conflict is one that never happens:

  1. Use branches – work on separate features
  2. Divide work clearly – “I’ll do Section A, you do Section B”
  3. Pull oftengit pull before starting work each day
  4. Communicate – tell your partner what you’re working on
  5. Keep commits small – easier to merge small changes

Alice and Bob’s strategy: Alice works on temperature functions (Section A), Bob works on wind chill functions (Section B). Different functions, different lines, no conflicts!

Practice Checkpoint: Create & Resolve a Conflict

This is intentional – you’re going to cause a conflict on purpose:

  1. Make sure you’re on main and everything is committed
  2. Create a branch: git checkout -b conflict-test
  3. In collaborative_analysis.py, change the first docstring line to: """Collaborative Weather Analysis -- edited on conflict-test"""
  4. git add . then git commit -m "Edit docstring on branch"
  5. Switch to main: git checkout main
  6. Change that same line to: """Collaborative Weather Analysis -- edited on main"""
  7. git add . then git commit -m "Edit docstring on main"
  8. Now merge: git merge conflict-test
  9. CONFLICT! Open the file, resolve the markers, pick whichever version you prefer
  10. git add . then git commit -m "Resolve conflict"

You just survived your first merge conflict! Clean up: git branch -d conflict-test

Pull Requests: Proposing Changes

What Is a Pull Request?

A pull request (PR) is a way to say:

“Hey, I made changes on my branch. Can you review them and merge them into main?”

Why not just merge directly?

  • PRs let someone review your code before it goes into main
  • They create a record of what changed and why
  • They’re how open-source projects and research labs manage contributions
  • In this class: a PR is how you “turn in” collaborative work

Think of it like: submitting a draft for peer review before it gets published.

Creating a Pull Request: Step by Step

Step 1: Push your branch to GitHub

git push -u origin alice-temperature

Step 2: Go to your repo on GitHub. You’ll see a yellow banner:

 alice-temperature had recent pushes — [Compare & pull request]

Click Compare & pull request. This opens GitHub’s Compare view – it shows you exactly what’s different between your branch and main before you create the PR.

Step 3: Fill in the PR form:

  • Title: Short description (“Add temperature conversion functions”)
  • Description: What you changed and why
  • Reviewers: Add your partner (if working together)

Click Create pull request.

Writing a Good PR Description

Good PR description:

## What I did
- Implemented fahrenheit_to_celsius()
- Implemented celsius_to_fahrenheit()
- Implemented daily_temp_range()

## How to test
Run: python collaborative_analysis.py
Temperature section should show correct
conversions.

## Notes
Used the standard F = C*9/5 + 32 formula.

Bad PR description:

done

or

(empty)

Rule of thumb: Future-you should be able to read this PR in 6 months and understand what happened.

Reviewing a Pull Request

When your partner creates a PR, you can review it on GitHub:

  1. Go to the Pull requests tab in the repo
  2. Click on the PR
  3. Click the Files changed tab to see what they modified – this is GitHub Compare in action
  4. Green lines = additions, Red lines = deletions
  5. You can leave comments on specific lines (click the + icon)
  6. When you’re satisfied, click Review changesApprove

Tip: You can compare any two branches anytime at github.com/you/repo/compare. You don’t need a PR – it’s a great way to preview what a merge would look like.

Good review comments:

  • “This looks correct!”
  • “Should this handle negative temperatures?”
  • “Nice work on the docstrings.”

Not helpful: “Looks good” (with no actual review)

Merging a Pull Request

Once the PR is approved:

  1. Click the green Merge pull request button on GitHub
  2. Click Confirm merge
  3. Optionally, click Delete branch to clean up

Back on your local machine, pull the merged changes:

git checkout main
git pull

Now your local main has the merged code!

The full cycle:

Branch → Commit → Push → PR → Review → Merge → Pull

Practice Checkpoint: Create a Pull Request

Let’s do a real PR:

  1. Create a new branch: git checkout -b add-my-section
  2. Edit collaborative_analysis.py – implement one function (e.g., fahrenheit_to_celsius)
  3. Commit: git add . then git commit -m "Implement fahrenheit_to_celsius"
  4. Push: git push -u origin add-my-section
  5. Go to your fork on GitHub
  6. Click Compare & pull request
  7. Write a title and description, then click Create pull request
  8. Look at the Files changed tab – that’s what a reviewer sees!

Don’t merge it yet! We’ll use this PR in the lab exercise.

.gitignore: Keeping Repos Clean

What Should NOT Be in Git?

Some files don’t belong in a repository:

Don’t track:

  • Large data files (.nc, .csv, .hdf5)
  • Compiled Python files (__pycache__/)
  • OS junk (.DS_Store, Thumbs.db)
  • Editor configs (.vscode/, .idea/)
  • Secrets and API keys
  • Virtual environments (venv/, .conda/)

Do track:

  • Source code (.py, .R, .f90)
  • Documentation (README.md)
  • Configuration (.yml, .toml)
  • Small sample data for testing
  • Notebooks (.ipynb, .qmd)
  • Scripts and workflows

Rule of thumb: Track things you wrote. Don’t track things that can be generated or downloaded.

The .gitignore File

Create a file called .gitignore in your repo root:

# Python
__pycache__/
*.pyc
*.pyo

# Data files (too large for Git)
*.nc
*.hdf5
data/raw/

# OS files
.DS_Store
Thumbs.db

# Environments
venv/
.conda/

# Jupyter checkpoints
.ipynb_checkpoints/

Git will ignore any files matching these patterns. They won’t show up in git status and won’t be committed.

Common .gitignore for ATOC Work

Here’s a good starter .gitignore for atmospheric science Python projects:

# Python bytecode
__pycache__/
*.pyc

# Large data -- keep out of Git
*.nc
*.grib
*.grib2
*.hdf5
*.h5

# Jupyter
.ipynb_checkpoints/

# OS & editor
.DS_Store
Thumbs.db
.vscode/
.idea/

# Environments
venv/
*.egg-info/

Tip: GitHub has a collection of templates at github.com/github/gitignore. The Python template is a great starting point.

The Complete Collaboration Workflow

Full Workflow Summary

Pull main → Branch → Edit → Add & Commit → Push → PR → Review → Merge → Pull main
                       ↑                     |
                       └── more changes? ────┘
git checkout main && git pull         # 1. Start fresh
git checkout -b my-feature            # 2. Create branch
# ... edit files ...                  # 3. Make changes
git add . && git commit -m "msg"      # 4. Commit
git push -u origin my-feature         # 5. Push
# Create PR on GitHub                 # 6-8. PR workflow
git checkout main && git pull         # 9. Get merged code

Branch Naming Conventions

Good branch names help your team understand what’s happening:

Pattern Example When to use
name-feature alice-temperature Personal feature work
feature/description feature/add-wind-chill Adding new functionality
fix/description fix/unit-conversion-bug Fixing a bug
hw/number hw/homework-3 Homework assignments

Avoid:

  • main or master (these are reserved)
  • Spaces in branch names (use hyphens: my-feature)
  • Super long names (alice-fixing-the-thing-from-tuesday-that-broke)

Golden Rules of Git Collaboration

  1. Never push directly to main – always use a branch and PR
  2. Pull before you start workinggit checkout main && git pull
  3. One feature per branch – keep branches focused
  4. Commit often – small commits are easier to merge and review
  5. Write descriptive commit messages – future-you will thank you
  6. Communicate with your partner – “I’m working on Section A”
  7. Review PRs carefully – don’t just click approve
  8. Delete merged branches – keep things tidy

The most important rule: git status is always your friend. When in doubt, run it.

Lab Exercise

Collaborative Analysis Lab

Pair up with a partner. You will:

  1. Work on collaborative_analysis.py together
  2. Each complete your assigned section on a branch
  3. Submit pull requests
  4. Review each other’s code
  5. Merge the final result

What to turn in: The URL of your merged pull request

Time: ~30 minutes. Work through the steps on the next slides.

Lab Step 1: Setup

Both partners:

  1. Make sure you have your fork of atoc4815-git-practice cloned
  2. Navigate to it: cd atoc4815-git-practice
  3. Get the latest version:
git checkout main
git pull
  1. Verify collaborative_analysis.py exists: ls
  2. Decide who is Partner A and who is Partner B

Partner A will implement temperature functions (Section A).

Partner B will implement wind chill and heat index (Section B).

Lab Step 2: Create Your Branch

Partner A:

git checkout -b partner-a-temperature

Partner B:

git checkout -b partner-b-windchill

Verify: git branch should show your new branch with a *.

Both partners work at the same time from here on. That’s the whole point of branches!

Lab Step 3: Implement Your Section

Partner A – Section A:

  1. Implement fahrenheit_to_celsius()
  2. Implement celsius_to_fahrenheit()
  3. Implement daily_temp_range()

Hints:

  • C = (F - 32) * 5/9
  • F = C * 9/5 + 32
  • Range dict: {"range_f": ..., "range_c": ...}

Partner B – Section B:

  1. Implement wind_chill()
  2. Implement heat_index()

Hints:

  • Check the docstrings for formulas
  • Return None when inputs are out of range
  • Wind chill: temp_f <= 50 and wind_mph >= 3
  • Heat index: temp_f >= 80

Test your section: python collaborative_analysis.py (the other section will print None – that’s OK!)

Lab Step 4: Commit & Push

Both partners (on your own branch):

git status                          # see your changes
git add collaborative_analysis.py   # stage
git commit -m "Implement [your section] functions"  # commit
git push -u origin [your-branch-name]               # push

Example (Partner A):

git add collaborative_analysis.py
git commit -m "Implement temperature conversion functions"
git push -u origin partner-a-temperature

Verify: Go to your fork on GitHub. You should see your branch listed.

Lab Step 5: Create a Pull Request

Both partners:

  1. Go to your fork on GitHub
  2. You should see a banner for your recently pushed branch
  3. Click Compare & pull request
  4. Set the base branch to main (in YOUR fork)
  5. Write a title: “Add temperature functions” (or “Add wind chill functions”)
  6. Write a description explaining what you implemented
  7. Click Create pull request

Share your PR link with your partner!

Lab Step 6: Review & Merge

Review your partner’s PR:

  1. Open your partner’s PR link (they shared it with you)
  2. Click the Files changed tab
  3. Look at the code – does it make sense?
  4. Leave at least one comment (e.g., “Looks good!” or a question)

Merge the PRs (one at a time):

  1. Partner A merges their PR first (click Merge pull request)
  2. Partner B: pull the updated main, then merge theirs:
git checkout main
git pull
  1. Partner B merges their PR on GitHub

Both partners: Pull the final version:

git checkout main && git pull
python collaborative_analysis.py    # Full report!

Lab: What to Turn In

Submit the URL of one of your merged pull requests.

It will look like:

https://github.com/YOUR_USERNAME/atoc4815-git-practice/pull/1

Grading criteria:

  • Branch was created with a descriptive name ✓
  • Functions are correctly implemented ✓
  • Pull request has a title and description ✓
  • At least one review comment ✓
  • PR was merged into main

Bonus: Resolve a merge conflict if both partners edited overlapping lines!

Wrapping Up

What You Learned Today

Tool What it does
git log View commit history
git diff See what changed before committing
git branch / git checkout -b Create and switch branches
git merge Combine branches
Merge conflicts Resolve when two people edit the same line
Pull requests Propose, review, and merge changes on GitHub
.gitignore Tell Git to skip certain files

Combined with your original 5 commands, you now have the full collaboration toolkit.

Cheat Sheet

Branching:

git branch               # list branches
git checkout -b name     # create + switch
git checkout main        # switch to main
git merge branch-name    # merge into current
git branch -d name       # delete branch

Collaboration:

git log --oneline        # view history
git diff                 # see changes
git push -u origin name  # push new branch
# Create PR on GitHub
# Review → Approve → Merge
git pull                 # get merged code

The workflow: Pull → Branch → Edit → Commit → Push → PR → Review → Merge → Pull

Resources

Questions?

Will Chapman | wchapman@colorado.edu | willychap.github.io