Git Aliases, Hooks & Statistics from the Terminal — Zsh #29
Video: Git Aliases, Hooks & Statistics from the Terminal — Zsh #29 by Taught by Celeste AI - AI Coding Coach
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 -snfor 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.