# Git 101: know which change you are handing over

An original 40-minute introduction for someone who can reason about software but
is new to Git. Follow one fictional configuration change through the files you
edit, the snapshot you select and the history you record. Then run real Git
commands through a small, disposable local lab.

## 1. Name the pieces before using the commands

**Git is a distributed version-control tool.** It records project snapshots and
their relationships so people can compare changes and collaborate. It is not a
programming language, a coding agent or a hosting website. GitHub and other
hosting services can store Git repositories and add review workflows. A local
Git commit does not require a hosting account.

A **repository** holds the project's Git history and metadata. In the ordinary
checkout used here, that metadata lives in `.git`; the editable project files
around it form the **working tree**. A downloaded archive of code may have no
Git history. Having a folder of files and having a repository are different.

The **index**, often called the staging area, holds the proposed next snapshot.
Think of preparing a package for a teammate: editing changes your desk copy;
staging selects bytes for the package; committing records the selected snapshot.
The analogy has limits: staging is not approval, and a commit is neither a
deployment nor proof that the code works. Git stores more than a folder copy,
including parent relationships and author/message metadata.

A **commit ID** identifies a particular recorded commit. A **branch** is a named
reference to a commit that normally advances when you commit on it. **HEAD**
normally identifies your checked-out branch, although Git can also check out a
commit directly. A branch is not a second folder or a backup of uncommitted work.

For an FDE using Claude Code or another coding agent, these distinctions answer
a practical question: “Which exact change am I asking someone to accept?” The
agent may edit files; Git records selected content. Neither substitutes for your
review of scope, behavior and customer requirements.

## 2. Read the three versions of one file

The fictional file `policy.json` contains one setting, `max_items`. The lab
records 1, stages 2, then edits the working copy to 3. At that moment there are
three versions worth inspecting: committed 1, staged 2 and working 3.

| Read-only command | Comparison or evidence | What it cannot establish |
| --- | --- | --- |
| `git status --short` | Staged, unstaged and untracked paths | Whether a change is correct |
| `git diff -- policy.json` | Working file against the index | The complete next commit |
| `git diff --cached -- policy.json` | Index against HEAD | Later unstaged edits |
| `git show HEAD:policy.json` | Content recorded in the current commit | The current file on your desk |

`git add -- policy.json` copies that file's current content into the index.
It does not keep following future edits. An ordinary `git commit -m ...` records
the index; the lab deliberately avoids options that select content differently.
`--` ends command options and makes the following item a path.

For the tracked file in this exercise, the two leading status columns compare
index to HEAD, then working file to index. A space followed by M (` M`) means an
unstaged modification; M followed by a space (`M `) means a staged modification;
`MM` means both comparisons changed. `??`
marks an untracked file. Preserve the spaces when reading the printed strings.
Conflict status uses additional rules outside this introduction.

The second file, `note.txt`, is an unrelated fictional note. The ordinary diff
above does not display its untracked contents. Read status and inspect the
intended paths; an empty diff does not mean there are no files left to consider.

## 3. Predict and run the local history

Download `fde-git-snapshot-lab.py`. Use Python 3.11+ and an approved Git 2.28+
installation. No Python packages, account, model, credentials or internet are
needed to execute it. Run these commands from any ordinary working folder:

```sh
python3 --version
git --version
python3 fde-git-snapshot-lab.py
```

The program creates a new temporary repository and runs real Git commands only
there. It uses a fictional author, ignores inherited Git repository/configuration
overrides and uses an empty initialization template. It does not change your
global Git configuration or the directory from which you launched it. The
temporary repository is removed after the report is captured. The report includes
the actual diff text and commit IDs; IDs may differ between runs.

Predict each event before comparing its JSON output. `working`, `index` and
`committed` report actual file contents, not hardcoded expected answers.

