# Your first repository

> Configure Git, create a repository, and use status, add, commit, log and diff to record and inspect clean, focused history.

Git & Collaboration roadmap · Core concepts · 18 min read · https://levelcode.org/roadmaps/git/first-repository

This lesson turns the mental model from the previous lesson into muscle memory. You will configure Git once, create a project, and practise the loop you will run hundreds of times a week: edit, inspect, stage, commit. Along the way you will learn to read `git status`, `git diff` and `git log` fluently, and to keep junk out of your history with `.gitignore`.

## One-time setup

Every commit records an author name and email, so tell Git who you are. The `--global` flag writes to `~/.gitconfig`, which applies to every repository on your machine:

```bash
git config --global user.name "Ada Lovelace"
git config --global user.email "ada@example.com"
git config --global init.defaultBranch main
git config --global core.editor "code --wait"
```

- `init.defaultBranch main` makes new repositories start on a branch called `main`, the modern convention. Without it, Git creates `master` and prints a hint about it.
- `core.editor` is the editor Git opens when it needs a longer message. `code --wait` uses VS Code; `nano` or `vim` work too. The `--wait` matters: Git must wait until you close the file.

Check what Git will use with `git config user.email`. If you use a hosting service, use the same email address you registered there so your commits are linked to your account.

> [!TIP]
> You can override any setting for one repository by running `git config` without `--global` inside it. A common use is a work email for work projects and a personal email everywhere else.

## Creating a repository

Turn any folder into a repository with `git init`:

```bash
mkdir notes-app && cd notes-app
git init
```

```text
Initialized empty Git repository in /home/ada/notes-app/.git/
```

That created the hidden `.git` directory and nothing else. Your files are untouched, and nothing is tracked yet. To stop using Git in a folder you would simply delete `.git` (which also deletes all history, so think twice).

Two rules keep you out of trouble. Run `git init` at the **root** of a project, not in your home directory, and never create a repository inside another repository's folder by accident. If `git status` reports files from all over your disk, you initialised in the wrong place.

## Reading git status

`git status` is the command you will run most. Create a couple of files and ask:

```bash
echo "<h1>Notes</h1>" > index.html
echo "debug" > debug.log
git status
```

```text
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	debug.log
	index.html

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

Every file in your project is in one of these states:

| State | Meaning | Shown under |
| --- | --- | --- |
| Untracked | Git has never been told about it | Untracked files |
| Staged | Its current content is in the index, ready to commit | Changes to be committed |
| Modified | Tracked, but the working tree differs from the index | Changes not staged for commit |
| Unmodified | Working tree, index and last commit agree | (not shown) |

Once you are comfortable, the short format is quicker to scan. The left column is the index, the right column is the working tree:

```bash
git status --short
```

```text
MM index.html
?? debug.log
```

`MM` means "modified and staged, then modified again"; `??` means untracked; `A ` means a newly added file is staged.

## Ignoring files

Logs, build output, dependency folders and secrets should never be committed. List patterns in a `.gitignore` file at the project root:

```text
# Dependencies and build output
node_modules/
dist/

# Logs and local environment
*.log
.env
```

A trailing `/` matches directories only, `*` matches anything except `/`, and a leading `!` re-includes something an earlier pattern excluded. Commit `.gitignore` itself so everyone on the team shares it. When you cannot work out why a file is being ignored, ask Git:

```bash
git check-ignore -v node_modules/react/index.js
```

```text
.gitignore:2:node_modules/	node_modules/react/index.js
```

> [!WARNING]
> `.gitignore` only affects **untracked** files. If you already committed `.env`, adding it to `.gitignore` does nothing. Run `git rm --cached .env` to stop tracking it (the file stays on disk), commit that, and rotate any secret it contained — it is still in the history.

## Staging and committing

Stage exactly what you want in the next commit with `git add`, then record it with `git commit`:

```bash
git add index.html .gitignore
git commit -m "Create landing page"
```

```text
[main (root-commit) 834a87f] Create landing page
 2 files changed, 8 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 index.html
