Git add, git commit, and git push are the three stages of getting a change from your working files onto a remote repository: git add moves changed files into the staging area, git commit takes a snapshot of whatever is staged and saves it to your local repository’s history, and git push sends your local commits to the remote so others, or a deploy pipeline, can see them. Staging exists as its own step because it lets you choose exactly what goes into a commit, not every changed file, independent of what is currently sitting in your working directory.
The three commands get used together often enough that git add git commit git push functions as a single mental unit for a lot of developers, which is fine in practice but skips the reason staging exists as a separate stage at all.
Table of contents
- The Three Stages: Working Tree, Staging, Repository
- git add: Staging a Change
- git commit: Taking the Snapshot
- git push: Sending It to the Remote
- A Real Example, Start to Finish
- Why Staging Is a Separate Step
- Common Failure Messages
- What a Push Triggers on a Git-Deploy Host
- How this fits the rest of the stack
- FAQ
The Three Stages: Working Tree, Staging, Repository
Git tracks a file through three states. The working tree is whatever is actually on disk, edited or not. The staging area, also called the index, holds changes marked for the next commit. The local repository holds the committed history, snapshots that exist whether or not you have pushed them anywhere. A fourth state, the remote, is where git push sends commits once they exist locally.
A file can be modified in the working tree without being staged, staged without being committed, and committed without being pushed, three independent checkpoints, not one action split into three commands. Understanding them as separate stages, rather than one combined ritual, is what makes the failure messages later in this post make sense instead of feeling arbitrary.
git add: Staging a Change
git add moves a file’s current state from the working tree into staging, so it will be included in the next commit. It does not save anything to history yet, it marks intent. Running it again after editing the file further re-stages the newer version, since staging always reflects a snapshot taken at the moment git add ran, not a live link to the file.
git add README.md
git add src/
git add -p
git add README.md stages one file. git add src/ stages every changed file under that directory. git add -p walks through each changed section (a hunk) and asks whether to stage it, which is the tool for staging part of a file rather than all of it.
git commit: Taking the Snapshot
git commit takes whatever is currently staged and saves it as a new commit in the local repository, with a message describing the change. Anything in the working tree that was not staged is left out of that commit entirely, which is the whole point of the two-step process.
git commit -m "Fix pagination off-by-one"
git commit -a skips the staging step for already-tracked files, staging and committing every modified tracked file in one command. It does not pick up new, untracked files, which still need an explicit git add, and it removes the ability to commit only part of what changed.
git push: Sending It to the Remote
git push uploads local commits to a remote repository, updating the remote branch to match. Nothing you commit is visible to a collaborator, a pull request, or a deploy pipeline watching the repository until it is pushed, commits are entirely local until this step.
git push origin main
origin is the name of the remote (set automatically when a repository is cloned), and main is the branch being updated. A push can fail even after a successful commit, most often because the remote has commits your local branch has not seen yet, covered below, and that failure never rolls back or loses the local commit that triggered it.
A Real Example, Start to Finish
Editing a file, then getting it from disk to a remote branch, looks like this:
git status
git add src/api/pagination.js
git commit -m "Fix pagination off-by-one error"
git push origin main
git status before staging is worth the habit: it shows exactly which files are modified, staged, or untracked, so git add targets what you actually intend rather than everything that happens to be sitting in the working tree. Running git status again after the commit confirms the working tree is clean and the change is safely in local history before the push, which is the point at which it becomes anyone else’s problem too.
Why Staging Is a Separate Step
Without staging, a commit would have to be all-or-nothing against the working tree, or every commit command would need its own list of files typed out inline. Staging lets you build a commit incrementally, stage one function’s fix, commit it, stage a second unrelated change, commit that separately, even when both changes exist in the working tree at the same time.
git add -p goes further, staging individual hunks within a file rather than the whole file, which is the tool for splitting an accidental mix of two unrelated changes into two clean commits instead of one tangled one. It is the difference between a commit history that reads as a log of intentional changes and one that reads as whatever happened to be on disk at save time.
Common Failure Messages
Two errors account for most git push confusion. rejected, non-fast-forward means the remote branch has commits your local branch does not, usually because someone else pushed first; the fix is git pull, or git fetch and merge or rebase, to bring those commits in before pushing again, not force-pushing over them.
fatal: The current branch has no upstream branch means this local branch has never been pushed before, so Git does not know which remote branch to update. git push -u origin branch-name sets that link once; after that, a plain git push knows where to go. Both messages are Git refusing to guess at something it should not guess at, not a sign anything is broken.
What a Push Triggers on a Git-Deploy Host
On a platform that deploys from a connected repository, a git push to the watched branch is what starts a build, the platform detects the new commit, pulls it, and runs the build and deploy pipeline against it. There is no separate deploy command in that setup; the push is the deploy trigger, which is also why a rejected push, fix it before force-pushing, matters more than it looks like it should, nothing ships until it lands.
That also means a commit sitting locally, unpushed, changes nothing about what is live. It is easy to forget that distinction after enough days of git commit and git push running back to back as one habit.
How this fits the rest of the stack
That last step is the one that matters for anything hooked up to continuous deployment: connecting a repository means every push to the watched branch starts a build, with the log visible from the first line, and a failed build leaving the previous deploy running instead of taking the site down. What a service on that build path costs is on the RunxBuild hosting calculator.
Useful related references:
- Creating a Repository: Local First or Remote First, What Not to Commit, and the Push That Deploys It
- Git Revert a Merge Commit: The -m Flag and the Trap Afterwards
- Removing a Git Commit: reset, rebase, and Which One Is Safe
- Deploying from GitHub on RunxBuild
FAQ
What is the difference between git commit and git push?
git commit saves a snapshot of staged changes to your local repository’s history, it does not touch the remote at all. git push uploads those local commits to a remote repository so collaborators or a connected deploy pipeline can see them. A commit can exist locally, unpushed, indefinitely.
Do I need git add before git commit?
Yes, for any new or modified file you want included in the commit, unless you use git commit -a, which stages and commits every already-tracked modified file automatically. New, untracked files always need an explicit git add first, commit -a does not pick those up.
Can I add, commit, and push in one command?
Not in a single native command, though git commit -am combines staging tracked changes and committing in one step, and a shell alias or script can chain add, commit, and push together. There is no built-in Git command that performs all three, by design, the steps stay separate.
What does git push origin main do?
It pushes the local main branch’s commits to the remote named origin, updating origin’s main branch to match. origin is the default name for a repository’s primary remote, set when the repository was cloned or added, and main is the branch being pushed.