Git Reference

Git version control patterns. From daily basics to repository surgery.

Topics

Topic Description

Basics

Status, diff, log, add, commit

Branches

Create, switch, merge, rebase, conflicts

History

Blame, bisect, log search, show — inspecting the past

Rewriting

Reset, revert, amend, interactive rebase, cherry-pick, reflog

Stash

Save, retrieve, inspect work-in-progress

Remotes

Push, pull, fetch, multi-remote, SSH

Tags

Lightweight, annotated, versioning, release tagging

Worktrees

Multiple working directories, parallel work

Filter-Repo

Repository surgery — remove secrets, extract projects

Config

Aliases, hooks, .gitignore, settings

Inspecting State

What changed? (the first command you run)
git status
Unstaged changes — what you haven’t `git add’d yet
git diff
Staged changes — what’s about to be committed
git diff --staged
Last 10 commits — one line each
git log --oneline -10
Visual branch history — see merges and divergence
git log --graph --all --oneline
Who wrote this line? (blame with context)
git blame -L 40,60 src/domus_api/main.py
Show a specific commit’s changes
git show abc1234

Staging & Committing

Stage all changes (tracked + untracked)
git add -A
Stage interactively — choose hunks within files
git add -p

This walks through each change and asks y/n/s/e — stage this hunk? Split it? Edit it? The senior-engineer move: never git add -A blindly. Use -p to review what you’re committing.

Commit with message
git commit -m "feat: add association graph endpoints"
Commit with heredoc (multiline message)
git commit -m "$(cat <<'EOF'
feat: association engine Phase 1 — core data structure

AssociationGraph class with bidirectional dicts.
22 tests, all passing.
EOF
)"
Amend the last commit (change message or add forgotten files)
git add forgotten-file.py
git commit --amend
Never amend a commit that’s already been pushed. It rewrites history. Use a new commit instead.

Branches

List all branches (local + remote)
git branch -a
Create and switch to a new branch
git checkout -b feature/association-engine
Switch to existing branch
git checkout main
Delete a merged branch
git branch -d feature/old-branch
Delete an unmerged branch (force)
git branch -D feature/abandoned-experiment

Merging & Rebasing

Merge a branch into current
git merge feature/association-engine
Rebase onto main (cleaner history — replays your commits on top)
git rebase main
Cherry-pick a specific commit from another branch
git cherry-pick abc1234

Undoing Things

Undo last commit — keep changes staged
git reset --soft HEAD~1
Undo last commit — keep changes unstaged
git reset HEAD~1
Undo last commit — discard everything (DESTRUCTIVE)
git reset --hard HEAD~1
Match remote exactly — discard all local changes (DESTRUCTIVE)
git reset --hard origin/main
--hard destroys uncommitted work. Check git stash first.
Reflog — your safety net. Shows every HEAD movement.
git reflog

If you accidentally reset --hard, the reflog has the commit hash. You can git reset --hard <hash> to recover.

Stash — Save Work Without Committing

Stash current changes
git stash
Stash with a descriptive message
git stash push -m "WIP: association engine CLI"
List stashes
git stash list
Apply most recent stash (keep in stash list)
git stash apply
Apply and remove from stash list
git stash pop

Remotes

List remotes
git remote -v
Push and set upstream tracking
git push -u origin main
Fetch all remotes and prune deleted branches
git fetch --all --prune
Change remote URL (HTTPS → SSH)
git remote set-url origin git@github.com:EvanusModestus/association-engine.git

This is the fix when gh repo create defaults to HTTPS but your SSH agent is loaded.

Tags

Create annotated tag (for releases)
git tag -a v0.1.0 -m "Association Engine — core data structure"
Push tags to remote
git push --follow-tags
List tags
git tag -l

Worktrees — Multiple Working Directories

Create a worktree for parallel work
git worktree add ../association-engine-fix hotfix-branch
List active worktrees
git worktree list
Remove a worktree when done
git worktree remove ../association-engine-fix

GitHub CLI (gh)

Create private repo from local directory and push
gh repo create association-engine --private --source . --remote origin --push
Fix: gh defaults to HTTPS, switch to SSH
git remote set-url origin git@github.com:EvanusModestus/association-engine.git
Fix: uv init creates master, GitHub expects main
git branch -m master main
git push -u origin main
Full repo creation workflow (tested 2026-04-07)
cd ~/atelier/_projects/personal/my-project
uv init .
git add -A && git commit -m "feat: initial commit"
gh repo create my-project --private --source . --remote origin --push
git remote set-url origin git@github.com:EvanusModestus/my-project.git
git branch -m master main
git push -u origin main

Configuration & Debugging

Show all config with source files
git config --list --show-origin
Debug why a file is ignored
git check-ignore -v path/to/file
Filter-repo — analyze repository history
git filter-repo --analyze
Find large files in history
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sort -k3 -rn | head -20

GitHub CLI (gh) — Automation & API

gh is not just for creating repos. It’s a full GitHub API client with jq-pipeable JSON output.

Repository Management

Create private repo from local directory
gh repo create association-engine --private --source . --remote origin --push
Fix HTTPS → SSH after creation
git remote set-url origin git@github.com:EvanusModestus/association-engine.git
git branch -m master main
git push -u origin main
List your repos (sorted by last push)
gh repo list --limit 20 --json name,pushedAt | jq -r '.[] | "\(.pushedAt)\t\(.name)"' | sort -r
Clone a repo
gh repo clone EvanusModestus/domus-api
View repo info
gh repo view EvanusModestus/domus-api --json name,description,defaultBranchRef | jq

Pull Requests

Create a PR
gh pr create --title "feat: association graph endpoints" --body "Adds /associations API"
List open PRs
gh pr list
List PRs as JSON → jq
gh pr list --json number,title,state,author | jq '[.[] | {number, title, author: .author.login}]'
View PR details
gh pr view 42
View PR checks/CI status
gh pr checks 42
Merge a PR
gh pr merge 42 --squash --delete-branch

Issues

Create an issue
gh issue create --title "Add YAML persistence to association engine" --label "enhancement"
List issues
gh issue list
Issues as JSON → filter with jq
gh issue list --json number,title,labels --limit 50 | jq '[.[] | {number, title, labels: [.labels[].name]}]'

GitHub API — Raw Power

gh api gives you direct access to any GitHub REST or GraphQL endpoint. This is where it gets powerful.

Get repo details
gh api repos/EvanusModestus/domus-api | jq '{name, stars: .stargazers_count, size: .size, language}'
List all repos with size and language
gh api user/repos --paginate | jq -r '.[] | "\(.size)\t\(.language)\t\(.name)"' | sort -rn | head -20
Get recent commits across a repo
gh api repos/EvanusModestus/domus-api/commits --jq '.[].commit.message' | head -10
PR comments on a specific PR
gh api repos/EvanusModestus/domus-api/pulls/1/comments | jq '[.[] | {user: .user.login, body: .body[:80]}]'
Get all open issues with labels
gh api repos/EvanusModestus/domus-captures/issues | jq '[.[] | {number, title, labels: [.labels[].name]}]'

Cross-Repo Automation

Status of all your repos — unpushed commits, dirty state
gh repo list --limit 50 --json name,pushedAt,defaultBranchRef | jq -r '.[] | "\(.pushedAt[:10])\t\(.name)"' | sort -r
Find repos by language
gh repo list --language python --json name,language | jq -r '.[] | "\(.language)\t\(.name)"'
Bulk clone all your repos (careful — downloads everything)
gh repo list --limit 100 --json nameWithOwner | jq -r '.[].nameWithOwner' | xargs -I{} gh repo clone {}
Create release with auto-generated notes
gh release create v0.1.0 --generate-notes --title "Association Engine v0.1.0"

gh + jq Patterns

Count repos by language
gh repo list --limit 100 --json language | jq -r '.[].language' | sort | uniq -c | sort -rn
Find your largest repos
gh repo list --limit 50 --json name,diskUsage | jq -r '.[] | "\(.diskUsage)\t\(.name)"' | sort -rn | head -10
Export all repo names for scripting
gh repo list --limit 100 --json name | jq -r '.[].name' > /tmp/my-repos.txt

Multi-Repo Operations

Push multiple repos in parallel
git -C ~/atelier/_bibliotheca/domus-captures push &
git -C ~/atelier/_projects/personal/domus-api push &
git -C ~/atelier/_projects/personal/association-engine push &
wait
Status across all repos (zsh-safe — avoids read-only status variable)
for d in ~/atelier/_bibliotheca/domus-*/ ~/atelier/_projects/personal/*/; do
    [[ -d "$d/.git" ]] || continue
    changed=$(git -C "$d" status --porcelain | wc -l)
    ahead=$(git -C "$d" rev-list --count @{upstream}..HEAD 2>/dev/null || echo "?")
    printf "%-35s %s files changed, %s unpushed\n" "$(basename "$d")" "$changed" "$ahead"
done