Git Add for Researchers: Step-by-Step Guide

By Dr. Zubair Khalid, DVM, MS, PhD ·

Git Add for Researchers: Step-by-Step Guide

By the end of this guide you will be able to put an analysis project under version control, stage exactly the files and changes you intend with git add, inspect what you staged before it becomes permanent, undo a wrong staging decision, and publish a clean snapshot to GitHub. You need three things on hand: Git installed on your computer (check with git, version), a terminal, and a text editor. The unstage command shown here, git restore, requires Git 2.23 or newer, released in 2019. A free GitHub account is optional until the sharing section near the end.

This walkthrough covers git add for researchers from the first commit to the first push, using a small clinical data analysis project as the running example. The same commands work for any project that mixes code, notebooks, and a manuscript.

Why version control belongs in every research project

Most labs already run a version control system without naming it: analysis_final.R, analysis_final_v2.R, analysis_final_v2_REAL.R. Those filenames encode a history that the filesystem cannot answer, such as which version produced the figure in your last submitted manuscript. Git replaces that convention with a repository, a complete ordered record of committed snapshots, each annotated with an author, a timestamp, and a message explaining why the change happened.

The staging area is the quality gate in that record. Before any change becomes permanent, git add lets you decide precisely what enters it, file by file and even line by line. Open-source research software shows the standard this supports. DrugUtilisation, an open-source R package for drug utilisation studies built on data mapped to the OMOP Common Data Model, implements comprehensive unit tests and standardized output formats that keep results consistent across databases and minimize coding errors [1]. That level of engineering is only practical when every change to the code is tracked and reviewable.

The same discipline matters well beyond a single laptop. An evaluation of national perinatal quality collaboratives argues that inconsistent measurement of key outcomes over time and unrecorded intermediate changes in clinical practice make it hard to separate genuine health improvements from data artifacts, and recommends systematic registries that track quality initiatives [2]. A Git history is that registry at project scale. It answers the question every collaborator, reviewer, and future version of you will ask: which exact version of the code produced this result?

Git for scientists differs from Git in commercial software teams mainly in what gets staged. Analysis scripts, notebooks, manuscript sources, and small configuration files go in. Raw data, large generated outputs, and anything sensitive usually stay out, as the walkthrough shows.

How git add works: working tree, index, and commits

A file moves from the working tree through git add into the index, is reviewed with git diff --staged, then recorded by git commit.

Four terms cover everything in this guide.

  • The working tree is the set of files you actually see and edit in your project folder.
  • The repository is the hidden .git directory plus the history it holds. git init creates it.
  • The index, also called the staging area, is the draft of your next commit. git add writes the current content of named files into it.
  • A commit is a permanent snapshot of the index, recorded with an author, date, and message. HEAD is Git's name for the commit you are working on top of, normally the latest commit on your current branch.

The daily rhythm is edit, stage, commit. You edit files in the working tree, stage the changes you want with git add, and record the staged content with git commit. Two properties of this design matter in practice. First, git add stores whole file snapshots rather than diffs, so staging is cheap and safe to repeat. Second, nothing is permanent until a commit exists, and nothing leaves your machine until you push, so every mistake in this guide is recoverable up to those points.

git add for researchers: the step-by-step workflow

The running example is a project folder named patient-analysis containing these files:

  • README.md, a short project description
  • .gitignore, the exclusion list you will create in step 2
  • scripts/01_clean.R and scripts/02_stats.py, analysis code
  • notebooks/eda.ipynb, an exploratory Jupyter notebook
  • data/raw/patients.csv, a large raw data file that will be ignored
  • results/figures/fig1.png, a generated figure that will be ignored

Step 1. Initialize the repository and identify yourself

Move into the project folder and create the repository:

cd ~/projects/patient-analysis
git init

Expected output:

Initialized empty Git repository in /home/you/projects/patient-analysis/.git/

Everything Git knows about your project now lives in the .git folder. Delete that folder and you have deleted the history, so never edit its contents by hand.

Tell Git who you are, because every commit records an author name and email:

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

