3. Version Control

Commit often, commit clearly

6 min read

Version control with Git is the single most valuable habit you can build as a researcher. It is your unlimited undo button, your collaboration tool, and your evidence trail — all in one.

The basic cycle in Positron

Every working session follows the same pattern:

Work — write code, analyse data, edit your manuscript in Positron

Stage — in the Source Control panel, click + next to the files you changed to stage them

Commit — write a clear message describing what you changed, click the tick

Sync — click Sync to push your commits to GitHub

That is it. Repeat every time you finish a meaningful piece of work.

Writing good commit messages

A commit message should answer: “What did I change and why?”

Good commit messages:

"added linear model for shelter preference"
"fixed figure 3 axis label — was showing mm not cm"
"updated methods section after reviewer comment 2"
"added raw data for shoreline survey round 3"
"removed outlier tank 7 — equipment malfunction noted in fieldbook"

Bad commit messages:

"update"
"fix"
"changes"
"asdfgh"
"final version 3"
TipHow often should I commit?

Commit whenever you have done something you would not want to redo. In practice this usually means at the end of every working session, or whenever you finish a discrete piece of work (writing a new analysis, finishing a section of the manuscript, adding new data).

What to track and what to ignore

Not everything belongs in version control. Your .gitignore file tells Git what to skip.

Track these: - All .qmd files (your analysis and manuscript) - _quarto.yml and styles.css - renv.lock (package versions) - references/references.bib - README.md - Small derived data files (CSVs under ~10 MB) - R model objects (*.rds) if small enough

Ignore these: - data/raw/ large files — upload directly to Zenodo instead - outputs/ generated files — these are reproduced by rendering analysis.qmd - _freeze/ Quarto cache - .Rhistory, .RData, .Rproj.user/ - Any file containing passwords, API keys, or personal information

A good starting .gitignore for a Quarto/R project:

# R
.Rhistory
.RData
.Rproj.user/

# Quarto
_freeze/
_site/
docs/
/.quarto/

# Large data files
data/raw/*.csv
data/raw/*.xlsx
*.zip

# renv cache (the lockfile is tracked, not the library)
renv/library/
renv/staging/

# OS files
.DS_Store
Thumbs.db

Using branches (optional but useful)

If you are making a big change you are not sure about — like trying a new statistical approach — you can create a branch so your main version stays safe.

In Positron, click the branch name in the bottom left corner → Create new branch.

Name it something descriptive like try-mixed-model or reviewer-revisions.

When you are happy with the changes, merge the branch back into main. If it does not work, just delete the branch — your main version is untouched.

NoteFor most PhD projects, one branch is enough

Branching is more useful for collaborative software projects. For a solo research project, committing to main with good commit messages is perfectly fine. Do not over-engineer this.

Recovering from mistakes

This is where version control earns its place. If you break something:

Undo the last commit (keep changes): In Positron Source Control panel → ... menu → Undo Last Commit

Revert a file to its last committed state: Right-click the file in Source Control → Discard Changes

Go back to an earlier version: In Positron, open the Timeline panel (View → Open View → Timeline) to see the history of any file and restore an earlier version.

Working with co-authors

Everything above works the same with co-authors — coordinating changes across people is the whole reason Git exists. A few things change once you’re not working alone:

Add them as collaborators

On GitHub: repo → Settings → Collaborators → Add people, using their GitHub username or email. They can now clone the repo and push commits directly, same as you.

Avoid stepping on each other

  • Sync (pull) before you start working each session — in Positron’s Source Control panel, click Sync to pull down anyone else’s changes before you add your own.
  • Split responsibility where you can (e.g. one person on Methods, another on Discussion) — two people editing the same section at the same time is the main source of conflicts.
  • If Git reports a merge conflict, Positron shows you the conflicting lines directly in the editor — choose which version to keep (or combine them), then stage and commit as normal.

For larger teams, use branches properly

For three or more active collaborators, or more experimental work, give each person their own branch (see Using branches above) instead of everyone committing straight to main. GitHub pull requests add an optional review step before merging a branch in, if you want one.

List everyone on Zenodo

When you reserve or publish your Zenodo record (chapter 4), add every co-author under Creators with their name and, ideally, their ORCID iD — this is what makes the permanent archived record credit everyone correctly, not just whoever happened to click upload.

TipWhat’s an ORCID?

A free, permanent researcher ID (orcid.org) that disambiguates your name from others and links your work across journals, Zenodo, and funders. Get one if you don’t have one, and ask co-authors for theirs before publishing the Zenodo record.

Optional: verify reproducibility with GitHub Actions

The template ships scripts/generate_evidence.R, which walks every fig-/tbl- label cited in index.qmd, traces it back through analysis.qmd to the reads/writes and code that actually produced it, and writes the result to evidence.json. Platforms like ScienceEcosystem can read this manifest to show a reader exactly what generated a given number — not just link to the repo in general.

Running it locally (Rscript scripts/generate_evidence.R) proves your code still produces those claims on your machine. Running it in GitHub Actions on every push proves it on a clean checkout with nothing but what’s committed — the actual claim a “reproducible” badge is making. Don’t wire this up before renv.lock exists (Day 1 checklist, previous chapter) — a workflow with no lockfile to install from just fails on every push.

Once renv.lock is committed, add .github/workflows/reproduce.yml:

on:
  push:
    branches: [main]

jobs:
  reproduce:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: r-lib/actions/setup-r@v2
      - uses: r-lib/actions/setup-renv@v2
      - uses: quarto-dev/quarto-actions/setup@v2
      - run: quarto render analysis.qmd && quarto render index.qmd
      - run: Rscript scripts/generate_evidence.R
      - uses: actions/upload-artifact@v4
        with:
          name: evidence
          path: evidence.json

This is optional. Rendering and archiving on Zenodo (next chapter) works exactly the same whether or not you set this up — it’s an extra layer of proof, not a requirement.

NoteKnown limitations

generate_evidence.R does static text analysis of R chunks, not real data-flow analysis — it reads variable assignments and file paths, it doesn’t execute your code to trace them. It’s R-only (no Python/Julia chunks), and can’t always tell apart two unrelated chunks that happen to reuse the same short, generic variable name. These are documented in detail in the script’s own header comment — read it before relying on the output for anything you haven’t spot-checked.

GitHub as a backup

Every time you sync to GitHub, you have a complete backup of your project in the cloud. If your laptop dies, floods, or gets stolen the day before you submit, you can clone your repository onto any computer and pick up exactly where you left off.

# Clone your repository onto a new computer
git clone https://github.com/yourusername/koura-habitat-mesocosm