Logics Guru

Git Command Cheat Sheet

The Git commands that come up in real work, including the ones for undoing things safely and the difference between the dangerous options and the safe ones.

4 min read 3 views

Most Git use is a dozen commands. The rest of it is undoing something, which is where the manual gets least helpful and the stakes are highest. This covers both, and is explicit about which commands rewrite history.

Looking at what you have#

Shell
git status                 # what is staged, changed, untracked
git status -sb             # the same, in two compact lines
git diff                   # unstaged changes
git diff --staged          # what a commit would contain right now
git log --oneline --graph --decorate --all
git log -p path/to/file    # every change to one file, with diffs
git log -S "someString"    # commits that added or removed that string
git blame path/to/file     # who last touched each line, and when
git show abc1234           # one commit in full

Branching#

Shell
git switch main                  # change branch (clearer than checkout)
git switch -c feature/thing      # create and change to it
git switch -                     # back to the previous branch
git branch -vv                   # local branches and their upstreams
git branch -d old-branch         # delete, refusing if unmerged
git branch -D old-branch         # delete regardless
git branch -m new-name           # rename the current branch

switch and restore were added because checkout did too many unrelated jobs. Prefer them.

Staging and committing#

Shell
git add path/to/file
git add -p                       # stage selected hunks, reviewing as you go
git commit -m "message"
git commit --amend               # fold changes into the last commit
git commit --amend --no-edit     # ...without editing the message

git add -p is the single habit most worth acquiring: it forces you to read your own diff before committing it.

Undoing, from safest to most destructive#

CommandWhat it doesSafe?
git restore fileDiscards unstaged changes to that fileLoses uncommitted work only
git restore --staged fileUnstages, keeping the changesYes
git revert abc1234New commit undoing an old oneYes — history is preserved
git reset --soft HEAD~1Undoes the commit, keeps everything stagedYes
git reset HEAD~1Undoes the commit, keeps changes unstagedYes
git reset --hard HEAD~1Undoes the commit and discards the changesDestroys uncommitted work
git clean -fdDeletes untracked files and directoriesNo undo. Run -nd first to preview

On a shared branch, use revert, never reset. Revert adds a commit; reset rewrites history that other people already have.

Stashing#

Shell
git stash push -m "half-done thing"
git stash list
git stash show -p stash@{0}       # what is actually in it
git stash pop                     # apply and remove
git stash apply                   # apply and keep
git stash push -u                 # include untracked files
git stash drop stash@{0}

-u matters more than it looks: without it a stash silently leaves new files behind, and switching branches carries them with you.

Remotes#

Shell
git fetch --all --prune           # update refs, drop deleted remote branches
git pull --rebase                 # rebase local commits on top, no merge commit
git push -u origin feature/thing  # push and set upstream
git push --force-with-lease       # safe force push

Use --force-with-lease, not --force. Plain force overwrites the remote whatever state it is in, including a colleague's push you have not seen. --force-with-lease refuses if the remote moved since your last fetch.

Rebase and merge#

Shell
git rebase main                   # replay your commits on top of main
git rebase --continue             # after resolving a conflict
git rebase --abort                # give up, leaving nothing changed
git merge --no-ff feature/thing   # merge keeping an explicit merge commit
git cherry-pick abc1234           # take one commit from elsewhere

Rebasing produces a linear history that is far easier to read. The rule is simply: never rebase anything you have already pushed to a branch someone else is using.

When something has gone badly wrong#

Shell
git reflog                        # every position HEAD has held
git reset --hard HEAD@{3}         # go back to one of them

reflog is the reason most Git accidents are recoverable. A commit dropped by a bad reset or rebase is still there, and reflog knows where. It keeps entries for 90 days by default — so a "lost" commit almost never is.

Shell
git bisect start
git bisect bad                    # current commit is broken
git bisect good v1.2.0            # this one was fine
# test, then mark each step good or bad
git bisect reset

Bisect finds the commit that introduced a bug by binary search. Over a thousand commits that is ten tests, not a thousand.

Configuration worth setting once#

Shell
git config --global pull.rebase true
git config --global push.default current
git config --global rerere.enabled true
git config --global init.defaultBranch main

rerere records how you resolved a conflict and replays that resolution when the same one reappears. During a long rebase it saves the same work being done repeatedly.

Mustasim Ali

Mustasim Ali

Senior Software Engineer & Technical Lead

Full-stack engineer working in PHP and Laravel since 2019. I lead a development team building web and mobile products, and spend most of my time in Laravel, Node.js, Vue and React against MySQL and MongoDB. Logics Guru is where I write up the things I had to work out the hard way — the architecture decisions, the debugging sessions, and the small utilities I kept rebuilding until I put them somewhere permanent. Everything here is what I actually use.