If you skip this, the error appears later, at your first commit. The exact message and fix appear in the mistakes section.

Step 2. Write .gitignore before the first add

Create a file named .gitignore in the project root:

# Large or regenerable content
data/
results/

# Credentials and secrets
.env
credentials*.json

# System and editor noise
.DS_Store
Thumbs.db
__pycache__/
*.pyc
.ipynb_checkpoints/

Each line is a pattern. data/ excludes the whole directory, *.pyc excludes any file ending in .pyc, and lines starting with # are comments. Write this file before your first git add because it is far easier to prevent a file from entering the index than to remove it from history afterward.

Step 3. Look before you stage

Ask Git what it sees:

git status

Expected output:

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.gitignore
	README.md
	notebooks/
	scripts/

nothing added to commit but untracked files present (use "git add" to track)

An untracked file exists in the working tree but has never been staged or committed. Notice that data/ and results/ are absent, because .gitignore excludes them. The compact form git status -s prints one line per path, with ?? marking untracked entries:

?? .gitignore
?? README.md
?? notebooks/
?? scripts/

Step 4. Stage one file

git add scripts/01_clean.R

Successful git add prints nothing. Silence means success, which surprises many newcomers. Confirm the result:

git status
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   scripts/01_clean.R

The file now sits in the index as a new file. Nothing is saved to history yet. Staging is safe and fully reversible, so stage freely and refine later.

Step 5. Read the staged diff before committing

Before committing, read exactly what you staged:

git diff --staged

Expected output for the new script:

diff --git a/scripts/01_clean.R b/scripts/01_clean.R
new file mode 100644
index 0000000..8a4c1f9
--- /dev/null
+++ b/scripts/01_clean.R
@@ -0,0 +1,7 @@
+library(readr)
+library(dplyr)
+
+raw <- read_csv("data/raw/patients.csv")
+clean <- raw |>
+  filter(!is.na(patient_id))
+write_csv(clean, "data/clean/patients_clean.csv")

Lines beginning with + are additions and lines beginning with - are deletions. A hunk is a contiguous block of changed lines, marked by the @@ header that shows its line numbers. Three diff variants cover daily work:

  • git diff shows working tree changes that you have not staged yet.
  • git diff, staged shows what is in the index, ready to commit. -cached is an exact synonym.
  • git diff HEAD shows all changes, staged and unstaged, against the last commit.

Step 6. Stage more files, directories, and patterns

Stage several paths in one command, including whole directories:

git add README.md .gitignore
git add scripts/
git add "scripts/*.R"
git add notebooks/eda.ipynb

Adding a directory stages every non-ignored file under it, recursively. The quoted glob "scripts/*.R" is a pathspec, a pattern Git matches against file paths. Quoting it hands the pattern to Git instead of letting the shell expand it, which keeps behavior consistent across terminals.

Step 7. Stage everything at once, deliberately

Three commands stage broadly, and the differences matter:

git add -A
  • git add . stages new, modified, and deleted files in your current directory and below. Since Git 2.0, this includes deletions.
  • git add -A stages new, modified, and deleted files across the entire repository, no matter where you run it.
  • git add -u stages modifications and deletions of already-tracked files only. It never picks up new files.

Run git status immediately after any broad add so you know what you just staged. When you truly want everything, git add -A from the repository root is the clearest choice. To move or rename a tracked file and stage the change in one step, use git mv oldname newname.

Step 8. Stage pieces of a file with git add -p

Mixed changes in one file are the norm in research code: a real fix next to a leftover debug line. Patch mode lets you approve changes hunk by hunk:

git add -p scripts/02_stats.py

Expected session:

@@ -14,7 +14,10 @@ def summarize(df):
     grouped = df.groupby("arm")
     means = grouped["score"].mean()
+    print("DEBUG", means)
     counts = grouped["score"].size()
     return means, counts

Stage this hunk [y,n,q,a,d,s,e,?]?

Answer with a single letter:

  • y stage this hunk
  • n skip this hunk
  • s split the hunk into smaller pieces when possible
  • e edit the hunk manually in your editor
  • a stage this hunk and all remaining hunks in the file
  • d skip this hunk and all remaining hunks in the file
  • q quit without staging further hunks

