"git add and git commit: How to Save Changes the Right Way"
Saving changes in Git is a two-step process: git add puts the changes you want into the index (staging area), and git commit -m "message" records them in history as a single snapshot. git status shows what has changed and what is already staged. You can fix your last commit with git commit --amend.
Git has no "Save" button. Changes are recorded in two steps: first git add picks which edits go into the next snapshot, then git commit writes that snapshot to history. The area in between is called the index, or staging area. Many people go years without really understanding it — and it's exactly what makes commits meaningful.
The three states of a file: working tree, staging area, history
Every change travels through three areas:
- Working directory — you edit the file in your editor.
- Index (stage) —
git addputs the current version of the file into the draft of the next commit. - History —
git committurns the contents of the index into a commit with an author, date, and message.
A handy analogy is shipping a package. The working directory is a room full of stuff, the index is an open box where you put whatever you want to send, and the commit is a sealed, labeled package. You decide what goes in the box, and you don't have to ship everything at once.
Why the staging area exists
Imagine that within an hour you fixed an auth bug, corrected a typo in the README, and started a new feature. Without the index, all of that would end up in one "misc changes" commit. With the index, you make three separate commits, each easy to understand, review, and revert if needed without touching the rest.
git status: what's changed and what's staged
It's a good habit to run git status before every add and commit. It's the safest command in Git — it doesn't change anything.
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/auth.js
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/profile.js
- Changes to be committed — already in the index, will go into the commit;
- Changes not staged — modified but not added;
- Untracked files — new files Git doesn't know about yet.
The short format is handier when there are lots of files:
$ git status -s
M src/auth.js
M README.md
?? src/profile.js
The first column is the state in the index, the second is the state in the working directory. M on the left means "modified and staged", on the right — "modified, not staged", and ?? means untracked.
git add: how to stage changes
| Command | What it stages |
|---|---|
git add file.js | A specific file |
git add src/ | Everything changed in a folder |
git add . | All changes in the current folder and below, including new and deleted files |
git add -A | All changes across the whole repository |
git add -u | Only already-tracked files, leaves new files alone |
git add -p | Interactively, chunk by chunk (hunks) |
git add '*.css' | Files matching a pattern |
git stage is a synonym for git add — it's the same command, which is where the terms "stage" and "staging" come from.
git add -p: stage part of a file
This is the most underrated trick. If one file contains two unrelated edits mixed together, git add -p shows each chunk and asks whether to stage it:
$ git add -p src/auth.js
@@ -12,7 +12,7 @@ function login(user) {
- if (user.password = input) {
+ if (user.password === input) {
(1/2) Stage this hunk [y,n,q,a,d,s,e,?]?
y — stage it, n — skip, s — split the hunk into smaller ones, q — quit. That's how one file turns into two clean commits.
How to check exactly what will be committed
git diff # changes NOT yet staged
git diff --staged # what's already staged and will go into the commit
A common source of confusion: after git add, a plain git diff shows nothing. That doesn't mean your changes are gone — they're in the index; use git diff --staged (a synonym for --cached).
How to unstage a file
git restore --staged file.js
Your edits to the file stay put; it simply stops being staged for the commit. Older versions of Git used git reset HEAD file.js for this — it still works.
git commit: recording your changes
git commit -m "Fix password check on login"
[main 3f2a9c1] Fix password check on login
1 file changed, 1 insertion(+), 1 deletion(-)
The output shows the branch, the short hash of the new commit, and stats. Only the contents of the index go into the commit. If you edited the file again after git add, those new edits won't be included until you add them again.
Useful git commit flags
| Command | What it does |
|---|---|
git commit | Opens an editor for a multi-line message |
git commit -m "..." | Message right in the command |
git commit -am "..." | Stages all modified tracked files and commits; new files are not included |
git commit -v | Shows the diff in the editor while you write the message |
git commit --amend | Rewrites the last commit |
git commit --allow-empty -m "..." | An empty commit, e.g. to re-trigger CI |
If your default editor is Vim and you're stuck in it: type your message, press Esc, then type :wq and hit Enter. To change the editor: git config --global core.editor "code --wait" or nano.
How to write a good commit message
Commit messages get read six months later, when someone is tracking down where a bug came from. A message should answer "what and why", not "what I did today".
- keep the first line to 50–72 characters, summarizing the change;
- use the imperative mood or a neutral tone: "Add email validation", "Fix login redirect";
- if needed, add a blank line followed by details: why it was done this way, any limitations;
- one commit — one logical change.
Bad: fix, changes, wip, asdf. Good: Fix cart crash when the product list is empty. Many teams use the Conventional Commits format: feat: ..., fix: ..., docs: ....
git commit --amend: how to fix your last commit
Typo in the message, or forgot to add a file? No need for a new "forgot a file" commit. That's what amend is for.
Change the commit message
git commit --amend -m "Fix password check on login"
Add a forgotten file
git add forgotten.js
git commit --amend --no-edit
The --no-edit flag keeps the existing message.
What you need to know about amend
--amend doesn't "edit" a commit — it creates a new one with a different hash and replaces the old one. As long as the commit exists only on your machine, that's safe. If you've already run git push, a regular push after amending will be rejected, and you'll need git push --force-with-lease. Don't do this on shared branches where others are working: you'd be rewriting history they're already building on.
If you amended by mistake, you can find the old commit in git reflog and go back to it with git reset --soft HEAD@{1}.
Common errors
"nothing to commit, working tree clean"
There are no changes — everything is already committed, or you're editing a file in a different folder. Check git status and your project path.
"no changes added to commit"
Files are modified but not staged. Run git add, or use git commit -am for already-tracked files.
"Please tell me who you are"
Your name and email aren't set. Run git config --global user.name "Your Name" and git config --global user.email "you@example.com" once.
Committed something you shouldn't have
If the commit hasn't been pushed yet, undo it while keeping the changes staged: git reset --soft HEAD~1. Then unstage the extra files with git restore --staged and commit again. If a secret ended up in the commit, treat it as compromised and rotate it.
git add . staged junk
Set up .gitignore in advance and check git status before committing. A file that's already tracked stops being tracked only after git rm --cached file.
What a commit really is
A commit doesn't store a diff from the previous version but a snapshot of the whole project: a reference to the file tree, a reference to the parent commit, the author, date, and message. Unchanged files aren't copied — Git points to objects it has already stored, so history takes up little space.
The commit hash (for example, 3f2a9c1) is computed from all of that content. Change even a single letter in the message and you get a different hash. That's why --amend and rebase create new commits instead of editing old ones, and why rewritten history conflicts with your teammates' copies.
Here's a compact way to view history:
git log --oneline --graph -10
Each line is a commit; if the messages in your history make sense without opening the diff, you're doing it right.
Your everyday workflow
git status # what has changed
git diff # review the edits
git add -p # pick what you need
git diff --staged # check what will be committed
git commit -m "Clear description"
git log --oneline -5 # make sure the commit is there
git push # send it to the server
A commit saves changes only locally. To get them onto GitHub or another server, you need git push.
Summary
git add builds the next snapshot in the index, git status and git diff --staged show what will go into it, and git commit writes the snapshot to history. Make small, logical commits with clear messages, and use git commit --amend for small fixes to your last commit — before you push. The index and the two-step add → commit flow are covered hands-on in the Git course.
FAQ
How often should I commit?
Every time you finish one logical change and the project is in a working state. Many small commits are better than one huge commit at the end of the day.
Is there a git save command?
No, there's no such command. Saving is git add plus git commit, and you can temporarily set aside unfinished work with git stash.
How do I see what was in a commit?
git show hash displays the author, message, and all changes in that commit. Without a hash, it shows the latest commit.
What's the difference between git add . and git add -A?
In modern versions of Git, both stage new, modified, and deleted files. The difference is scope: git add . works on the current folder and its subfolders, while git add -A covers the entire repository.
Can I change an older commit, not just the last one?
Yes, with an interactive rebase: git rebase -i parent-hash, then mark the commit you need as edit or reword. Only do this with commits that haven't been pushed to a shared branch yet.
This topic is covered in depth with practice in the course “Git from Scratch to Confident Developer”.
Go to course