Git Command Reference

Every Git tutorial teaches you commands one at a time, but once you’re working day to day you need a map of the whole toolbox: which command does what, when to reach for it, and how the dozens of subcommands relate to one another. This lesson is that map – a single, organized reference to the Git commands you will use constantly, grouped by purpose, with worked examples and the internals behind them. Keep it bookmarked; you will come back to it long after you finish the rest of this course.

Overview: How Git’s Command Surface Is Organized

Git’s command-line interface has more than 150 subcommands, but almost everything you do falls into a handful of categories: configuring Git itself, creating or cloning a repository, recording snapshots, branching and merging, synchronizing with a remote, inspecting history, and undoing changes. Git itself distinguishes porcelain commands – the ones ordinary users touch, like commit, merge, and log – from plumbing commands, the lower-level building blocks porcelain is made of, like hash-object, cat-file, and update-ref. This lesson focuses on porcelain, but understanding the plumbing model underneath makes every porcelain command make sense.

Underneath every porcelain command, Git is manipulating three things: the working tree (the files you see and edit on disk), the index (also called the staging area – a file at .git/index listing exactly what will go into the next commit), and the object database (the content-addressed store under .git/objects). Every version of every file’s content is stored as a blob, named by the SHA-1 hash of its content. A tree object is a snapshot of a directory: it lists blobs and other trees by name and file mode. A commit object points to exactly one tree (the full snapshot at that point), to one or more parent commits, and carries metadata – author, committer, timestamp, and message. A branch is nothing more than a small file containing a commit hash, living under .git/refs/heads/; moving a branch just means writing a new hash into that file. HEAD points to the branch you currently have checked out – or, in detached HEAD state, directly to a commit – and it is what Git updates when you commit and what it reads to know what your working tree should be compared against.

The Core Command Reference

The table below groups the commands you will reach for constantly. When you are not sure which command applies to a task, find the row that matches your intent.

Category Command What it does
Setup & Config git init Creates a new, empty repository in the current directory (adds a .git folder).
Setup & Config git clone <url> Downloads an existing repository and its full history, and checks out its default branch.
Setup & Config git config Reads or sets configuration (user name/email, aliases, defaults) at --local, --global, or --system scope.
Snapshotting git status Shows the working tree and index state: staged, unstaged, and untracked changes.
Snapshotting git add Copies file contents from the working tree into the index, staging them for the next commit.
Snapshotting git commit Records a permanent snapshot of the index as a new commit object.
Snapshotting git restore Discards working-tree changes, or unstages files from the index with --staged.
Snapshotting git rm / git mv Removes or renames a tracked file and stages that change in one step.
Branching & Merging git branch Lists, creates, or deletes branches. A branch is just a pointer to a commit.
Branching & Merging git switch Changes which branch HEAD points to, or creates and switches to a new one with -c.
Branching & Merging git checkout The older, multi-purpose command for switching branches or restoring files; still very common.
Branching & Merging git merge Combines another branch’s history into the current one, creating a merge commit when needed.
Branching & Merging git rebase Replays the current branch’s commits on top of another branch’s tip, producing new commits and linear history.
Sharing & Updating git remote Manages the named URLs (typically origin) your repository talks to.
Sharing & Updating git fetch Downloads commits, branches, and tags from a remote without touching your working tree.
Sharing & Updating git pull Runs git fetch, then merges (or rebases, with --rebase) the remote branch into yours.
Sharing & Updating git push Uploads local commits to a remote branch, updating its pointer there.
Inspection git log Shows commit history, newest first, with author, date, and message.
Inspection git diff Shows line-by-line differences between the working tree, the index, and commits.
Inspection git show Displays a single commit’s metadata and the diff it introduced.
Inspection git blame Shows which commit and author last touched each line of a file.
Undoing git reset Moves the current branch pointer, optionally updating the index and working tree; rewrites history.
Undoing git revert Creates a new commit that undoes an earlier commit’s changes, without rewriting history.
Undoing git stash Temporarily shelves uncommitted changes so you can switch context, then reapplies them later.
Undoing git cherry-pick Applies the changes from a specific commit elsewhere onto the current branch.
GitHub (gh CLI) gh pr create, gh pr merge, gh issue list Command-line equivalents of the GitHub web UI for pull requests and issues.

Examples

Example 1: Starting a project

git init my-project
cd my-project
git add README.md
git commit -m "chore: initial commit"

Output:

Initialized empty Git repository in /home/user/my-project/.git/
[main (root-commit) a1b2c3d] chore: initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

git init creates the .git directory – the object database and refs live there from this point on. git add hashes README.md‘s content into a blob and records it in the index. git commit then writes a tree object from the index and a commit object pointing at that tree, and moves main to point at the new commit. The (root-commit) label means this commit has no parent – it is the first one in the repository.

Example 2: A feature branch workflow

git switch -c feature/login-page
git add src/login.js
git commit -m "feat: add login form validation"
git push -u origin feature/login-page

Output:

Switched to a new branch 'feature/login-page'
[feature/login-page 9f8e7d6] feat: add login form validation
 1 file changed, 42 insertions(+)
Enumerating objects: 5, done.
Writing objects: 100% (3/3), 512 bytes | 512 KiB/s, done.
To github.com:yourname/yourrepo.git
 * [new branch]      feature/login-page -> feature/login-page
Branch 'feature/login-page' set up to track remote branch 'feature/login-page' from 'origin'.

git switch -c creates a new branch reference pointing at the current commit and moves HEAD to point at that new branch – no files change yet. After committing, feature/login-page points one commit ahead of main. The -u (--set-upstream) flag on the first push tells Git to remember that this local branch tracks the newly created remote branch, so future plain git push and git pull commands on this branch know where to go without naming the remote again.