Pressing ? prints the full key list. This is the single most useful staging tool for keeping debug prints, abandoned experiments, and unrelated refactoring out of a commit.

Step 9. Preview a broad add with a dry run

Before staging many files, preview the outcome without changing anything:

git add -n .

Expected output:

add 'scripts/02_stats.py'
add 'notebooks/eda.ipynb'

The -n or -dry-run flag stages nothing. If the list shows a file you do not want, fix .gitignore first, then add.

Step 10. Commit the staged snapshot

git commit -m "Add cleaning and summary scripts with .gitignore"

Expected output:

[main (root-commit) 3f9c1d2] Add cleaning and summary scripts with .gitignore
 5 files changed, 112 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 README.md
 create mode 100644 notebooks/eda.ipynb
 create mode 100644 scripts/01_clean.R
 create mode 100644 scripts/02_stats.py

The commit records the index exactly as it stood, not the current working tree. 3f9c1d2 is the short hash, a unique identifier for the snapshot, and root-commit marks the first commit in the history. Write messages in the imperative mood and lead with the why: "Add sensitivity analysis for missing covariates" beats "changes".

Step 11. Undo staging mistakes

To remove a file from the index while keeping your edits in the working tree:

git restore --staged notebooks/eda.ipynb

On Git older than 2.23, the equivalent is git reset, notebooks/eda.ipynb. To stop tracking a file that is already committed while keeping it on disk, use git rm, cached data/leaked.csv. That removes it from future commits but not from past history, which is why prevention through .gitignore matters more.

Step 12. Verify the result

Three checks confirm a clean state:

git status
git log --oneline
git show --stat HEAD

Expected output for the first:

On branch main
nothing to commit, working tree clean

For the second:

3f9c1d2 (HEAD -> main) Add cleaning and summary scripts with .gitignore

git show, stat HEAD lists the files and line counts in the latest commit. To see every file Git is tracking, run git ls-files, and add -stage to see the raw index entries with their content hashes.

Key git add options at a glance

Decision tree leading from a staging question to git add -p, git add -n, git add -A, or a named file path.

CommandWhat it stagesUse it when
git add scripts/01_clean.ROne named file or pathYou know exactly which file changed
git add scripts/Everything under a directory, recursivelyA whole folder of related changes
git add .New, modified, and deleted files in the current directory and below (Git 2.0+)A broad change from the project root, after checking git status
git add -ANew, modified, and deleted files across the whole repositoryOne commit should capture everything
git add -uModifications and deletions of tracked files only, never new filesYou edited or renamed existing files and created nothing new
git add -pIndividual hunks you approve interactivelyMixed changes in one file, or debug lines to exclude
git add -nNothing, it prints what would be stagedPreviewing a broad add
git add -fA file that .gitignore would normally excludeDeliberate exceptions, such as a small test fixture
git add -vPrints the names of staged filesConfirmation inside scripts and logs
git restore, staged <file>Nothing, it removes the file from the indexUndoing an add while keeping your edits

What to stage and what to leave out

Stage content that is small, text-based, and meaningful to a human reader:

  • Source code in any language, plus shell scripts and Makefiles or pipeline definitions
  • Notebook files, ideally with large outputs cleared before staging
  • Manuscript sources in Markdown, LaTeX, or plain text
  • Environment definitions such as requirements.txt or environment.yml
  • Small configuration files and the .gitignore itself
  • Tiny test fixtures, a few kilobytes at most

Leave out content that is large, regenerable, or sensitive:

  • Raw data files, instrument outputs, and database dumps
  • Generated figures, tables, and model checkpoints that your scripts can rebuild
  • Credentials, API keys, .env files, and service account JSON files
  • Protected health information or any identifiable participant data, which must never enter a repository, especially one with a remote copy

To find out which rule is excluding a file, ask Git directly:

git check-ignore -v data/raw/patients.csv
.gitignore:2:data/	data/raw/patients.csv

