On our server, git help -a lists exactly 165 Git commands. In day-to-day work we use maybe fifteen of them. The rest are either specialist tools or internal plumbing you never have to touch directly. That is exactly why a Git commands list is useful: it separates what you type every day from what you look up once a year.
This Git commands list is sorted by situation, not alphabetically. First setup, then the daily loop, then branches, then undoing things, then working with other people, and finally the emergency and detective commands. Every command comes with an example you can copy. All output and numbers come from our own tests on 18 October 2026 with Git 2.43 on our Ubuntu server: on a test repository with 100 commits and on a fresh clone of Express.js with 6,182 commits.
If you are not sure yet what a commit or a branch actually is, read our primer What is Git? first. It explains the model behind everything. This article is about the commands.
If you prefer to watch the commands in action first: Conner Ardman’s “Every GIT Command You’ll EVER Need” (May 2026) walks through most of this list in order. The video is for following along, this page is for looking things up.
The Git commands list at a glance
If all you need is a table, here it is. Details for every command follow below.
| Situation | Command | What it does |
|---|---|---|
| One-time setup | git config --global user.name "Name" | Set your name for commits |
| New project | git init | Create a repository in the current folder |
| Get a project | git clone <url> | Copy an existing repository completely |
| What’s going on? | git status | Show changed, staged and new files |
| Look at changes | git diff | Show line-by-line differences |
| Stage | git add <file> | Put a file into the staging area |
| Save | git commit -m "message" | Create a snapshot with a message |
| History | git log --oneline | List commits compactly |
| Create branch | git switch -c <name> | Create a new branch and switch to it |
| Switch branch | git switch <name> | Move to an existing branch |
| Combine | git merge <branch> | Bring another branch into the current one |
| Upload | git push | Send local commits to the server |
| Download | git pull | Fetch new commits and integrate them |
| Just look | git fetch | Fetch new commits, change nothing |
| Put aside | git stash | Shelve unfinished work |
| Discard file changes | git restore <file> | Throw away uncommitted changes |
| Undo a commit | git revert <commit> | Create an inverse commit, history stays |
| Jump back | git reset --hard <commit> | Move branch and files to an old state |
| Fix last commit | git commit --amend | Change message or content of the last commit |
| Safety net | git reflog | Every movement of HEAD in recent weeks |
| Find a bug | git bisect | Binary search for the guilty commit |
| Who wrote this? | git blame <file> | Show the last commit for every line |
That is 22 rows. Those commands get you through 95 percent of working days. Below we explain each in detail, plus around 20 more variants and options worth knowing.
Setup: commands for day one
You type these once per machine and then never again.
git config: who are you?
git config --global user.name "Fabian Budde"
git config --global user.email "fabian@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase false
Name and email go into every commit. Without them Git refuses to commit. init.defaultBranch main makes new repositories start with main instead of master. In our test, git init without this setting still started on master, even though GitHub, GitLab and most teams have used main for years. Not a disaster, but an unnecessary source of confusion when your local branch is master and the server’s is main.
pull.rebase false answers a question Git will otherwise ask you sooner or later (more on that below). git config --list --show-origin shows every active setting and the file it comes from.
git init and git clone: starting a repository
# New project in the current folder
git init
# Copy an existing project
git clone https://github.com/expressjs/express.git
# Latest snapshot only, without the full history
git clone --depth 1 https://github.com/expressjs/express.git
git init creates a hidden .git folder that holds the entire memory of the project. git clone fetches someone else’s repository in full, every commit since the beginning.
The difference to a shallow clone with --depth 1 is measurable. Full Express.js clone: 12 MB in .git, done in 0.97 seconds. With --depth 1: 392 KB in 0.54 seconds. For a CI server that only needs to build, the shallow clone is perfect. For development, use the full one, otherwise git log, git blame and git bisect have no data to work with.
The daily loop: status, add, commit
Ninety percent of your Git time is spent in a loop of four commands: look, stage, save, upload.
git status: the most important command
git status
git status -s # short form
If you only memorise one Git command, make it this one. It tells you which branch you are on, which files are modified, which are staged, and which Git does not know about yet. It is also fast: on the Express repository it took 8 milliseconds. There is no reason not to type it constantly.
The short form -s shows two columns: left is the staging area, right is the working directory. M means modified, A added, ?? untracked.
git diff: what exactly did I change?
git diff # working directory vs. staging area
git diff --staged # staging area vs. last commit
git diff main..feature # compare two branches
git diff --stat HEAD~3 # summary of the last three commits
The most common beginner surprise: you have already staged files with git add, and now git diff shows nothing. That is correct. Without options, git diff only compares what has not been staged. For staged changes you need --staged.
git add: staging
git add index.html # one file
git add src/ # a whole folder
git add -A # everything, including deletions
git add -p # pick changes piece by piece
git add -p is the most underrated command on this list. Git shows you every change individually and asks whether it belongs in the next commit. That lets you turn one messy afternoon into two clean commits: one for the bug fix, one for the new feature.
git commit: saving
git commit -m "Login form now validates email addresses"
git commit -am "Fix typo" # add + commit for tracked files
git commit --amend -m "Better message" # replace the last commit
-am is handy but has a trap: it only includes files Git already tracks. A newly created file stays out. You usually notice when a colleague reports something missing.
--amend replaces the last commit. The important part: it does not modify it, it builds a new one. In our test, commit 012eb1d became 18a7889 after the amend. A new ID means a different commit as far as Git is concerned. Before you push, that is fine. After you push, it causes trouble, because your history and the server’s no longer match.
git log: the history
git log --oneline
git log --oneline --graph --all
git log -p file.txt # changes to one file
git log --since="2 weeks ago"
git log --author="Fabian"
git shortlog -sn # commits per person
git log is surprisingly fast. All 6,182 Express.js commits were listed by git log --oneline in 40 milliseconds. git shortlog -sn showed 399 authors, led by TJ Holowaychuk with 1,891 commits. With --graph --all you see branches as an ASCII tree, which during merge trouble often helps more than any GUI.
Branches: working in parallel
In Git a branch is just a note containing a commit ID. That is why branches are so cheap, and why you should use them generously.
git branch, git switch, git checkout
git branch # all local branches
git branch -a # including remote branches
git switch -c feature/login # create and switch
git switch main # back to main
git branch -d feature/login # delete (only if merged)
git branch -D experiment # delete, even if unmerged
git checkout used to do everything: switch branches, create branches, reset files. Since Git 2.23 there are two separate commands for this: git switch for branches and git restore for files. checkout still works but is ambiguous. In our test we created a branch and a file both named dev. git checkout dev then has to guess what you mean. git switch dev can only mean a branch. For beginners the rule is simple: switch for branches, restore for files, checkout only so you can read old tutorials.
Both commands protect you equally well, by the way: with uncommitted changes to a file that differs on the target branch, checkout and switch both refuse with “Your local changes … would be overwritten”. You lose nothing.
git merge: combining
git switch main
git merge feature/login
git merge --abort # on conflicts, go back
git merge brings another branch’s commits into the current one. If nothing overlaps, it happens silently. If both sides changed the same line, Git reports a conflict and marks the spot in the file with <<<<<<<, ======= and >>>>>>>. You decide what stays, run git add and git commit. And if it gets too much: git merge --abort restores the exact state before the merge. We walk through that in detail in What is Git?.
git rebase: tidying history
git switch feature/login
git rebase main # replay your commits on top of the latest main
git rebase -i HEAD~3 # edit the last three commits
git rebase --abort
rebase replays your commits as if you had started just now. The result is a straight history without merge commits. With -i (interactive) you can squash, reword or reorder commits. In our test we squashed three commits named “fixup 4/5/6” into one. The history shrank from eight to six commits.
The golden rule: never rebase commits other people already have. Like --amend, rebase creates new commit IDs. For your local feature branch before the first push, that is ideal. For main, it is off limits.
git cherry-pick: taking a single commit
git cherry-pick a1b2c3d
Sometimes you need exactly one commit from a branch, say a bug fix, without the rest. cherry-pick copies it onto your current branch. Again, it is a copy with a new ID, not a reference.
Undoing things: the part everyone is afraid of
This is where beginners mix up commands most often. The distinction becomes clear if you ask: What do I want to take back, and does anyone else already have it?
git restore: discarding file changes
git restore index.html # discard an uncommitted change
git restore --staged index.html # unstage, keep the change
git restore --source=HEAD~2 app.js # file as it was two commits ago
git restore is the safest undo command because it only touches files, never history. But what you throw away with git restore file was never committed. Git has no copy of it. That is one of the few ways to genuinely lose work in Git.
git reset: moving the branch back
git reset --soft HEAD~1 # commit gone, changes stay staged
git reset HEAD~1 # commit gone, changes stay (unstaged)
git reset --hard HEAD~1 # commit gone, changes gone
We tested the three variants side by side, each on a repository with three commits where the last commit created the file f3:
| Variant | Commits after | git status after | File f3 |
|---|---|---|---|
--soft | 2 | A f3 (staged) | still there |
--mixed (default) | 2 | ?? f3 (untracked) | still there |
--hard | 2 | clean | deleted |
Memory aid: --soft is “un-commit, keep everything else”, --hard is “everything back to how it was”. All three move the branch, so they rewrite history. That makes them the wrong tool for commits you have already pushed.
git revert: undoing without rewriting history
git revert a1b2c3d
git revert --no-edit HEAD
revert deletes nothing. It creates a new commit that exactly inverts the old one. In our test, git revert took the commit count from 103 to 104, and the commit was automatically named Revert "feat". This is the right choice for anything already on the server: colleagues simply receive one more commit and have nothing to repair.
A small gotcha from testing: git revert --no-edit HEAD -q fails, because revert simply does not accept -q. Other commands like commit or stash pop do. Git is not consistent here.
The rule of thumb
| I want to … | Command |
|---|---|
| throw away an uncommitted file change | git restore file |
| take a file out of the next commit | git restore --staged file |
| redo my last local commit | git reset --soft HEAD~1 or git commit --amend |
| undo a pushed commit | git revert <id> |
| throw everything away and start over | git reset --hard origin/main |
git stash: putting work aside
git stash # shelve changes
git stash -u # including new, untracked files
git stash list
git stash pop # restore and delete the stash
git stash apply # restore, keep the stash
git stash drop
The classic case: you are halfway through a feature and suddenly a bug fix is needed on main. git stash clears your desk, you switch, fix, come back and bring everything back with git stash pop.
We hit the trap right away in testing. After a plain git stash, git status still showed ?? neu.txt. New files Git does not track yet are not included by git stash without an option. They simply stay where they are and may end up in the wrong commit after a branch switch. Only git stash -u packed the file away; it vanished from the folder and came back with pop. If you do not want to remember that, set an alias (see below).
git clean: deleting untracked files
git clean -n # show what would be deleted
git clean -fd # actually delete, including folders
git clean deletes files Git does not track: build leftovers, temp files. There is no trash bin and no reflog for this. So always run -n (dry run) first. In our test it neatly reported “Would remove a.tmp” and “Would remove b.tmp” without touching anything.
git rm —cached: stop tracking a file
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "Stop tracking .env"
This makes Git stop tracking the file while it stays on your disk. What many people miss: the file stays in the history. In our test, git log --all -- .env still showed the commit that added it. If you ever committed a password, treat it as burned and change it. git rm --cached is housekeeping, not damage control.
Collaboration: remote, push, pull, fetch
As soon as a second person or a server is involved, you need the remote commands.
git remote: where does the repository belong?
git remote -v
git remote add origin git@github.com:name/project.git
git remote set-url origin git@github.com:name/new.git
origin is just a nickname for a URL, a convention rather than a rule. For SSH access you need a key; our guide on how to generate an SSH key covers that.
git push
git push # upload current branch
git push -u origin feature/x # first push of a new branch, with tracking
git push --tags
git push --force-with-lease # careful overwrite
You only need -u on the first push of a branch. After that Git knows where the branch belongs and a plain git push is enough.
—force vs. —force-with-lease: our most important test
If you rewrote an already pushed commit with --amend or rebase, the server rejects a normal push. The temptation is git push --force. That blindly overwrites the server state, even if a colleague pushed something in the meantime. Their work is then gone from the server.
--force-with-lease is considered the safe alternative: Git only overwrites if the server still looks the way you last saw it. We reproduced this with a local bare repository and two clones:
- A colleague pushes a new commit.
- We amend our last commit and try
git push --force-with-lease. - Result:
! [rejected] HEAD -> master (stale info). Good, the protection works. - Then we do what everyone does reflexively when a push fails:
git fetch. - Again
git push --force-with-lease:+ f214804...60af982 HEAD -> master (forced update). The colleague’s commit is overwritten on the server.
--force-with-lease only compares against the state your last fetch saw. A fetch “acknowledges” the foreign commit without you ever looking at it. Since Git 2.30 there is --force-if-includes, which additionally checks whether the remote state is actually contained in your local history. We tested that too: after the same fetch, git push --force-with-lease --force-if-includes refused with ! [rejected] (remote ref updated since checkout). Our recommendation:
git push --force-with-lease --force-if-includes
Even better: never force on main at all, and block force pushes there via branch protection on GitHub or GitLab.
git fetch vs. git pull
git fetch # fetch new commits, change nothing
git pull # fetch + merge
git pull --rebase # fetch + rebase
git fetch is risk-free. It downloads new commits and updates origin/main, your files stay untouched. git pull then also performs a merge or rebase. In our test the server and the local branch had different commits. git pull downloaded them but then stopped with “You have divergent branches and need to specify how to reconcile them”. Git wants you to decide once: merge (pull.rebase false) or rebase (pull.rebase true). That is why the setting appears in the setup section.
git tag: marking versions
git tag v1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0
Tags are branches that never move. Express.js has 306 of them, one for every released version. Tags are not uploaded automatically by git push, which is easy to forget.
Detective and emergency commands
You rarely need these. When you do, they save hours.
git reflog: the safety net
git reflog
git reset --hard HEAD@{2}
Every time HEAD moves (commit, switch, reset, rebase, cherry-pick), Git writes an entry to the reflog. In our small test repository there were 120 entries after an hour of experimenting. If you accidentally typed git reset --hard, you find the old state here and get it back with a second reset. The reflog is local and keeps entries for 90 days by default. It rescues commits, not changes that were never committed.
git bisect: finding the guilty commit
git bisect start
git bisect bad # current state is broken
git bisect good v1.0 # this state was fine
# … Git checks out the middle, you test and say good or bad …
git bisect reset
# or automatically with a test command:
git bisect run npm test
Bisect is binary search over your history. In our test a bug sat in commit 51 out of 100. git bisect run with a one-line test script found it in 6 steps and 0.05 seconds. With 1,000 commits it would take 10 steps, with a million 20. If “it used to work” describes your problem, this is the command.
git blame: who wrote this line?
git blame lib/application.js
git blame -L 40,60 lib/application.js
The name suggests finger-pointing, but it is about tracing. git blame shows for every line which commit last changed it. Express’s lib/application.js took 33 milliseconds. With the commit ID you then go to git show and read why the line is the way it is.
git log -S: when did this text enter the project?
git log -S "trust proxy" --oneline
The so-called pickaxe does not search commit messages but the content of every change. In Express it found 28 commits that added or removed the string “trust proxy”, across all 6,182 commits in 0.38 seconds. It also finds passwords that were committed and later deleted, which shows why deleted secrets are not safe.
git show: looking at a commit
git show a1b2c3d
git show HEAD~2:src/app.js # show a file in an old state
git gc: housekeeping
git gc
git count-objects -vH
Git occasionally packs its data on its own. Our test repository had 320 loose objects and a 2.2 MB .git folder after 100 commits. After git gc it was 264 KB, all in one 33 KB pack file. Running it manually is almost never necessary, but it shows how efficiently Git stores data internally.
Aliases: your own Git commands list
What you type daily, you may abbreviate. We use these aliases ourselves:
git config --global alias.st "status -s"
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.undo "reset --soft HEAD~1"
git config --global alias.save "stash push -u"
git config --global alias.pushf "push --force-with-lease --force-if-includes"
After that, git lg gives you the tree and git save stashes including new files. The stash trap from above is permanently solved.
Common error messages and the matching command
| Message | Meaning | Fix |
|---|---|---|
Please tell me who you are | Name/email missing | git config --global user.name … |
fatal: not a git repository | You are in the wrong folder | cd into the project or git init |
Your local changes would be overwritten | Uncommitted changes block the switch | git stash -u, then switch |
rejected … (fetch first) | Server has newer commits | git pull, then git push |
divergent branches | Both sides have their own commits | set git config pull.rebase false or true |
CONFLICT (content) | Same line changed on both sides | edit file, git add, git commit or git merge --abort |
You are in 'detached HEAD' state | You are on a commit, not a branch | git switch -c new-branch to keep your work |
Git in practice: where we actually use these commands
On our servers practically everything lives in Git: websites, config files, deploy scripts. A typical day looks like git status, git add -p, git commit, git push. For releases we add git tag, and for live systems a separate worktree on a fixed tag, so nobody accidentally keeps developing on production. That is why we type git switch --detach v2.1.0 more often than you might think.
For automated tasks on the server, such as nightly git pull runs, we combine Git with cron jobs. How to set those up reliably is covered in how to create a cron job. And if you are setting up a new server from scratch, Git is one of the first steps in our Linux server setup guide.
A lesson we learned the hard way: Git is not a backup. A git push to the same server that holds your working copy does not protect you from a disk failure. That is what the 3-2-1 backup strategy is for.
Short and to the point: Thetips4you’s “Git Explained in 10 Minutes” (August 2026) is a compact video cheat sheet that pairs well with the tables on this page.
Conclusion: which Git commands do you really need?
Of the 165 commands, you need exactly eight at the start: status, add, commit, log, switch, merge, push and pull. With those you work cleanly alone and in a team. The next level is the undo commands restore, revert, reset and stash, and here it pays to genuinely understand the difference instead of copying commands from forums. At the top sit reflog, bisect, blame and log -S: rarely needed, but priceless when you do.
Two things from our tests we would tell everyone. First: git stash without -u leaves new files behind. Second: --force-with-lease only protects you until your next fetch, so add --force-if-includes. Neither shows up in most Git commands lists, and both can cost you work.
Bookmark the table at the top. After a few weeks you will not need it anymore.
FAQ: Git commands list
Which Git commands are used most often?
git status, git add, git commit, git push, git pull and git switch. Those six handle most of the daily work. git log and git diff join them for looking things up.
How many Git commands are there?
git help -a lists exactly 165 commands in Git 2.43. Many are internal “plumbing” commands you never use directly. For daily work, 20 to 40 are enough.
What is the difference between git reset and git revert?
git reset moves the branch to an older commit and therefore rewrites history. git revert creates a new commit that inverts an old one and leaves history intact. Use revert for pushed commits and reset for local ones.
What is the difference between git fetch and git pull?
git fetch downloads new commits from the server but changes neither your files nor your branch. git pull additionally merges or rebases, bringing the new commits straight into your work.
Should I use git checkout or git switch?
git switch for branches, git restore for files. Both exist since Git 2.23 and are clearer than the old git checkout, which did both and has to guess when a branch and a file share a name.
How do I undo the last commit?
If it has not been pushed: git reset --soft HEAD~1. The changes stay staged, only the commit is gone. If you only want to change the message: git commit --amend. If it is already on the server: git revert HEAD.
How do I recover a deleted commit?
Use git reflog to find the ID of the old state, then git reset --hard <id> or git switch -c rescue <id>. The reflog keeps entries for 90 days by default, but only locally on your machine.
Does git stash include new files?
Not without an option. A plain git stash only saves changes to files Git already tracks. New files stay in the folder. git stash -u includes them.
Is git push —force-with-lease safe?
Safer than --force, but not foolproof. It only compares against the state of your last git fetch. If you fetch first, you will still overwrite other people’s commits; we reproduced that. Adding --force-if-includes makes the protection considerably better.
Is there a Git commands cheat sheet as a PDF?
The official reference is git help <command> right in the terminal, or the documentation on git-scm.com. GitHub offers a free cheat sheet PDF. You can save the table at the top of this page as a PDF using your browser’s print function.
Do I need the command line, or is a GUI enough?
For daily work a GUI like VS Code’s Git integration or GitHub Desktop is enough. It is still worth learning the commands in the terminal once: every GUI runs exactly these commands under the hood, and error messages refer to them.