Example 3: Undoing before you commit

git status
git restore --staged config.yml
git restore config.yml

Output:

On branch feature/login-page
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   config.yml

After git restore --staged config.yml:
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
	modified:   config.yml

After git restore config.yml:
nothing to commit, working tree clean

The first git restore --staged call copies the file’s version from the last commit back into the index, unstaging it – the working tree copy is untouched. The second call, without --staged, copies the file from the index back into the working tree, discarding your edits there. This pair replaces the old, more overloaded git checkout -- config.yml and git reset HEAD config.yml commands with clearer, single-purpose ones.

Example 4: Inspecting history

git log --oneline -n 3
git diff HEAD~1 HEAD

Output:

a1b2c3d (HEAD -> main) feat: add index page
9f8e7d6 feat: add login form validation
c4d5e6f chore: initial commit

diff --git a/src/index.js b/src/index.js
index e69de29..8f94a3c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,3 @@
+function renderHome() {
+  return "Welcome";
+}

--oneline compresses each commit to its abbreviated hash and subject line, and -n 3 limits it to the three most recent commits. git diff HEAD~1 HEAD compares the tree of the commit one before HEAD against the tree of HEAD itself, showing exactly which lines changed between those two snapshots.

How It Works Step By Step

Take git add src/login.js followed by git commit -m "..." and git switch -c feature/x. Internally:

  • git add reads the file from the working tree, computes a SHA-1 hash of its content, writes a new blob object to .git/objects if that exact content doesn’t already exist there, and updates the index to point the path src/login.js at that blob.
  • git commit walks the index, builds one or more tree objects representing the full directory structure being committed, writes a commit object that references the root tree plus the current branch tip as its parent, and then updates the ref file for the current branch (for example .git/refs/heads/main) to contain the new commit’s hash. HEAD doesn’t change here because it already points at the branch name, not at a specific commit.
  • git switch -c feature/x creates a new file under .git/refs/heads/ named feature/x containing the current commit’s hash, then rewrites .git/HEAD so it contains ref: refs/heads/feature/x instead of ref: refs/heads/main. No blobs, trees, or commits are created – branching is cheap precisely because it is just writing one small pointer file.
  • git merge other-branch compares the tip commits of the two branches, finds their common ancestor, and if the change sets don’t overlap, either fast-forwards the current branch’s pointer (if no divergent commits exist on the current branch) or creates a new merge commit with two parents, whose tree reflects both branches’ changes combined.

Common Mistakes

Mistake 1: Staging everything blindly

git add .
git commit -m "wip"

This stages every modified and untracked file in the directory, including build artifacts, editor files, or secrets you never meant to commit – and a vague message like “wip” gives future readers (including you) no way to know what changed. Check git status first, list what’s actually staged, and keep an accurate .gitignore so generated files never show up as untracked in the first place.

git status
git add src/index.js
git commit -m "feat: add index page"

Mistake 2: Bare force-pushing a shared branch

git push --force origin main

A bare --force overwrites whatever is on the remote unconditionally, even if a teammate pushed commits you haven’t seen – those commits are simply discarded from the branch’s history on the remote. Use --force-with-lease instead: it fails safely if the remote has moved since you last fetched, so you can pull and reconcile first rather than silently erasing someone’s work.

git push --force-with-lease origin main

Mistake 3: Rebasing a branch other people already pulled

git switch main
git rebase feature/login-page
git push --force origin main

Rebase’s golden rule: never rebase commits that others have already fetched or based work on. Rebasing rewrites commit hashes, so everyone else’s local main diverges from the rewritten one, and force-pushing over a shared branch multiplies the damage. If main is shared, use git merge to bring branches together instead – it never rewrites existing commits.

git switch main
git merge feature/login-page
git push origin main

Best Practices

  • Run git status before every git add and every git commit so you know exactly what you’re about to stage or record.
  • Write commit messages in a consistent style, such as Conventional Commits (feat:, fix:, chore:, docs:), so history is easy to scan and can drive automated changelogs.
  • Prefer git switch and git restore for everyday branch and file operations; reserve git checkout for cases where you specifically need its dual behavior or are following older documentation.
  • Default to git push --force-with-lease, never bare --force, when you must overwrite a remote branch you own (like your own feature branch after an interactive rebase).
  • Use git log --oneline --graph regularly to build an intuition for how your branches and merges actually relate to one another.
  • Keep a personal ~/.gitconfig with useful aliases (for example git config --global alias.st status) so the commands you run most end up short and memorable.

Practice Exercises

  • You’ve edited three files but only want to commit two of them together with a clear message, leaving the third for a separate commit. Using the reference table, work out which staging and commit commands accomplish this without touching the third file.
  • You committed a change directly to main that should have gone on a feature branch, and it hasn’t been pushed yet. Figure out how to move that commit onto a new branch and reset main back to where it was, using commands from the Branching and Undoing rows of the table.
  • You have five old feature branches locally that were already merged into main weeks ago. Work out which single-purpose command (from the Branching & Merging row) lists merged branches, and how you would delete the stale ones safely.

Summary

  • Git commands fall into a handful of categories: setup, snapshotting, branching, sharing with remotes, inspection, and undoing – use the reference table to find the right one fast.
  • Every snapshot is built from blobs (file content) and trees (directory listings); a commit points to one tree and its parent commit(s); a branch is just a movable pointer to a commit.
  • git switch and git restore replace the overloaded parts of git checkout with clearer, single-purpose commands, but checkout still works and appears constantly in the wild.
  • git fetch only downloads; git pull downloads and then merges or rebases – know which one you’re running.
  • Never rebase or bare force-push a branch other people already have local copies of; use --force-with-lease and prefer merge for shared history.