Most of the confusion around git commit comes from assuming it saves your files. It saves the staging area, which is a different thing, and no remote is contacted at any point.
The command reads the index, not your working files, so even a modified file must be staged before it is committed (Git 2.55.0 manual, June 2026). The -a flag stages tracked changes automatically but skips new files.
Each commit stores a full snapshot of the project tree, the author and committer, a timestamp and the message. The current branch then moves forward to point at it.
What Does git commit Do?

It records the staged changes as a new commit in the local repository history. Nothing leaves your machine, and your working files stay exactly as they were.
The git-commit manual (Git 2.55.0, June 2026) describes the result as a new commit holding the current contents of the index plus a log message. That commit becomes a direct child of HEAD, and the current branch moves to it. Unstaged edits, untracked files and every remote are ignored along the way.
A commit is a snapshot, not a diff. Git keeps the complete tree state and calculates differences only when a command like git show asks for them.
Because each commit lives in the local Git repository, you can make a dozen of them offline and share them later in one push.
Where git commit Gets Its Content: The Staging Area

By default, git commit reads the index and never looks at the working directory. An edit that was never staged stays out of the commit, even in a tracked file.
That index, usually called the staging area, is what lets one messy working directory become several clean commits.
| Method | What gets committed | Catch |
|---|---|---|
| git add, then git commit | Exactly what is staged | Modified files still need git add |
| git commit -a | All tracked modified and deleted files | New files are skipped |
| git commit with paths | Current content of the listed paths | Ignores changes staged for other paths |
| git commit -p | Hunks you pick, plus the index | Interactive prompts |
What -a Skips
git commit -a stages modified and deleted files. Nothing else.
A modified tracked file goes in, and so does a deleted one. A brand-new file Git has never seen stays out and still shows as untracked, which catches people more often than you’d think.
Reviewing the staged content with git diff –cached shows the lines that will be committed.
git commit –dry-run is a decent sanity check too. It lists the paths that will be committed, then the paths with local changes that will be left out, and the untracked ones after that.
Committing Specific Paths
Give git commit one or more paths and it records the current content of those files, ignoring whatever is staged for other paths. The staged changes are not lost. They stay staged for the next commit.
The manual’s example has hello.c and hello.h staged, with Makefile edited but not staged. Running git commit Makefile records only the Makefile change, and a plain git commit afterward records the two staged files.
There is one documented exception. During a merge resolution, git commit refuses pathnames, because the merge must be recorded as a single commit.
What git commit Writes Into the Repository

Under the hood, git commit writes tree objects built from the index, then one commit object that points to the top-level tree and to its parent. The file contents are already stored as blobs, because git add wrote them at staging time.
- Git collects the message from -m, -F or the editor.
- It writes tree objects that record the staged directory structure.
- It builds the commit object: top-level tree, parent commit, author, committer, timestamps and message.
- It hashes the content with a header, compresses it and stores it under .git/objects.
- It moves the current branch to the new hash.
The commit object format is plain: a tree line, parent lines if any, author and committer lines, a blank line, then the message (Pro Git, Git Objects chapter). Identity comes from user.name and user.email unless environment variables such as GIT\_AUTHOR\_NAME override them.
Run git cat-file -p HEAD right after committing to read the raw object yourself.
Object names run 40 hexadecimal digits with SHA-1 and 64 with SHA-256 (Git hash-function-transition document, 2.55.0). A loose object file sits in a directory named after the first 2 characters, with the remaining 38 as the file name (Pro Git).
That 40-digit value is the commit hash, the ID every later command uses to point at this exact commit.
Git v2.13.0 moved to a hardened SHA-1 implementation after the SHAttered collision was demonstrated on 23 February 2017, per the same document. SHA-256 repositories are an extension enabled per repository with extensions.objectFormat, and older Git versions refuse to read them.
How git commit Moves the Branch and Stays Local

