Back to Blog

Git Aliases, Hooks & Statistics from the Terminal — Zsh #29

Sandy LaneSandy Lane

Video: Git Aliases, Hooks & Statistics from the Terminal — Zsh #29 by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Zsh Lesson 29: Git Aliases, Hooks, and Statistics

Set short Git commands with git config alias.X "...". Combine multi-step flows into shell functions. Pretty logs with --graph --oneline --decorate. Pre-commit hooks block bad commits before they happen. git shortlog -sn for contribution stats.

Git from the terminal is faster than any GUI — once you learn the moves.

Shell aliases for git

In ~/.zshrc:

alias gs='git status --short'
alias gco='git checkout'
alias gd='git diff'
alias gds='git diff --stat'
alias glog='git log --oneline --graph --decorate -20'
alias gca='git commit -am'
alias gpu='git push -u origin HEAD'
alias gpl='git pull --rebase'
alias gb='git branch'
alias gba='git branch -a'
alias grm='git rm'

Reload (source ~/.zshrc); gs shows short status, etc.

If you use Oh My Zsh's git plugin, you get 100+ of these for free.

Git config aliases

These live inside Git, in ~/.gitconfig:

git config --global alias.co "checkout"
git config --global alias.br "branch -v"
git config --global alias.last "log -1 --oneline"
git config --global alias.unstage "reset HEAD --"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.lg "log --graph --oneline --decorate -20"

Now git co, git br, git last work without an alias function.

For complex aliases:

git config --global alias.cleanup "!git branch --merged | grep -v '\\*\\|main\\|master' | xargs -n 1 git branch -d"

The ! prefix means "shell command" instead of git subcommand.

Functions for multi-step flows

gcp() {
  git add -A && git commit -m "$*" && git push
}

# usage: gcp fix typo in readme

gcp (git commit-push) — add all, commit with message, push. One command for a quick checkpoint commit.

gnew() {
  git checkout -b "$1" && git push -u origin "$1"
}

Create + track a new branch in one go.

gsync() {
  git fetch --all --prune
  git checkout main && git pull --rebase
}

Fetch + update main.

Pretty git log

git log --oneline --graph --decorate -20

--oneline short form, --graph ASCII branch graph, --decorate shows branch/tag refs.

Make it the default with an alias:

git config --global alias.lg "log --oneline --graph --decorate -20"

For more detail:

git log --pretty=format:"%h %ad %an: %s" --date=short -20
# 6f3a1c2 2026-05-08 Alice: Add login form

Or the elaborate format:

git log --graph --pretty=format:"%C(yellow)%h%C(reset) %C(cyan)%ad%C(reset) %C(green)%an%C(reset) %s" --date=short

Save the format as an alias.

Branch management

git branch -v --sort=-committerdate           # branches by recency

# Delete merged branches (excluding main/master)
git branch --merged | grep -v '\*\|main\|master' | xargs -n 1 git branch -d

# Show branches with their tracked remote
git branch -vv

# Rename current branch
git branch -m new-name

# Delete local + remote
git push origin --delete old-branch
git branch -D old-branch

The "delete merged branches" pipeline is gold for keeping local repos clean.

Pre-commit hooks

In any repo, hooks live in .git/hooks/. Each is a script that runs at a specific Git event.

# .git/hooks/pre-commit
#!/usr/bin/env zsh
set -e

# Block commits with TODO in the diff
if git diff --cached --diff-filter=AM | grep -E "^\+.*TODO"; then
  echo "Commit blocked: contains TODO" >&2
  exit 1
fi

# Run linter
if [[ -f package.json ]]; then
  npm run lint
fi

chmod +x .git/hooks/pre-commit. Now git commit runs it; non-zero exit blocks the commit.

