Today’s question: How do two people work on the same code without breaking everything?
Catherine & Heathcliff
Catherine and Heathcliff are ATOC students working on a weather analysis project together.
They share a GitHub repository
Catherine is writing temperature conversions
Heathcliff 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… 💥
What Actually Goes Wrong
Catherine: clone → edit line 10 → commit → push ✅ (she pushed first)
Heathcliff: clone → edit line 10 → commit → push ❌ REJECTED!
"Updates were rejected because the remote contains work
that you do not have locally."
Heathcliff 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.
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
Review & New Commands
Two New Read-Only Commands
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 (-).
hit (q) to exit the window
Pro tip: Run git diffbeforegit add to review your changes. Catches mistakes early.
git diff Options
git diff is more powerful than it looks — you can compare almost anything:
One file only:
git diff analysis.py # changes to one file
Staged changes (after git add, before git commit):
git diff --staged# what's about to be committedgit diff --staged analysis.py # staged changes in one file only
Between two commits:
git diff b7c9e4a a3f1d2e # what changed between these two commitsgit diff b7c9e4a a3f1d2e -- analysis.py # ...but just this one file
Between two branches:
git diff main catherine-temperature # all differencesgit diff main catherine-temperature -- analysis.py # one file only
The pattern:git diff [thing1] [thing2] [-- optional/file/path]
Omit thing1 and thing2 → compares working directory to last commit.
git log Options
# Short version (one line per commit)git log --oneline# See which files changed in each commitgit log --oneline--stat# See the last 5 commitsgit 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 repo:
Run git log --oneline – see your past commits from last session
Open any file and make a small change (add a comment)
Run git diff – see your change highlighted
Undo the change (delete the comment)
These are your version tools. Use them whenever you need to understand what happened.
Using Your History: Rewinding
Those hashes in git log aren’t just for reading. You can use them to travel back in time.
Scenario 1: “What did that commit actually change?”
git log --oneline# a3f1d2e Tweak wind chill formula ← that one looks suspicious# b7c9e4a Add heat indexgit show a3f1d2e # see the full diff for that commit
Scenario 2: “I broke a file — bring back the old version”
git log --oneline# a3f1d2e (current, broken)# b7c9e4a (this was working)git checkout b7c9e4a -- analysis.py # restore just that one filegit status # analysis.py is now stagedgit commit -m"Restore analysis.py to working version"
This is surgical — only restores one file, leaves everything else alone.
Undoing a Bad Commit: git revert
The safest way to undo a commit that’s already been pushed:
git log --oneline# a3f1d2e Add broken analysis ← oops# b7c9e4a Add heat index# 1f2a3b4 Initial commitgit revert a3f1d2e# Git creates a NEW commit that undoes that commit's changes.# Your editor opens for a commit message — just save and close it.
git log --oneline# c9d4e5f Revert "Add broken analysis" ← new undo commit# a3f1d2e Add broken analysis ← original still there# b7c9e4a Add heat index
Why revert instead of deleting the commit?
The history is preserved. If you’ve already pushed and a partner has pulled, you can’t just erase history — revert is the safe, collaborative way to undo.
Situation
Command
See what a commit changed
git show <hash>
Restore one file to an old version
git checkout <hash> -- filename
Undo a pushed commit safely
git revert <hash>
Branches: Parallel Code Bases
Why Branches?
Without branches:
Catherine and Heathcliff both edit main. Every push risks a conflict. They have to take turns.
Catherine and Heathcliff each get their own parallel copy. They work independently, then combine when ready.
main: ─────────────────────────
Catherine: ╲── work ── work ──╱
Heathcliff: ╲── 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.
main: A ── B ── C ← main points here
│
catherine-temp: ╰── D ← catherine-temp points here
main is the default branch (the “real” version)
When Catherine creates catherine-temp, it starts as a copy of main
Her new commits go on catherine-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 ongit branch# Create a new branch AND switch to itgit checkout -b catherine-temperature# Or using the newer command (same thing)git switch -c catherine-temperature
$ git branchmain* catherine-temperature ← you are here
The * shows your current branch.
Naming convention: Use descriptive names with your name or the feature:
catherine-temperature, heathcliff-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 catherine-temperaturegit status# ... edit collaborative_analysis.py ...git add collaborative_analysis.pygit 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 catherine-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 catherine-temperature
What does -u origin catherine-temperature mean?
origin = your GitHub fork (set up when you cloned)
catherine-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 branchesgit branch# Switch to an existing branchgit checkout maingit checkout catherine-temperature# Or with the newer commandgit switch maingit switch catherine-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 switchgit add .git commit -m"Save work in progress"git checkout main # now it works
What This Looks Like
Here’s what Catherine and Heathcliff’s repo looks like with branches:
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):<<<<<<< HEADreturn (temp_f -32) *5/9=======return (temp_f -32) * (5/9)>>>>>>> heathcliff-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.pygit 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
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:
Use branches – work on separate features
Divide work clearly – “I’ll do Section A, you do Section B”
Pull often – git pull before starting work each day
Communicate – tell your partner what you’re working on
Keep commits small – easier to merge small changes
Catherine and Heathcliff’s strategy: Catherine works on temperature functions (Section A), Heathcliff works on wind chill functions (Section B).
Practice Checkpoint: Create & Resolve a Conflict
This is intentional – you’re going to cause a conflict on purpose:
Make sure you’re on main and everything is committed
Create a branch: git checkout -b conflict-test
In collaborative_analysis.py, change the first docstring line to: """Collaborative Weather Analysis -- edited on conflict-test"""
git add . then git commit -m "Edit docstring on branch"
Switch to main: git checkout main
Change that same line to: """Collaborative Weather Analysis -- edited on main"""
git add . then git commit -m "Edit docstring on main"
Now merge: git merge conflict-test
CONFLICT! Open the file, resolve the markers, pick whichever version you prefer
git add . then git commit -m "Resolve conflict"
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 catherine-temperature
Step 2: Go to your repo on GitHub. You’ll see a yellow banner:
catherine-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 testRun: python collaborative_analysis.pyTemperature section should show correctconversions.## NotesUsed 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:
Go to the Pull requests tab in the repo
Click on the PR
Click the Files changed tab to see what they modified – this is GitHub Compare in action
Green lines = additions, Red lines = deletions
You can leave comments on specific lines (click the + icon)
When you’re satisfied, click Review changes → Approve
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:
Click the green Merge pull request button on GitHub
Click Confirm merge
Optionally, click Delete branch to clean up
Back on your local machine, pull the merged changes:
Create a new branch: git checkout -b add-my-section
Edit collaborative_analysis.py – implement one function (e.g., fahrenheit_to_celsius)
Commit: git add . then git commit -m "Implement fahrenheit_to_celsius"
Push: git push -u origin add-my-section
Go to your fork on GitHub
Click Compare & pull request
Write a title and description, then click Create pull request
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*.hdf5data/raw/# OS files.DS_StoreThumbs.db# Environmentsvenv/.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_StoreThumbs.db.vscode/.idea/# Environmentsvenv/*.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? ────┘
How to find it: Go to Partner A’s repo on GitHub → Pull requests → click your merged PR → copy the URL from the browser.
Submit on Canvas under this week’s lab assignment. Both partners submit individually, but the URL will be identical.
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 left ✓
PR was merged into main ✓
Wrapping Up
What You Learned Today
Tool
What it does
git log
View commit history
git diff
See what changed before committing
git show <hash>
See what a specific commit changed
git checkout <hash> -- file
Restore one file to a past version
git revert <hash>
Undo a pushed commit safely
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 branchesgit checkout -b name # create + switchgit checkout main # switch to maingit merge branch-name # merge into currentgit branch -d name # delete branch
Collaboration:
git log --oneline# view historygit diff # see changesgit show <hash># inspect a commitgit checkout <h> -- f # restore one filegit revert <hash># undo safelygit push -u origin name # push new branch# Create PR on GitHub# Review → Approve → Mergegit pull # get merged code