The output names the file, line number, and pattern responsible. To track an otherwise empty directory, such as a templates folder, add a placeholder file conventionally named .gitkeep:

mkdir -p scripts/templates
touch scripts/templates/.gitkeep
git add scripts/templates/.gitkeep

Git tracks files, not directories, so a directory with no files simply does not exist in the repository.

For files that must be versioned but are too large for normal handling, Git Large File Storage (LFS) replaces the file in the repository with a small pointer and stores the actual content on a separate server-side store:

git lfs install
git lfs track "*.fastq.gz"
git add .gitattributes

Run git lfs track before adding the large file, because the pattern is recorded in .gitattributes. Hosting platforms meter LFS storage and transfer separately from ordinary repository content. GitHub rejects individual files above its documented 100 MB limit and warns about files above 50 MB, which is why a stray git add . in a data directory usually surfaces at push time.

Sharing your work on GitHub

GitHub for researchers adds three things on top of local version control: an off-machine backup, a shareable home for collaborators and reviewers, and pull requests, which are proposed changes that teammates can comment on before you merge them.

Create an empty repository on the GitHub website, then connect and publish your local history:

git remote add origin https://github.com/yourname/patient-analysis.git
git branch -M main
git push -u origin main

Here origin is the nickname for the remote copy, and -u records it as the default destination so future pushes need only git push. Before your first push, audit what history will publish, because pushing sends every commit, including ones you forgot about:

git ls-files
git log --stat

For day-to-day work, develop on a branch, a parallel line of history, and open a pull request when it is ready:

git switch -c sensitivity-checks

Keep pre-publication work in a private repository, and make it public when the manuscript is out if funder or journal policy allows. A sensible next step after your first push is a pre-commit hook, a script Git runs before each commit, to block large files or credential patterns automatically.

Practical git add tips for researchers

  1. Stage in logical units. One commit should contain one idea: a fix, a new analysis, or a documentation update. Reviewers and collaborators read history by commits, not by lines.
  2. Make git status a reflex. Run it before every git add. Ten seconds of looking beats ten minutes of unstaging.
  3. Pair every add with git diff, staged. The diff is the last chance to catch a debug print or a hardcoded path before it becomes permanent.
  4. Use git add -p for anything you edited heavily. Hunk-level staging keeps unrelated changes out of each other's commits.
  5. Preview broad adds with git add -n. A dry run costs nothing and catches ignored-file surprises early.
  6. Re-add after late edits. If you edit a file after staging it, the index still holds the older snapshot. git status will list the file under both "Changes to be committed" and "Changes not staged for commit". Run git add again to refresh.
  7. Use git commit -a -m for tracked-file edits. It stages modifications to tracked files and commits in one step, but it never picks up new files, so brand-new scripts still need git add.
  8. Set two aliases to speed up the safe path:
git config --global alias.st "status -sb"
git config --global alias.unstage "restore --staged"

After this, git st shows a compact status and git unstage file.R undoes an add.

Common Mistakes and Limitations

git add cannot find your file

fatal: pathspec 'analys.R' did not match any files

The path you typed does not exist from where you stand. Causes are a typo, running from the wrong directory, a file not yet created, or case mismatch, because paths are case-sensitive on most servers. Run git status to see real names, use shell tab completion, and prefer paths relative to the repository root.

Git refuses to add an ignored file

The following paths are ignored by one of your .gitignore files:
data/raw/patients.csv
hint: Use -f if you really want to add them.

This is .gitignore doing its job. Run git check-ignore -v data/raw/patients.csv to find the responsible rule, then either fix the pattern or, if the file genuinely belongs in version control, add it with git add -f as a deliberate exception.

git add runs outside a repository

fatal: not a git repository (or any of the parent directories): .git

You are in a folder with no repository above it. Move into the project with cd, or create a repository with git init if the project has none.

Treating git add as a save button

git add stages content, it does not record it. Checkpoints in history are commits, and only commits appear in git log. If you staged work at the end of the day and turned off the computer, the staging area survived, but a commit is what makes the snapshot findable and restorable later.

Editing a file after staging it

