DEV Community

Cover image for The git commands I actually run every day
Harshit Luthra
Harshit Luthra

Posted on • Edited on • Originally published at harshit.cloud

The git commands I actually run every day

Originally published at harshit.cloud on 2026-05-20.


I've been using git for a decade and most of what I type still fits on a single hand. The 200-page Pro Git book is wonderful and almost none of it survives contact with a real Tuesday. What survives is a small, boring set of commands that get rerun constantly.

This post is that list, ordered by how often my fingers actually type them. Aliases are from the oh-my-zsh git plugin (enabled in most zsh configs that exist); the full command sits next to the alias so it's portable.

the daily eight

These are the ones I'd type in my sleep. If you're not using all eight already, picking them up pays back inside a week.

gst

git status

gst
Enter fullscreen mode Exit fullscreen mode

I run this between every other command. It's the cheapest sanity check git has. Branch, ahead/behind, staged, unstaged, untracked. Two seconds. If you only learn one alias, learn this one.

glola

git log --oneline --graph --decorate --all

glola | head -30
Enter fullscreen mode Exit fullscreen mode

The one true log. Graph of every branch (local + remote), one line per commit, colored refs. Pipe through head because most of the time you only care about the last 20-30 commits.

gd / gds

git diff / git diff --staged

gd          # what's changed but not staged
gds         # what's staged and about to be committed
Enter fullscreen mode Exit fullscreen mode

gds before every commit. If you set delta as your pager (brew install git-delta, then pager = delta in ~/.gitconfig), the output stops being painful to read.

gcam

git commit -a -m

gcam "fix: trailing slash in webhook URL"
Enter fullscreen mode Exit fullscreen mode

Quick one-line commits for small fixes. For anything bigger I drop the -m and let $EDITOR open so I can write a proper message with a body.

gpsup

git push --set-upstream origin <current-branch>

gpsup
Enter fullscreen mode Exit fullscreen mode

First push of a new branch. The full command is annoying to type, so gpsup figures out the current branch name itself. After the first push, plain gp (just git push) works because upstream is set.

gco / gcb

git checkout / git checkout -b

gco main             # switch to main
gco -                # switch to previous branch
gcb feature/login    # create + switch to new branch
Enter fullscreen mode Exit fullscreen mode

gco - is the one to notice. Like cd - for branches. When you're bouncing between two branches all day, it's a single keystroke each way instead of typing the name.

gpf

git push --force-with-lease

gpf
Enter fullscreen mode Exit fullscreen mode

After rebasing or amending. Always use --force-with-lease, never --force. The lease version refuses to push if someone else has pushed to your branch since your last fetch, saving you from silently overwriting a teammate's work. There is no good reason to ever type --force in 2026.

gfa

git fetch --all --prune

gfa
Enter fullscreen mode Exit fullscreen mode

Refresh every remote, prune deleted remote branches. Run before you start anything that depends on knowing the current state of the world. The --prune half is what makes the cleanup ritual below work.

checkout recent branches

git branch lists alphabetically, which is useless. What you actually want is "that branch from Tuesday," which means sorting by last commit:

git config --global alias.recent \
  "for-each-ref --sort=-committerdate refs/heads/ \
   --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) \
             %(color:green)(%(committerdate:relative))%(color:reset) %(contents:subject)'"

git recent | head -10
Enter fullscreen mode Exit fullscreen mode

That covers looking. For switching, pipe the same list into fzf and you never type a branch name again:

# fco: fuzzy-checkout a recent branch
fco() {
  local branch
  branch=$(git for-each-ref --sort=-committerdate refs/heads/ \
             --format='%(refname:short)' \
           | fzf --height 40% --reverse \
                 --preview 'git log --oneline --decorate --color=always -15 {}')
  [ -n "$branch" ] && git checkout "$branch"
}
Enter fullscreen mode Exit fullscreen mode

