# 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.](/article-images/illustrations/git-add-for-researchers-staging-workflow.webp)

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:

```bash
cd ~/projects/patient-analysis
git init
```

Expected output:

```text
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:

```bash
git config --global user.name "Your Name"
git config --global user.email "you@university.edu"
```

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:

```text
# 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:

```bash
git status
```

Expected output:

```text
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:

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

### Step 4. Stage one file

```bash
git add scripts/01_clean.R
```

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

```bash
git status
```

```text
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:

```bash
git diff --staged
```

Expected output for the new script:

```diff
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:

```bash
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:

```bash
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:

```bash
git add -p scripts/02_stats.py
```

Expected session:

```diff
@@ -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:

```bash
git add -n .
```

Expected output:

```text
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

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

Expected output:

```text
[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:

```bash
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:

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

Expected output for the first:

```text
On branch main
nothing to commit, working tree clean
```

For the second:

```text
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.](/article-images/illustrations/git-add-for-researchers-broad-add-choice.webp)

| Command | What it stages | Use it when |
|--|--|--|
| `git add scripts/01_clean.R` | One named file or path | You know exactly which file changed |
| `git add scripts/` | Everything under a directory, recursively | A 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 -A` | New, modified, and deleted files across the whole repository | One commit should capture everything |
| `git add -u` | Modifications and deletions of tracked files only, never new files | You edited or renamed existing files and created nothing new |
| `git add -p` | Individual hunks you approve interactively | Mixed changes in one file, or debug lines to exclude |
| `git add -n` | Nothing, it prints what would be staged | Previewing a broad add |
| `git add -f` | A file that `.gitignore` would normally exclude | Deliberate exceptions, such as a small test fixture |
| `git add -v` | Prints the names of staged files | Confirmation inside scripts and logs |
| `git restore, staged <file>` | Nothing, it removes the file from the index | Undoing 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:

```bash
git check-ignore -v data/raw/patients.csv
```

```text
.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`:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:**

```bash
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

```text
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

```text
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

```text
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:

```text
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

```text
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.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What does git add actually do?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between git add . and git add -A?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "How do I undo git add?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Run git restore, staged with the file name. Your edits stay in the working tree, they simply leave the next commit. On Git older than 2.23, use git reset with the file name instead."
      }
    },
    {
      "@type": "Question",
      "name": "Should researchers commit their data files?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "Why does git add say my file did not match any files?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "What is a hunk in git add -p?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "Do I need GitHub to use git add?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    },
    {
      "@type": "Question",
      "name": "How often should I stage and commit?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "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."
      }
    }
  ]
}
</script>

## Further Reading

- [Dual-pathway processing of AI-generated Chinese ink painting: evidence from eye-tracking, EEG, and artistic expertise.](https://pubmed.ncbi.nlm.nih.gov/42871037/)
- [A unified decision-driven framework: Long-term tracking via visual-language models with motion estimation.](https://pubmed.ncbi.nlm.nih.gov/42594410/)
- [Redistribution of Gaze Patterns in Facial Localized Scleroderma Following Autologous Fat Transfer: An Eye-Tracking Study.](https://pubmed.ncbi.nlm.nih.gov/42443420/)
- [Interferon release does not add discriminatory value to smear-negative HIV-tuberculosis algorithms.](https://pubmed.ncbi.nlm.nih.gov/21719487/)

## Sources

1. [DrugUtilisation: An R Package to Support Drug Utilisation Research Using the OMOP Common Data Model.](https://pubmed.ncbi.nlm.nih.gov/42448616/)
2. [Scaling evidence: national causal evaluation of perinatal quality collaboratives needs better data.](https://pubmed.ncbi.nlm.nih.gov/42253882/)