```

The output tells you the branch, the new commit's short ID, and a summary of what changed. `root-commit` appears only for the very first commit.

Useful forms of `git add`:

- `git add path/to/file` stages one file; `git add src/` stages a directory.
- `git add .` stages everything in the current directory that is not ignored. Convenient, but run `git status` first so you know what "everything" is.
- `git add -p` walks through your changes hunk by hunk and asks about each one. This is how you split unrelated edits into separate commits:

```text
@@ -1,4 +1,4 @@
-<h1>Notes</h1>
+<h1>My Notes</h1>
 <p>Write things down.</p>
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?
```

Answer `y` to stage, `n` to skip, `s` to split a large hunk (when it can be split), `q` to quit, or `?` for help.

There is also a shortcut, `git commit -a -m "..."` (or `-am`), which stages every change to **already tracked** files and commits in one step. It never picks up new, untracked files, which is a classic way to forget a file.

### Writing a good message

Run `git commit` without `-m` and Git opens your editor so you can write a proper message:

```text
Add search box to the notes list

Users with more than a few dozen notes could not find anything.
Filter the list client-side as the user types; server-side search
can come later if lists get large.
```

The conventions are simple: a summary line of about 50 characters written as a command ("Add", "Fix", not "Added" or "Fixes"), a blank line, then a body explaining **why** the change was made. The diff already shows *what* changed. Teams often add further conventions on top, which you will meet in the workflows lesson.

## Inspecting changes with git diff

`git diff` compares two of the three areas. Which two depends on the flags:

| Command | Compares | Answers |
| --- | --- | --- |
| `git diff` | index → working tree | What have I changed but not staged? |
| `git diff --staged` | last commit → index | What will my next commit contain? |
| `git diff HEAD` | last commit → working tree | Everything I have changed since the last commit |
| `git diff A B` | commit A → commit B | How do two snapshots differ? |

After editing `index.html`:

```bash
git diff
```

```text
diff --git a/index.html b/index.html
index 1cc1758..0618f11 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
 <h1>Notes</h1>
+<p>Write things down.</p>
```

Read it like this: `---` is the old version and `+++` is the new one. The hunk header `@@ -1 +1,2 @@` says "starting at line 1, one old line became two new lines". Lines starting with `+` were added, `-` removed, and a space means unchanged context.

A frequent surprise: you stage a file and `git diff` suddenly prints nothing. That is correct, because the working tree now matches the index. Use `git diff --staged` to review staged work, and make that review a habit before every commit. Add `--stat` to any diff for a one-line-per-file summary.

## Exploring history with git log

`git log` lists commits, newest first:

```bash
git log
```

```text
commit 01673bb42e0d2093fda140b06d104e5a48e0acd3
Author: Ada Lovelace <ada@example.com>
Date:   Sun Oct 11 08:03:40 2026 +0000

    Add tagline

commit 834a87ff6216c6f107488c68f879fc2cd67201dd
Author: Ada Lovelace <ada@example.com>
Date:   Sun Oct 11 08:01:12 2026 +0000

    Create landing page
```

The full format gets long quickly. These options cover most needs:

```bash
git log --oneline              # one line per commit
git log --oneline --graph --all  # draw branches (useful from the next lesson on)
git log -3                     # only the last three commits
git log --stat                 # which files each commit touched
git log -p                     # the full diff of each commit
git log --author="Ada"         # filter by author
git log --since="2 weeks ago"  # filter by date
git log -- index.html          # only commits that changed this file
```

```text
01673bb Add tagline
834a87f Create landing page
```

To look at one commit in detail, use `git show`, which prints its metadata followed by its diff:

```bash
git show --stat HEAD
```

```text
commit 01673bb42e0d2093fda140b06d104e5a48e0acd3
Author: Ada Lovelace <ada@example.com>
Date:   Sun Oct 11 08:03:40 2026 +0000

    Add tagline

 index.html | 1 +
 1 file changed, 1 insertion(+)