Branches arrive sorted by recency, so the one you want is almost always in the top three. Type two letters of its name, Enter, done. The preview pane shows the branch's recent commits so you can confirm it's the right Tuesday. gco - still wins for bouncing between exactly two branches; fco wins for everything else. (brew install fzf if you don't have it. You want it for Ctrl-R history search anyway.)

the cleanup ritual

Run this weekly. If you've ever scrolled through 80 stale branches looking for the one you actually want, you already know why.

The easy half deletes every local branch whose tip is already in main:

gfa
git branch --merged main | grep -v '\*\|main\|master' | xargs -n1 git branch -d
Enter fullscreen mode Exit fullscreen mode

Works only if your team uses merge commits. Most don't. GitHub's "Squash and merge" creates a brand-new commit on main with a different SHA, so git branch --merged never catches your local branch — its commits aren't in main's history at all.

The workaround: after gfa, any branch whose tracked remote was deleted shows as [gone]. Those are your merged-and-deleted PRs.

# git-gone: delete local branches whose remote tracking branch is gone
git-gone() {
  git fetch --prune
  local gone
  gone=$(git for-each-ref --format '%(refname:short) %(upstream:track)' refs/heads \
         | awk '$2 == "[gone]" {print $1}')
  if [ -z "$gone" ]; then
    echo "No gone branches"
    return
  fi
  echo "$gone"
  echo -n "Delete these? [y/N] "
  read -r confirm
  [[ "$confirm" == "y" ]] && echo "$gone" | xargs -r git branch -D
}
Enter fullscreen mode Exit fullscreen mode

Or install git-trim (brew install git-trim), which is smarter. It detects patch-equivalent commits, so it catches squash-merges even when the upstream tracking ref isn't [gone].

git trim                # dry-run
git trim --confirm      # actually delete
Enter fullscreen mode Exit fullscreen mode

This is the closest thing to "did my PR ship?" you can ask git directly.

the weekly four

Not in your fingers yet, but should be.

git switch and git restore

git switch -c new-feature           # create + switch
git restore --staged file.txt       # unstage
git restore --source=abc123 file.go # restore single file from any commit
Enter fullscreen mode Exit fullscreen mode

switch and restore split the four jobs checkout used to do. The one I reach for most is restore --source=<sha> <path>. Translation: "grab this single file from three commits ago without touching anything else."

interactive rebase with autosquash

git commit --fixup=abc123       # fixup commit targeting abc123
# ... keep working ...
git rebase -i --autosquash main # all fixups slot into place automatically
Enter fullscreen mode Exit fullscreen mode

The single biggest workflow win I've found in ten years of git. While reviewing your own PR you find a bug four commits back. Don't fix it on top — --fixup=<sha> creates a commit targeting the offender, and the autosquash rebase squashes everything into place when you're done. Install git-absorb (brew install git-absorb) and it even picks the target SHA for you: edit the files, run git absorb --and-rebase, done.

git reflog, the universal undo

git reflog
git reset --hard HEAD@{5}
Enter fullscreen mode Exit fullscreen mode

Every change to HEAD is logged for 90 days. Bad rebase? reflog. Deleted branch? reflog. There is almost nothing in git you can't undo if you know about it.

git worktree

git worktree add ../proj-hotfix hotfix/prod-down
git worktree remove ../proj-hotfix
Enter fullscreen mode Exit fullscreen mode

Need to fix a prod bug while halfway through a feature? Don't stash. worktree add gives you a second checkout in a sibling directory, sharing the same .git. I use it constantly for "let me review your PR" without leaving my own branch.

set it once

Five config lines and a daemon. Enable, forget.

git config --global rerere.enabled true          # remember conflict resolutions, replay them
git config --global push.default current         # `git push` pushes current branch to same name
git config --global push.autoSetupRemote true    # first push sets upstream automatically
git config --global diff.algorithm histogram     # cleaner diffs than the default myers
git config --global merge.conflictStyle zdiff3   # conflict markers include the common ancestor
git maintenance start                            # background gc/prefetch on a schedule
Enter fullscreen mode Exit fullscreen mode

autoSetupRemote retires gpsup entirely. zdiff3 shows the original code both sides diverged from; once you've used it, plain <<<<<<< markers feel like flying blind.

when something is broken

Not daily, but when the question is "when did this start," nothing else answers it:

git log -S "functionName"          # pickaxe: commits where this string was added or removed
git blame -w -C -C -C file.go      # blame the logic's actual author, not the formatter
git log -p --follow file.go        # full file history, including across renames
git range-diff @{u} @              # what a rebase actually changed; run before force-pushing
Enter fullscreen mode Exit fullscreen mode

-S searches the content of the diff, not commit messages — different thing entirely from --grep. And plain blame gives credit to whoever last ran Prettier; -w -C -C -C follows the code across whitespace changes, moves, and file boundaries to the person who wrote the logic.

the four tools worth installing today

  • fzf (brew install fzf). Powers the fco branch picker above, plus fuzzy Ctrl-R history.
  • git-absorb (brew install git-absorb). Auto-fixup commits without picking SHAs.
  • delta (brew install git-delta). Diff and blame output that doesn't hurt to look at.
  • lazygit (brew install lazygit). TUI for the operations that are tedious on CLI: partial commits, stash management, conflict resolution.

This post used to end with two AI shell helpers for the stuff git can't tell you; those now live in their own TIL.

ten years in, the surprise

After a decade, the command I run most isn't commit. It isn't push. It's gst, hundreds of times a day, between every other operation. The most-used git command in my shell is the one that does nothing.

Top comments (0)