The new commit becomes a child of HEAD, and the branch HEAD points to advances to it. No network call happens at any point.
A branch is a small file under .git/refs/heads that holds a commit hash. HEAD is normally a symbolic reference to that branch, so moving the branch moves HEAD with it (Pro Git).
With a detached HEAD, there is no branch to move. The commit hangs off HEAD alone, and creating a branch is what keeps it easy to find.
All git commit does is write objects and move a local branch. Sending those objects to a remote, and updating the branch over there, belongs to git push.
Pushing is its own step with its own failure modes, covered in what git push does. Remote-tracking refs such as refs/remotes/origin/master record where the server was at last contact, and git commit never writes to them.
Which git commit Options Change the Result
For most commits, -m and sometimes -a are all you need. –amend deserves more care and belongs only on commits nobody else has pulled. The other flags serve narrower jobs.
| Flag | What it does | Watch out for |
|---|---|---|
| -m | Sets the message inline, repeated flags become separate paragraphs | Cannot combine with -c, -C or -F |
| -a | Stages modified and deleted tracked files, then commits | New files are skipped |
| –amend | Replaces the branch tip with a new commit, same parents and author | Rewrites history if already pushed |
| –allow-empty | Permits a commit with the same tree as its parent | Meant for scripts, per the manual |
| –no-verify | Skips the pre-commit and commit-msg hooks | Bypasses your team’s checks |
Amend Rewrites History
–amend does not edit a commit. It creates a new one with the same parents and author, so the hash changes.
The manual flags the cost. Amending a commit that is already published means everyone holding the old one has to deal with the rewrite. So amend before the push, and after it, add a follow-up commit instead.
git commit –amend –no-edit keeps the old message and only swaps in the newly staged changes.
Fixup Commits for Later Cleanup
A fixup commit is a small correction aimed at an earlier commit, made now and folded in later.
Pass –fixup a commit reference and it creates a “fixup!” commit that changes content but keeps the target’s message. –fixup=reword goes the other way and creates an “amend!” commit that changes only the message.
A later rebase with –autosquash folds the correction into its target. Any extra -m text on a plain fixup commit is thrown away at that point.
Which Hooks Run When You Commit
The hooks around git commit are pre-commit, prepare-commit-msg, commit-msg, post-commit and post-rewrite (githooks manual, Git 2.54.0, April 2026). The first three can stop the commit. post-commit cannot.
| Hook | Runs | What it can do |
|---|---|---|
| pre-commit | Before the message is requested | Abort before any commit exists |
| prepare-commit-msg | After the default message is built, before the editor opens | Edit the message file, abort on non-zero exit |
| commit-msg | After the message is final | Validate or edit the message, abort on non-zero exit |
| post-commit | After the commit is made | Notification only |
| post-rewrite | After –amend rewrites a commit | Receives old and new object names on stdin |
prepare-commit-msg ignores –no-verify, so it keeps running even when the other two are skipped.
It gets one to three arguments, and the second names where the message came from (message, template, merge, squash, or commit for -c, -C and –amend). The sample pre-commit hook, once enabled, blocks non-ASCII filenames and lines with trailing whitespace.
When no editor will open, hooks run with GIT\_EDITOR set to a colon, so scripts can tell. Also, a hook file without the executable bit is ignored, and core.hooksPath moves the whole hooks directory.
A pre-commit hook is the natural home for fast linting and formatting checks.
Because –no-verify skips two of these hooks, repeat the same checks in a continuous integration run, where a local flag cannot bypass them.
How to Write a Commit Message

Write a subject of about 50 characters, leave one blank line, then explain why the change was made. Git treats everything up to the first blank line as the commit title, and git format-patch turns that title into an email subject (git-commit manual, Git 2.55.0).
The git-commit manual puts the subject at no more than 50 characters, while Pro Git says “about 50”. Pro Git also suggests wrapping the body at about 72 characters. The blank line matters because tools like rebase get confused when subject and body run together (Pro Git).
Both sources frame these as guidelines. Git enforces neither limit.
Write in the imperative, so “Fix bug”, not “Fixed bug” or “Fixes bug”. Pro Git notes this matches the messages Git generates for merges and reverts.
The Git project asks the body to state the motivation for the change and contrast the new behavior with the old.
Conventional Commits 1.0.0 adds structure on top: a type, an optional scope, a description, then an optional body and footers. It is opt-in, and plain git commit accepts any text.
A fix commit patches a bug and correlates with PATCH. A feat commit adds a feature and correlates with MINOR. For MAJOR, you use a BREAKING CHANGE footer, or a ! after the type or scope.
These types map onto semantic versioning, which is why release tools can read them. The spec’s own example reads feat(parser): add ability to parse arrays.
The spec mandates only feat and fix, and its FAQ says a change that fits more than one type should be split into multiple commits.
How to Undo a Commit