```

When the output is longer than your screen, Git shows it in a pager: use the arrow keys or space to scroll and `q` to quit.

Finally, when you delete or rename tracked files, use `git rm old.txt` and `git mv old.txt new.txt`. They change the file and stage the change in one step. (Deleting or renaming with your file manager and then running `git add -A` achieves the same result.)

## Practice

Build a tiny project with a deliberately messy working session, then turn it into clean history:

1. Create a repository `journal`, add a `.gitignore` that ignores `*.tmp`, and an `entries.txt` with nine lines (the first is `Monday: lernt Git`). Make your first commit with both files.
2. Create `scratch.tmp`, and confirm `git status` does not list it.
3. In `entries.txt`, fix a typo on line 1 **and** add a new line at the end (with seven or more lines between the edits, Git shows them as separate hunks). Commit these as two separate commits using `git add -p`.
4. Show the last two commits as one line each, then show the full diff of only the first of them.

**Solution**

```bash
mkdir journal && cd journal
git init
printf '*.tmp\n' > .gitignore
printf 'Monday: lernt Git\n' > entries.txt
for i in 2 3 4 5 6 7 8 9; do echo "line $i" >> entries.txt; done
git add .gitignore entries.txt
git commit -m "Start journal"

touch scratch.tmp
git status            # scratch.tmp is not listed: it is ignored

sed -i 's/lernt/learnt/' entries.txt      # macOS: sed -i '' 's/lernt/learnt/' entries.txt
echo "Tuesday: staged with -p" >> entries.txt

git add -p entries.txt   # answer y for the typo hunk, n for the new line
git diff --staged        # check: only the typo fix
git commit -m "Fix typo in Monday entry"

git add entries.txt
git commit -m "Add Tuesday entry"

git log --oneline -2
git show HEAD~1          # the typo-fix commit with its diff
```

If both edits landed in one hunk, press `s` at the prompt to split it; if Git cannot split it, use `e` to edit the hunk, or simply move the edits further apart. The key check is `git diff --staged` before each commit.

## Key takeaways

- Set `user.name`, `user.email` and `init.defaultBranch` once with `git config --global`.
- `git status` tells you which files are untracked, staged or modified; check it before every commit.
- `git add` (especially `git add -p`) lets you build focused commits; `git commit -a` never includes untracked files.
- `git diff` shows unstaged changes, `git diff --staged` shows what you are about to commit.
- `.gitignore` only affects untracked files; untrack an already committed file with `git rm --cached`.
- `git log` with `--oneline`, `--stat`, `-p` and path filters, plus `git show`, lets you explore any part of history.

## Quiz

1. You staged all your changes with git add, and now git diff prints nothing. What is happening?

   A. The staging failed and your changes were lost
   B. git diff compares the working tree with the index, and they now match; use git diff --staged
   C. git diff only works after a commit
   D. Your editor has not saved the files

   **Answer: B.** Plain git diff shows changes not yet staged. After staging everything, the working tree equals the index, so the diff is empty. git diff --staged compares the last commit with the index.

2. You created a new file report.py and edited an existing tracked file app.py, then ran git commit -am "Add report". What does the commit contain?

   A. Only the change to app.py
   B. Both files
   C. Only report.py
   D. Nothing, because -a requires git add first

   **Answer: A.** -a stages modifications and deletions of tracked files only. report.py is untracked, so it is left out until you git add it.

3. config.env was committed last week. You add config.env to .gitignore. What happens on your next commit?

   A. Git deletes config.env from history
   B. Git deletes config.env from your disk
   C. Git refuses to commit until the file is removed
   D. Nothing changes: Git keeps tracking the file because .gitignore only affects untracked files

   **Answer: D.** Ignore rules apply only to untracked files. You must run git rm --cached config.env and commit to stop tracking it; the old content remains in history.

4. Which command lists only the commits that changed the file styles.css, one line per commit?

   A. git show styles.css --oneline
   B. git diff --oneline styles.css
   C. git log --oneline -- styles.css
   D. git status styles.css

   **Answer: C.** git log accepts a path after -- to limit history to commits touching that path, and --oneline prints each commit compactly.
