Nearly everything you'll type in Git day to day boils down to moving a change through three places: your working directory, a staging area, and your local repository, then optionally sending that repository's history to a remote. Once that three-stage picture is solid, the commands stop feeling like magic incantations.
The three trees
Git tracks your project through three distinct "areas," and almost every command moves something between them:
| Area | What it is |
|---|---|
| Working directory | The actual files on disk that you edit in your editor |
| Staging area (the "index") | A draft of what will go into the next commit |
| Repository | The committed history - every snapshot you've saved, stored in .git/ |
Starting a repository
git init turns the current folder into a Git repository by creating a hidden .git directory that holds all of Git's tracking data. git clone <url> does the same thing but also copies an existing repository's full history from somewhere else (typically a remote like GitHub) - clone is by far the more common way most people first encounter a repo.
Checking what's changed
git status is the command you'll run more than any other - it tells you which files are modified, which are staged, and which Git doesn't know about yet. git diff shows the actual line-by-line changes in your working directory that haven't been staged yet; git diff --staged shows what's staged and about to be committed.
Staging: git add
git add <file> moves a change from the working directory into the staging area - it tells Git "include this in the next commit." You can stage specific files, a whole directory, or everything with git add .. Staging exists as a separate step precisely so you can build a commit out of only some of your changes, rather than being forced to commit everything you've touched at once.
Committing: git commit
git commit -m "message" takes whatever is currently staged and saves it as a permanent snapshot in your repository's history, with a message describing what changed and why. A good commit message has a short, specific summary line (aim for under ~50 characters) and, for anything non-trivial, a blank line followed by more detail on the reasoning - future-you, reading git log six months from now, is the actual audience.
git add before git commit and wondering why nothing happened. Commit only acts on what's staged - if you haven't added a change, it isn't part of the next commit no matter how long you've been staring at it in your editor.Ignoring files on purpose
Not everything belongs in version control - build artifacts, dependency folders like node_modules, local environment files with secrets. A .gitignore file in your repo's root lists patterns Git should never track or suggest adding, so git status and git add . quietly skip them.
Looking at history: git log
git log shows the commit history, newest first - author, date, message, and a unique commit hash for each one. git log --oneline condenses each commit to one line, which is usually what you actually want once a history has more than a handful of commits.
Sending it somewhere: git push and git pull
Everything so far has been entirely local. git push uploads your local commits to a remote repository (covered in depth in the next section of this series) so other people - or other machines - can see them. git pull does the reverse: it downloads new commits from the remote and merges them into your current branch. Push and pull are the two commands that turn a solo, offline tool into something teams actually collaborate through.
git add it → git commit -m "..." it → git push it. That four-step loop, repeated, is the large majority of real-world Git usage - branching and merging (next guide) are what make it safe to do that loop in parallel with other people.