Skip to main content
PProfileMatch

Git Basics: Working Tree, Staging Area and Your First Commits

DevOpsLesson 1 of 46 min read

What you'll learn

  • Explain the working tree, the index and the commit history, and what moves between them
  • Read the two-letter codes in git status -s
  • Stage part of your work and commit only that part
  • Ignore scratch files and write a useful commit message

On this page

  1. Three areas, not two
  2. Tell Git who you are, once
  3. Your first repository and first commit
  4. The same file in two states at once
  5. Keeping scratch files out, and what a commit really is
  6. Commit messages that help
  7. Common mistakes
  8. How this shows up in projects and interviews

Stage a change, edit the same file again, then run git status -s. Instead of one status letter you get two: MM. That small oddity is the best door into Git, because it only makes sense once you know that Git keeps your work in three different places at the same time. This lesson follows a bakery's price list through those three places, one command at a time, and every transcript below is real output from a throwaway repository.

Three areas, not two

Most beginners picture a file as either saved or not saved. Git has a middle step. The working tree is the folder you edit. The index, also called the staging area, is a draft of your next commit. The repository holds the permanent history of commits. You choose, change by change, what moves from one area to the next.

Where changes live
working tree --git add--> index --git commit--> history
 (your files)             (next commit)          (commits)
      ^                        |
      +------ git restore -----+
CommandWhat it movesTypical use
git add <file>Working tree to indexPick what goes into the next commit
git commitIndex to historyRecord the draft as a permanent snapshot
git restore <file>Index to working treeThrow away edits you have not staged
git restore --staged <file>History to indexUnstage a file but keep your edits
git diffCompares working tree with indexSee what you have not staged yet
git diff --stagedCompares index with last commitSee exactly what the next commit will contain

Tell Git who you are, once

Every commit records an author. Without a name and email Git refuses to commit and prints Author identity unknown followed by *** Please tell me who you are. Set both once per machine, and choose the default branch name for new repositories while you are at it. The values below are the practice identity used for this lesson's transcripts.

One-time setup
git config --global user.name "Asha Rao"
git config --global user.email "asha@example.test"
git config --global init.defaultBranch main

Your first repository and first commit

Create a folder called bakery, open a terminal in it, and follow along. Shown paths are shortened and your commit hashes will differ, since a hash depends on content, author and time. The -s flag asks for the short status format: two columns, where the left column compares the index to the last commit and the right column compares the working tree to the index.

Terminal
$ git init
Initialized empty Git repository in /home/asha/bakery/.git/

$ printf 'Sourdough loaf, 6.50\nCroissant, 3.20\n' > prices.txt

$ git status -s
?? prices.txt

$ git add prices.txt

$ git status -s
A  prices.txt

$ git commit -m "Add first price list"
[main (root-commit) c2b541a] Add first price list
 1 file changed, 2 insertions(+)
 create mode 100644 prices.txt

Read the codes: ?? means Git sees the file but is not tracking it. A (an A, then a space) means added to the index. After the commit, the status is empty because all three areas agree. A message such as [main (root-commit) ...] marks the very first commit, which has no parent.

The same file in two states at once

Now the puzzle from the top. We stage the rye bun, then append a to-do line without staging it. The file exists in three versions: the last commit, the index and the working tree.

Terminal
$ echo 'Rye bun, 2.10' >> prices.txt

$ git status -s
 M prices.txt

$ git add prices.txt

$ echo 'TODO: ask about cardamom buns' >> prices.txt

$ git status -s
MM prices.txt

$ git diff
diff --git a/prices.txt b/prices.txt
index 7201cc5..dcd4a8b 100644
--- a/prices.txt
+++ b/prices.txt
@@ -1,3 +1,4 @@
 Sourdough loaf, 6.50
 Croissant, 3.20
 Rye bun, 2.10
+TODO: ask about cardamom buns

$ git diff --staged
diff --git a/prices.txt b/prices.txt
index 775a966..7201cc5 100644
--- a/prices.txt
+++ b/prices.txt
@@ -1,2 +1,3 @@
 Sourdough loaf, 6.50
 Croissant, 3.20
+Rye bun, 2.10

$ git commit -m "Add rye bun"
[main 374b16b] Add rye bun
 1 file changed, 1 insertion(+)

$ git status -s
 M prices.txt

$ git log --oneline
374b16b Add rye bun
c2b541a Add first price list

MM reads left to right: the index differs from the last commit, and the working tree differs from the index. The two diffs prove it. git diff shows only the TODO line, while git diff --staged shows only the rye bun. The commit took what was in the index, so after it the status is M: the to-do is still an unstaged edit. This is the habit that makes Git worth learning. You can keep a scratch note in a file and commit only the part that is ready.