For team-wide hooks (since .git/hooks isn't tracked), use pre-commit (the framework):

pip install pre-commit
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.5.0
    hooks:
      - id: ruff
pre-commit install
# now hooks run on every git commit

Statistics

# Contribution count by author
git shortlog -sn

# Number of commits in this branch
git rev-list --count HEAD

# Lines of code by author
git ls-files | xargs -n1 git blame -e -w | awk -F'[<>]' '{print $2}' | sort | uniq -c | sort -rn

# Commits per day
git log --pretty=format:"%ad" --date=short | sort | uniq -c

# Most-changed files
git log --pretty=format: --name-only | sort | uniq -c | sort -rn | head -20

# Commits in the last month
git log --since="1 month ago" --oneline | wc -l

shortlog -sn is the most useful — quick count by author.

Recent activity

# What did I do today?
git log --author="Alice" --since="6am" --pretty=format:"%h %s"

# Last week's changes
git log --since="1 week ago" --pretty=format:"%h %an: %s"

# Branches with unmerged work
git branch --no-merged main

For a daily standup brief:

gdaily() {
  echo "=== Today ($(date +%Y-%m-%d)) ==="
  git log --author="$(git config user.name)" --since="midnight" --pretty=format:"  %h %s"
}

Stash workflow

git stash                  # save changes
git stash list             # see stashes
git stash pop              # apply latest + remove
git stash apply            # apply latest, keep
git stash apply stash@{2}  # specific stash
git stash drop             # delete latest

# With message
git stash push -m "WIP: refactor parser"

# Just specific files
git stash push -m "WIP frontend" src/components/

Stash for "I need to switch contexts but my work isn't ready to commit."

Diff workflows

git diff                   # unstaged changes
git diff --staged          # staged
git diff main              # vs main
git diff main..feature     # commits in feature not in main
git diff main...feature    # since branches diverged

# Specific file
git diff src/app.py

# Show the change for a single commit
git show abc123

--word-diff highlights at the word level — useful for prose changes:

git diff --word-diff README.md

Searching history

# Lines added/removed mentioning "TODO"
git log -S "TODO" --oneline

# All changes to a file
git log --follow -p src/parser.py

# Who changed this line?
git blame src/parser.py

# When was this line introduced?
git log -L 50,60:src/parser.py

git log -S is great for "when did this string first appear/disappear?"

A daily git workflow

# Morning: sync
git fetch --all --prune
git checkout main && git pull --rebase
git branch --merged | grep -v '\*\|main' | xargs -n1 git branch -d

# Start work
git checkout -b feature/x

# During work
git status -s
git diff
git add -p              # interactive staging
git commit -m "Add X"

# Push
git push -u origin HEAD

# Open PR via gh CLI
gh pr create --fill

gh is GitHub's CLI:

brew install gh
gh auth login

gh pr create
gh pr list
gh pr view 123
gh pr merge 123 --squash
gh issue list
gh repo clone owner/repo

Massive time-saver if you use GitHub.

Common stumbles

git config --global for personal settings. Per-repo settings (no --global) override.

Hooks not running. Check .git/hooks/X exists, is executable. chmod +x .git/hooks/pre-commit.

Slow git status. Large repo? Try:

git config --global core.fsmonitor true
git config --global core.untrackedcache true

Hooks committed to git. They're not — .git/ isn't tracked. Use pre-commit (the tool) for shareable hooks.

Pretty log breaks colors. Add --color=always if you're piping to a pager that handles colors.

git pull creates merge commits. Default behavior. git config --global pull.rebase true to always rebase, or use git pull --rebase explicitly.

Stash with binary files. git stash can be slow on large binaries. For fast switching, just commit a WIP and squash later.

What's next

Lesson 30: the finale. Advanced globs, expansion, ZLE.

Recap

Shell aliases (alias gs='...') and git aliases (git config alias.X "...") for short commands. Functions for multi-step flows (gcp, gnew, gsync). Pretty log: --graph --oneline --decorate. Pre-commit hooks for blocking bad commits; pre-commit framework for team-wide hooks. Stats: git shortlog -sn, git log -S, git blame. gh CLI for GitHub workflows. Daily flow: fetch, prune merged, branch, commit, PR.

Next lesson: advanced Zsh.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.