Prof. Marc Los Huertos • Pomona College
Why Git? Concepts Setup Basic Workflow RStudio Pane History Branches Remotes Complications Shared Repos .gitignore Reference
What this module covers. Git records every change to a project, lets you travel back to any earlier state, and lets multiple people work on the same files without overwriting each other. GitHub hosts Git repositories and adds pull requests, issue tracking, and code review. This module builds from the first
git initthrough branching, merge conflicts, and the conventions that make shared EA030 repositories work without chaos.
Without Git, project histories look like this:
# A folder without version control
analysis.R
analysis_v2.R
analysis_FINAL.R
analysis_FINAL_fixed.R
analysis_FINAL_fixed_marccomments.R
analysis_use_this_one.R
With Git, you have one file and a complete labelled history of every change — who made it, when, and why. You can revert to any previous state with one command, branch to try a new approach without touching the working version, and trace exactly which code produced which result.
📌 Git is not just for software engineers
Every reproducible environmental analysis benefits from version control. When a reviewer asks “what changed between your submitted draft and the revision?”, Git answers precisely. When you re-run an old analysis on new data, Git tells you whether the code itself changed.
Understanding Git means understanding four locations your files inhabit and the commands that move them between locations.
git add → git restore
git commit → git reset HEAD~
git push → git pull
| Location | What it contains | Analogy |
|---|---|---|
| Working directory | Files you see and edit | Your desk — messy, in progress |
| Staging area | Curated preview of your next commit | A tray of items ready to photograph |
| Local repository | Permanent record of all commits | A filing cabinet in your office |
| Remote (GitHub) | The same history, hosted online | A filing cabinet in a shared office |
The staging area is the most misunderstood part of Git. It lets you group related changes into one logical commit even when many files have changed — you choose exactly what goes into each commit.
⚙️ A commit is a snapshot, not a diff
Git stores each commit as a complete snapshot of all tracked files —
not just the changed lines. The diff you see with git diff
is computed on demand by comparing two snapshots. Each commit has a
unique SHA-1 hash (e.g., a3f9c2d) that permanently
identifies it and can be referenced in advanced commands.
Git embeds your name and email in every commit. Set these once in the Terminal tab in RStudio:
$ git config –global user.name “Jamie
Smith”
$ git config –global user.email
“jsmith@students.pomona.edu”
$
git config –global init.defaultBranch main
# Verify
$ git
config –list
Go to https://github.com and sign up with your Pomona email. Verify student status at education.github.com for free private repositories.
GitHub no longer accepts passwords over HTTPS. Generate a PAT:
Store the PAT in R:
install.packages("gitcreds") # if needed — already on Pomona server
gitcreds::gitcreds_set() # paste your PAT when prompted
The usethis package wraps common setup steps:
library(usethis)
create_github_token() # opens browser with correct scopes pre-selected
gitcreds::gitcreds_set() # store the PAT after copying from GitHub
git_sitrep() # "situation report" — verifies everything is connected
⚠️ Never commit a PAT or any secret inside a script or .Rmd file
If you commit a token — even briefly — GitHub revokes it automatically. Secrets belong in environment variables or credential stores, never in code.
Route A — New project from GitHub (recommended for shared work)
Route B — Existing RStudio Project → GitHub
library(usethis)
use_git() # initialises git, makes first commit, opens Git pane
use_github() # creates the GitHub repo and pushes
<div class="wf-title">Check status — what has changed?</div>
<div class="wf-code">git status</div>
<div class="wf-note">Red = not staged. Green = staged and ready to commit.</div>
<div class="wf-title">Review the diff — what exactly changed?</div>
<div class="wf-code">git diff scripts/01_clean.R</div>
<div class="wf-note">Lines with <code>+</code> were added; lines with <code>-</code> were removed.</div>
<div class="wf-title">Stage the changes you want in this commit</div>
<div class="wf-code">git add scripts/01_clean.R</div>
<div class="wf-note">Stage everything reviewed: <code>git add .</code></div>
<div class="wf-title">Commit with a meaningful message</div>
<div class="wf-code">git commit -m "fix: drop QC-flagged DO records before summarising"</div>
<div class="wf-note">A permanent record — write it for your future self.</div>
<div class="wf-title">Push to GitHub</div>
<div class="wf-code">git push origin main</div>
<div class="wf-note">Or just <code>git push</code> once upstream is set.</div>
Complete the sentence: “If applied, this commit will…”
# Bad — tells you nothing
git commit -m "changes"
git commit -m "fix stuff"
# Good — one sentence, explains what and why
git commit -m "add DO percent-saturation using Benson and Krause equation"
git commit -m "fix: filter() drops NAs before group_by to prevent silent errors"
git commit -m "refactor: extract site-cleaning logic into its own function"
git commit -m "docs: add README section on data sources and field protocol"
✅ Conventional Commits prefixes
feat: new feature or analysis step · fix:
bug or error correction · docs: documentation only ·
refactor: restructuring without behaviour change ·
data: new or updated data file
The Git pane (top-right, next to Environment) provides a graphical interface for the most common operations.
| Action | RStudio Git pane | Terminal equivalent |
|---|---|---|
| See changed files | Files listed automatically with status icons | git status |
| Stage a file | Check the box in the Staged column | git add <file> |
| Unstage a file | Uncheck the box | git restore --staged <file> |
| View diff | Click the file name | git diff <file> |
| Commit | Commit button → write message → Commit | git commit -m 'message' |
| Push | Green up-arrow Push button | git push |
| Pull | Blue down-arrow Pull button | git pull |
| View history | Clock icon (History) | git log --oneline |
| Switch branch | Branch dropdown top-right of pane | git checkout <branch> |
| New branch | Branch dropdown → New Branch | git checkout -b <branch> |
📌 Use the Terminal for anything not in the pane
The Git pane covers about 80% of daily operations. For branching, rebasing, reset, stash, and conflict resolution you need the Terminal tab (Alt+Shift+R or Tools → Terminal → New Terminal). It opens at the project root automatically.
# Compact one-line history
$ git log –oneline
a3f9c2d
fix: drop NAs before group_by
8b21e4a add
DO percent-saturation calculation
f1dc39a
initial project structure and raw data
# Graph showing all branches
$ git log –oneline –graph –all –decorate
# Full detail for one commit
$ git show a3f9c2d
# Changes between two commits
$ git diff f1dc39a a3f9c2d
# One file between HEAD and 3 commits ago
$ git diff HEAD~3 HEAD –
scripts/02_visualise.R
# What is staged
but not yet committed?
$ git diff
–staged
# Restore a file to how it looked in commit
f1dc39a
$ git restore
–source=f1dc39a scripts/01_clean.R
# File is
now restored — stage and commit to keep it
A branch is a parallel line of development. Use
branches to work on analyses without touching the working
main branch. Merge back when done.
# Create and switch to a new branch
$ git checkout -b
feature/turbidity-analysis
Switched to a new
branch ‘feature/turbidity-analysis’
# List branches — asterisk marks current
$ git branch
*
feature/turbidity-analysis
main
# After making commits, merge back into
main
$ git checkout main
$ git pull
$
git merge feature/turbidity-analysis
# Delete the merged branch
$ git branch -d feature/turbidity-analysis
For shared repositories, merge through a Pull Request (PR):
git push origin feature/turbidity-analysisCloses #12✅ Branch naming conventions
feature/add-turbidity-model ·
fix/do-na-filter · docs/update-readme ·
analysis/seasonal-comparison
Use consistent prefixes. Never work directly on main in
a shared repository.
git
log origin/main.
origin is the default name for the
GitHub remote.
origin
automatically.
git push needs no
arguments.
A merge conflict occurs when two branches edited the same lines of the same file. Git marks the conflict and asks you to resolve it manually.
<<<<<<< HEAD (your current branch — main)
scale_colour_manual(values = c("#4a7c5e", "#c8832a"))
=======
scale_colour_brewer(palette = "Dark2")
>>>>>>> feature/seasonal (the branch being merged in)
Resolution steps:
<<<<<<<, =======,
>>>>>>>)git add scripts/02_visualise.Rgit commit — Git pre-fills the
message⚙️ Reading the conflict markers
<<<<<<< HEAD
— start of your version (the branch you are merging INTO)
======= — divider between the two versions
>>>>>>> branch-name
— end of their version (the branch being merged in)
You can keep
yours, keep theirs, keep both, or write something new entirely. No
markers may remain in the file when you stage it.
✅ Use RStudio’s three-panel merge tool
When a conflict is detected, the Git pane shows an orange U (unresolved) next to the file. Click it and choose Open in merge tool for a side-by-side view with Accept Ours / Accept Theirs buttons.
You are in ‘detached HEAD’ state.
HEAD is now at f1dc39a initial project structure
This happens when you git checkout a commit hash
directly. Commits made here are orphaned and will be
garbage-collected.
# Option 1 — go back to main, discarding
detached commits
$ git checkout
main
# Option 2 — save your work by
creating a branch first
$ git
checkout -b rescue/my-exploration
$ git
checkout main
$ git merge
rescue/my-exploration
# Save the commit on a new branch
$ git branch feature/oops-commit
# Reset main back one commit, keeping changes in
working directory
$ git reset
HEAD~1
# Continue on the feature
branch
$ git checkout
feature/oops-commit
⚠️ Never use git push –force on a shared branch
git push --force rewrites remote history. Everyone who
has pulled the branch will have a diverged history that is very
difficult to reconcile. On main or any shared branch, use
git revert instead — it adds a new undo commit without
touching history.
| Situation | Command | Safety |
|---|---|---|
| Changed a file — not staged, not committed | git restore <file> |
Destructive — file changes lost |
| Staged a file — not committed | git restore --staged <file> |
Safe — file stays modified, just unstaged |
| Committed locally — not pushed (discard changes) | git reset --hard HEAD~1 |
Destructive — changes lost forever |
| Committed locally — not pushed (keep changes in working dir) | git reset HEAD~1 |
Safe — changes returned to working directory |
| Committed and pushed to shared branch | Do not rewrite. Use
git revert <hash> |
Safe — revert adds a new undo commit |
| Want to undo one commit without rewriting history | git revert <hash> |
Safe — new commit, history intact |
# Mid-analysis and need to urgently fix
something on main
$ git stash
push -m “half-finished turbidity model”
Saved
working directory and index state: half-finished turbidity
model
$ git checkout main
# working directory is now clean
# … fix the bug, commit, push …
$ git checkout feature/turbidity-analysis
$ git stash pop #
restore stashed work
.gitignore tells Git which files to never track. Place
it in the project root.
# .gitignore for an EA030 R project
# R-specific
.Rhistory
.RData
.Rproj.user/
# Rendered outputs — reproducible from source
output/figures/
output/reports/
*.html
*.pdf
# Large raw data files — share via server or Google Drive
data/raw/*.csv
data/raw/*.xlsx
data/raw/*.tif
data/raw/*.shp
# OS clutter
.DS_Store
Thumbs.db
# Secrets
.Renviron
secrets.R
Generate a standard R .gitignore in one line:
usethis::use_gitignore()
⚠️ Already committed a file by mistake?
Adding a file to .gitignore does NOT untrack it if
already committed. To stop tracking without deleting from disk:
git rm --cached data/raw/huge_file.csv
git commit -m "stop tracking large raw data file"
git add . stages everything
–staged for staged
add, commit, push) and a reverse
(restore, reset, pull).
main. Use pull requests for review.
git
revert to undo publicly — it adds a commit rather than rewriting
history.
.RData,
.Rhistory, rendered HTML, large data files, and secrets.
Use usethis::use_gitignore() for a sensible R default.