Keeping scratch files out, and what a commit really is

A file named in .gitignore is invisible to git status and git add . as long as it is untracked. Below, the scratch note is ignored while the new .gitignore file itself is committed so teammates share the rule. The last command prints the raw commit object.

Terminal
$ echo 'scratch.tmp' > .gitignore

$ echo 'half-written supplier note' > scratch.tmp

$ git status -s
 M prices.txt
?? .gitignore

$ git add .gitignore prices.txt

$ git commit -m "Add cardamom TODO and ignore scratch files"
[main 7ab492e] Add cardamom TODO and ignore scratch files
 2 files changed, 2 insertions(+)
 create mode 100644 .gitignore

$ git status -s

$ git ls-files
.gitignore
prices.txt

$ git cat-file -p HEAD
tree 8b80563a71569f75c2b8c24d5a5875c8a100ad6d
parent 374b16b1e0ec1324351851e4d785da67056e4685
author Asha Rao <asha@example.test> 1791170752 +0530
committer Asha Rao <asha@example.test> 1791170752 +0530

Add cardamom TODO and ignore scratch files

A commit is not a diff. It points to a tree, a full snapshot of every tracked file, and to its parent commit, plus the author, committer, timestamp and message. The hash of all that is the commit's name. Because each commit names its parent, history is a chain, and changing any old commit would change every hash after it. That is why rewriting shared history is a big deal, as the last lesson shows. Git stores snapshots efficiently, so a file that did not change is not copied again.

Commit messages that help

  • Start with a short summary in the imperative mood, about 50 characters: Add rye bun to price list, not added stuff.
  • Describe one logical change per commit. If you need the word and, consider two commits.
  • Use the body, after a blank line, to explain why, because the diff already shows what.
  • Do not commit half-working states to a shared branch just to save progress; use a branch for that.

Common mistakes

  • Committing before setting your identity. Symptom: Author identity unknown and fatal: unable to auto-detect email address; run the two git config commands above and repeat the commit.
  • Editing after `git add`, then committing. Symptom: the commit lacks your last edit and git status still shows M on the file. Stage again before committing.
  • Using `git add .` blindly. Symptom: .env files, build output or a huge binary slip into a commit; if it is pushed, a host may reject files over its size limit (GitHub refuses files above 100 MB) or, worse, publish a secret. Check git status first and add a .gitignore early.
  • Expecting `.gitignore` to hide an already-tracked file. Symptom: the file keeps appearing as modified. Ignore rules only affect untracked files; run git rm --cached <file> to stop tracking it.

How this shows up in projects and interviews

On a team, your commit history is read by reviewers and by the person debugging at midnight in six months, so small commits with clear messages are a real skill. Interviewers often ask what the difference is between git add and git commit, or between the working tree and the staging area; the three-area picture answers both in a sentence. If you are building a portfolio for job applications, a repository with readable history tells a reviewer more about you than a single giant first commit.

Try it yourself

Create a new folder with a file called menu.txt holding three lines about a food truck. Make two commits: first the file, then an added fourth line. Then add a fifth line without staging it and a sixth line you do stage, and use git status -s, git diff and git diff --staged to explain what the next commit will contain before you run it.

Show a hint

Stage the sixth line by editing, running git add, and then editing the file again to add the fifth. You should see MM in the status.

Key takeaways

  • Git has three areas: working tree, index (staging area) and committed history.
  • git add copies to the index and git commit records the index, so a file can be both staged and modified.
  • git diff compares working tree with index; git diff --staged compares index with the last commit.
  • A commit is a snapshot with a parent pointer, which is what makes history a chain.
  • Set your identity once, add a .gitignore early and keep commits small and described.

Test yourself

A trick from this lesson's ride: Status has two columns: left is staged, right is not-yet-staged. Four more tricks like it wait along the route, each followed by a question.

Ride this lesson

Frequently asked questions

What is the difference between git add and git commit?

git add copies the current state of a file into the index, which is a draft of your next commit. git commit then records whatever is in the index as a permanent snapshot in history. Edits you make after git add are not included unless you add them again.

What is the staging area in Git?

The staging area, also called the index, sits between your working folder and your commit history. It holds exactly what will go into the next commit, so you can commit part of your changes instead of everything. git diff --staged shows its contents compared with the last commit.

How do I see what I changed before committing in Git?

Use git status -s for a short list of changed files and git diff to see unstaged changes. After git add, use git diff --staged to see exactly what the next commit will contain. Running both before each commit catches stray edits and debug lines.