| Event | Working / index / committed `max_items` | Observation |
| --- | --- | --- |
| `baseline` | 1 / 1 / 1 | One recorded file; clean status |
| `edited` | 2 / 1 / 1 | Policy unstaged; note untracked |
| `staged` | 2 / 2 / 1 | Policy staged; note still untracked |
| `edited-after-stage` | 3 / 2 / 1 | Policy status is `MM` |
| `review` | 3 / 2 / 1 | The two diffs compare different changes |
| `committed` | 3 / 2 / 2 | Commit contains 2; working edit to 3 remains |
| `branch-reference` | 3 / 2 / 2 | New reference points to baseline 1; checkout stays on main |

In `committed`, one small fixture check asks whether `max_items` equals 3.
The working file passes; the committed file does not. This is an actual value
comparison, not an application test suite or a quality score. It exposes a common
handoff problem: evidence from one set of bytes can be attached to another.

The final command creates `review/baseline` at the first commit. It does not
switch branches, copy the current working edits or merge anything. There are no
remotes. Nothing is pushed, published or deployed.

## 4. Change one decision and inspect the result

Make two separate copies of the Python file and keep the original for comparison.

**Experiment A — stage the later edit.** In one copy, set
`RESTAGE_AFTER_EDIT = True`. The review now shows 3 in the working tree and
index. The next commit records 3, the policy diff becomes empty, and the unrelated
note remains untracked. Both fixture checks pass. Explain why this is an evidence
match, not proof that a limit of 3 is a good product decision.

**Experiment B — include another path.** In a fresh copy, set
`INCLUDE_NOTE = True` and leave restaging false. The next commit now contains
`note.txt` as well as `policy.json`; the policy mismatch remains. The note is
harmless fictional text, but its inclusion illustrates a scope error. In a real
repository, inspect all staged paths and bytes for unrelated work and sensitive
material before committing or publishing. A tracked secret is not removed from
history merely by deleting its current file.

For each experiment, keep the changed setting, actual status, committed tree
and your explanation. Do not substitute a screenshot of a green process exit
for the contents you intend to hand over.

## 5. Carry this into an agent-assisted change

A fictional client asks for one bounded policy change. An assistant edits the
policy and writes a planning note. Before accepting its proposed commit:

1. Confirm the intended repository and current branch. Read status before edits
   so you can distinguish existing work from the agent's contribution.
2. Review the changed behavior and its requirement. Inspect working and staged
   differences, including relevant untracked paths. Do not sweep unrelated files
   into a commit to make the status look clean.
3. Select the intended content deliberately. If the file changes after staging,
   review the new content before staging it again.
4. Associate test evidence with the exact candidate contents and commit. A clean
   checkout of that commit is one way to verify the handoff independently;
   this lab does not run the application's tests for you.
5. Treat remote publication and deployment as separate authorized operations.
   A pull request is a hosting-service review mechanism, not a Git commit.

You do not need a clever command sequence to explain this. Say which bytes were
changed, which were recorded, what evidence covers those bytes and what remains
outside the handoff. The [Claude Code setup lesson](/app/academy?course=claude-code&lesson=setup&view=focus)
applies that habit to the offline ticket-triage project. The supplied project
intentionally starts with failing tests; this introduction does not erase them.

## 6. Keep your evidence and continue

Before moving on, explain why the default run can have a successful working-copy
check and a different committed value. Draw the working tree, index and commit,
then label the two comparisons made by the diff commands. Ask a teammate to
identify the intended snapshot without watching your terminal session.

Continue to the [client delivery workbook](/training/guides/workbook) to connect
technical evidence to a request, owner and acceptance decision. If the terminal
itself is unfamiliar, start with [your first command](/training/guides/terminal).

Primary references checked 21 October 2026. This explanation, fictional example
and program are original LockedIn Labs instruction.

- [Git: what it is and how its snapshots work](https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F)
- [Git add: selecting content for the index](https://git-scm.com/docs/git-add)
- [Git diff: selecting the two versions to compare](https://git-scm.com/docs/git-diff)
- [Git commit: recording the index](https://git-scm.com/docs/git-commit)
- [Git status: staged, unstaged and untracked paths](https://git-scm.com/docs/git-status)
- [Git branch: named references](https://git-scm.com/docs/git-branch)