Amend or reset a commit that has not been pushed. Revert one that has.
| Method | Effect on history | Safe after push | Your changes |
|---|---|---|---|
| git commit –amend | Replaces the tip with a new commit | No | Folded into the new commit |
| git reset –soft HEAD~1 | Moves the branch back one commit | No | Stay staged |
| git reset –hard HEAD~1 | Moves the branch back one commit | No | Discarded |
| git revert | Adds an inverse commit | Yes | History stays intact |
Reset Modes
git reset moves the branch tip to another commit, and the mode decides what happens to the index and the working directory. The default is –mixed (git-reset manual, Git 2.54.0).
With –soft, the index and working tree are left alone, so the undone commit’s changes stay staged. –mixed resets the index but leaves your files as they are, which means nothing stays staged. –hard overwrites files to match the target, and it may overwrite untracked files too.
Rewinding with git reset is for private work only. The manual says not to run –hard on commits already given to somebody else.
Before any reset, Git saves the old tip in ORIG\_HEAD. A reset –soft HEAD~5 followed by git commit squashes the last 5 commits into one.
Reverting a Pushed Commit
git revert records a new commit that reverses an earlier one, so history only grows. It needs a clean working tree.
For anything teammates already pulled, git revert is the right tool, and -n applies the reversal without committing so several can be combined.
There is a documented limit with merges. A merge commit cannot be reverted without -m and a parent number, and reverting it declares you never want the tree changes it brought in. Later merges then carry only changes from commits that are not ancestors of the reverted merge.
Recovering With the Reflog
Reflog entries expire after 90 days by default, and unreachable ones after 30 days (git-reflog manual, Git 2.51.1).
A commit lost to a reset stays reachable by its hash until it expires. Run git reflog to find the old tip, then point a branch at it.
The reflog tracks commits, not uncommitted edits. A hard reset that discards unstaged work leaves nothing to recover.
When git commit Does Nothing or Fails
git commit stops without creating a commit in a handful of documented cases: a tree identical to its parent, a hook that exits non-zero, an empty or untouched message, and unresolved merge conflicts. The fix starts with reading what Git printed.
Git blocks a commit whose tree matches its sole parent, and –allow-empty overrides that. Exiting the editor without changing a template aborts the commit, and an empty message needs –allow-empty-message.
With unresolved conflicts, stage each fixed path with git add, confirm with git status, then run git commit. And if user.name and user.email are missing, Git builds an author from the system user name and hostname.
Run git status first. It shows whether the staging area is empty, which paths are unmerged, and which files Git has never seen.
Set the identity once with git config, because a hostname-based address is rarely the one you want on a permanent record.
If git commit exits with an error before creating a commit, the message you wrote is kept in .git/COMMIT\_EDITMSG until the next run overwrites it (git-commit manual). Copy it out first.
When a hook rejects the commit, fix what it reported. Git also warns when a message is not valid UTF-8 unless i18n.commitEncoding says the project uses a legacy encoding.
Common Questions About git commit
How do I sign a commit with GPG?
Add -S to the command, optionally with a key ID attached directly, as in -SKEYID. To sign every commit, set commit.gpgSign to true, and use –no-gpg-sign to skip it once.
The manual warns that signing during a rebase can mean many passphrase prompts, so an agent helps.
Which editor does git commit open?
Git checks the GIT\_EDITOR environment variable first, then the core.editor setting, then VISUAL, then EDITOR.
Passing -m skips the editor entirely unless you add -e to edit the message afterward.
What does the -s flag add to a commit?
It appends a Signed-off-by trailer with the committer’s name and email at the end of the message. What that line certifies depends on the project, for example the Developer Certificate of Origin used by the Linux kernel and Git.
Git has no setting to turn it on by default. For other trailers, use –trailer.
Can I commit under a different author or date?
Yes. –author overrides the author using the “A U Thor” name and email format, and –date overrides the author date.
When amending or reusing a message with -c or -C, –reset-author hands authorship to the committer and renews the timestamp.
How do I see what a commit contains after making it?
Run git show HEAD to display the newest commit with its changes. git show HEAD^ shows its parent, and git log lists the whole history.
For the raw stored object, git cat-file -p HEAD prints the tree, parent, author, committer and message lines.
How Much Belongs in One Commit

One logical change per commit is the rule I’d follow, meaning the smallest unit a reviewer would read, a teammate would revert and a subject line could describe. Size does not alter the final code, only the history.
Pro Git notes that the snapshot at the branch tip is identical whether the same work lands as one commit or five.
Pair that with how git revert behaves and a rule follows. A revert reverses an entire commit, so commit size sets the smallest piece of work you can back out cleanly.
The order I’d work in looks like this.
- Stage one change with git add –patch.
- Check the staged diff.
- Write the subject last, once the content is fixed.
Small commits do have a price. Each one runs the hooks again and adds a line to the log. Fixups that pile up can be folded together by squashing them into one commit before the work is shared.
- How to Turn On Dark Mode in Notepad++ (Built-In, No Plugin) - October 3, 2026
- PostgreSQL Cheat Sheet - October 2, 2026
- How to Repair a Corrupt SQL Server Database Without Any Data Loss - October 2, 2026