The index holds the content as of the moment you staged. Edit afterward and git status shows the file in two places at once:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   scripts/02_stats.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
	modified:   scripts/02_stats.py

The staged version and the working tree version differ. Run git add again to stage the newer content, or git diff to see what changed since you staged.

Staging data files and huge binaries

A broad git add . in a data-heavy project produces slow pushes and, on GitHub, a rejection for files above the platform's 100 MB limit. Worse, Git history keeps everything forever, so deleting a 2 GB file in a later commit does not shrink the repository. Fix this before the first add with a thorough .gitignore, and use Git LFS for the few large files that genuinely need versioning.

Committing credentials or patient data

.env files, API keys, and identifiable participant data must stay out of repositories. History retains removed content, so a leaked key in an old commit is still a leaked key. If credentials ever reach a commit, rotate them immediately, then remove them from history with a rewrite tool such as git filter-repo and follow your hosting platform's guidance on sensitive data removal. Prevention through .gitignore is the control that actually works.

The embedded repository warning

warning: adding the embedded git repository: notebooks/old-analysis
hint: You've added another git repository inside your current repository.
hint: Clones of the outer repository will not contain the contents of
hint: the embedded repository and will not know how to obtain it.

This happens when you git add a folder that contains its own .git directory. The outer repository records a bare pointer with no way for collaborators to fetch the content, so their clone shows an empty folder. Decide deliberately: either absorb the files by deleting the inner .git directory and re-adding the folder, or register it properly as a submodule, a repository nested inside another with its version pinned, using git submodule add.

Confusing unstage with discard

git restore, staged file.R keeps your edits and only empties the staging entry. git restore file.R throws away the working tree edits permanently. When in doubt, unstage first, look at git diff, and only discard changes you can regenerate.

Smaller stumbles

On Windows you may see warning: LF will be replaced by CRLF in scripts/01_clean.R. This comes from the core.autocrlf setting translating line endings and is harmless, but agree on one setting across your team to avoid noisy diffs. Empty directories vanish from commits because Git tracks files, so use a .gitkeep placeholder where the folder itself matters.

Two limitations bound this guide. Commands and defaults follow current Git behavior, and installations older than 2.23 differ in the unstage command. Beyond mechanics, deciding what belongs in a repository is a policy question that depends on your data governance rules, funder requirements, and institutional policy, so agree on a written convention with your team before the first commit.

Frequently Asked Questions

What does git add actually do?

git add copies the current content of the files you name into the staging area, also called the index, which is the draft of your next commit. Nothing is permanently recorded until you run git commit, and nothing leaves your computer until you push.

What is the difference between git add . and git add -A?

Scope. git add . stages new, modified, and deleted files in your current directory and below, while git add -A does the same across the entire repository regardless of where you run it. Both include deletions in Git 2.0 and newer.

How do I undo git add?

Run git restore, staged <file>. Your edits stay in the working tree, they simply leave the next commit. On Git older than 2.23, use git reset, <file> instead.

Should researchers commit their data files?

Usually no. Keep raw and large data out of Git with a .gitignore entry, use Git LFS for the few large files that must be versioned, and never commit identifiable participant data or credentials, because history preserves removed files.

Why does git add say my file did not match any files?

Because the path you typed does not exist from your current directory. Check git status for the real names, fix the spelling, or move to the repository root before adding.

What is a hunk in git add -p?

A hunk is a contiguous block of changed lines in a file. Patch mode shows each hunk in turn and lets you stage it with y, skip it with n, split it with s, or edit it with e.

Do I need GitHub to use git add?

No. git add works entirely on your own machine, and so do commits. GitHub becomes relevant when you want remote backup, sharing, and pull request review, which starts at your first push.

How often should I stage and commit?

Small and often. Stage one logical change, review it with git diff, staged, and commit it with a message that says why, ideally before you switch to a different task.

Further Reading

Sources

  1. DrugUtilisation: An R Package to Support Drug Utilisation Research Using the OMOP Common Data Model.
  2. Scaling evidence: national causal evaluation of perinatal quality collaboratives needs better data.