Git is one of those tools you use every day, but most people only scratch the surface. Here are the commands I find myself reaching for the most.
The Essentials
git log —oneline —graph —all
The default git log output is verbose. This combination gives you a compact,
visual history of all branches.
git log --oneline --graph --all
You can alias this to something short like git lg by adding it to your
.gitconfig:
git config --global alias.lg "log --oneline --graph --all"
git diff —staged
Before committing, I always check what is actually staged. git diff alone
shows unstaged changes, but --staged shows exactly what will go into the next
commit.
git diff --staged
git stash
When you need to quickly switch context without committing half-done work,
stash saves your working directory and
git stash
git checkout other-branch
# do some work
git checkout -
git stash pop
Naming stashes
By default stashes get a generic name based on the branch and commit. Use -m
to give them a meaningful description so you can identify them later.
git stash push -m "wip: auth refactor"
git stash list
# stash@{0}: On main: wip: auth refactor
Stashing specific files
You do not have to stash everything. Use push with file paths to stash only
certain files.
# stash only specific files
git stash push -m "just the config" src/config.ts src/env.ts
# stash only staged changes (leaves unstaged changes in the working tree)
git stash push --staged -m "staged only"
Stashing untracked files
By default git stash only saves tracked files. Use -u to include untracked
files, or -a to also include ignored files.
# include untracked files
git stash push -u -m "with new files"
# include untracked AND ignored files
git stash push -a -m "everything"
Stashing interactively
Use -p (patch) to interactively choose which git add -p.
git stash push -p -m "partial changes"
Listing and inspecting stashes
# list all stashes
git stash list
# show the diff of a specific stash
git stash show stash@{1}
# show the full diff with file contents
git stash show -p stash@{1}
Applying and removing stashes
# apply the most recent stash; remove it only if application succeeds
git stash pop
# apply without removing (keeps the stash for reuse)
git stash apply
# apply a specific stash
git stash apply stash@{2}
# drop a specific stash
git stash drop stash@{1}
# clear all stashes
git stash clear
Creating a branch from a stash
If your stashed changes conflict with work done since the stash was created, you can apply the stash onto a new branch starting from the commit where the stash was originally made.
git stash branch new-feature-branch stash@{0}
Rewriting History
git rebase -i HEAD~n
Interactive rebase is the most powerful command for cleaning up commits before pushing. You can squash, reorder, reword, or drop commits.
git rebase -i HEAD~3
This opens your editor with the last 3 commits. Common operations:
- squash: merge a commit into the previous one
- reword: change the commit message
- drop: remove the commit entirely
git commit —amend
Quick fix for the last commit, whether you forgot a file or made a typo in the message.
git add forgotten-file.ts
git commit --amend
Use --no-edit if you only want to add files without changing the message.
Debugging
git bisect
When a bug was introduced somewhere in the history and you have no idea where,
bisect performs a binary search through your commits.
git bisect start
git bisect bad # current commit is broken
git bisect good v1.0.0 # this tag was working
# git checks out a middle commit, you test it
git bisect good # or git bisect bad
# repeat until git finds the first bad commit
git bisect reset
git blame -L start,end file
Shows who last modified each line. The -L flag lets you focus on a specific
range instead of the entire file.
git blame -L 10,30 src/auth/login.ts
git log -S “search_term”
Finds commits that change the number of occurrences of a string. Use -G
instead to match added or removed lines with a regular expression. See
the git log documentationgit-scm.comGit reference for git log, including the -S and -G options that search commits for changes to a string.
git log -S "validateToken" --oneline
Working with Remotes
git fetch —prune
Fetches updates from the remote and removes references to branches that no longer exist on the remote.
git fetch --prune
git push -u origin branch-name
The -u flag sets the upstream tracking reference. After this, you can just use
git push and git pull without specifying the remote and branch.
git push -u origin feature/new-auth
git pull —rebase
git pull fetches and then integrates changes using the selected strategy.
With --rebase, it replays local commits on top of the upstream history. A
merge-based pull only needs a merge commit when histories diverge; a git pullgit-scm.comGit reference for git pull, including how --rebase, --ff-only and pull.rebase choose how changes are integrated.
git pull --rebase
You can make this the default behavior:
git config --global pull.rebase true
Recovery
git reflog
reflog records local reference updates, including recent HEAD movements
that git log may not show. It can help recover committed work after a reset or
rebase while the entries and objects remain available. It is not a backup of
uncommitted files, and entries expire.
git reflog
git checkout HEAD@{3}
git cherry-pick
Applies a specific commit from another branch onto your current branch. Useful when you need one particular fix without merging an entire branch.
git cherry-pick abc1234
git branch from a commit
You can create a new branch starting from any commit in the history. This is useful when you realize an older commit would be a better starting point for a fix or feature, or when you need to recover work from a specific point in time.
# create a branch from a specific commit hash
git branch hotfix/login abc1234
# create and switch to it in one step
git checkout -b hotfix/login abc1234
# same thing using the newer switch command
git switch -c hotfix/login abc1234
You can also use references like tags, HEAD~n, or branch names instead of a
commit hash:
# branch from 3 commits ago
git switch -c experiment HEAD~3
# branch from a tag
git switch -c patch-v2 v2.0.0
git restore —source origin/branch file
Restores a file to the version that exists on the remote. Useful when you have made local changes to a file and want to discard them in favor of the remote state.
# restore a single file to the remote main version
git restore --source origin/main -- src/auth/login.ts
# restore multiple files
git restore --source origin/main -- src/auth/login.ts src/auth/utils.ts
Make sure to git fetch first so your remote references are up to date:
git fetch origin
git restore --source origin/main -- path/to/file
Worktrees
A worktree lets you check out multiple branches of the same repository at the
same time, each in its own directory on disk. Instead of stashing or committing
unfinished work to switch branches, you create a new worktree and work on both
branches simultaneously. Worktrees share the object database and most references, while each has its
own index and HEAD. Git normally prevents checking out the same branch in two
worktrees at once.
This is especially useful when you need to review a pull request while working
on a feature, reproduce a bug on main without losing your current state, or
run tests on one branch while developing on another.
git worktree add
Creates a new worktree linked to your repository. You can specify an existing branch or create a new one.
# check out an existing branch in a new directory
git worktree add ../hotfix-auth hotfix/auth
# create a new branch and check it out in a new directory
git worktree add -b feature/new-api ../new-api
Each worktree is a regular directory with a working tree. You can cd into it,
run builds, tests, and commits independently.
git worktree list
Shows all worktrees linked to the repository.
git worktree list
Output looks like:
/home/user/project abc1234 [main]
/home/user/hotfix-auth def5678 [hotfix/auth]
/home/user/new-api abc9012 [feature/new-api]
git worktree remove
Removes a worktree when you are done with it. The branch is kept, only the directory is cleaned up.
git worktree remove ../hotfix-auth
If the worktree has uncommitted changes, git will refuse to remove it unless you
pass --force.
git worktree prune
Cleans up stale worktree references, for example when a worktree directory was
deleted manually instead of using git worktree remove.
git worktree prune
Useful Aliases
Here are the aliases I keep in my .gitconfig:
[alias]
st = status -sb
lg = log --oneline --graph --all --decorate
co = checkout
br = branch -vv
unstage = reset HEAD --
last = log -1 HEAD --stat
These save keystrokes and make the output more readable. The small investment of setting them up pays off every day.