Git Basics
Core Git operations for everyday use.
Status Fundamentals
# BASIC STATUS
git status # Full status output
git status --short # Compact: XY filename format
git status -sb # Short + branch info
# STATUS CODES (XY format)
# X = staging area, Y = working tree
# ?? = untracked
# M = modified in staging
# M = modified in working tree
# MM = modified in both
# A = added to staging
# D = deleted
# R = renamed
# C = copied
# U = unmerged (conflict)
# FILTER BY PATH
git status -- docs/ # Only docs/ changes
git status --porcelain # Machine-readable format
# PARSE STATUS WITH AWK
git status --porcelain | awk '{print $2}' # Just filenames
git status --porcelain | awk '/^M/{print $2}' # Only staged modified
git status --porcelain | awk '/^\?\?/{print $2}' # Only untracked
# COUNT CHANGES
git status --porcelain | wc -l # Total changed files
git status --porcelain | grep -c '^??' # Count untracked
git status --porcelain | grep -c '^M' # Count staged
# IGNORED FILES
git status --ignored # Show ignored files
git status --ignored --short # Compact with ignored
Diff Operations
# BASIC DIFF
git diff # Unstaged changes vs staging
git diff --staged # Staged changes vs last commit
git diff --cached # Same as --staged
git diff HEAD # All changes vs last commit
# DIFF STATISTICS
git diff --stat # Summary: files changed, +/-
git diff --stat --staged # Staged changes summary
git diff --numstat # Machine-readable: adds dels file
git diff --shortstat # Just totals
# DIFF SPECIFIC FILES
git diff -- path/to/file.txt # Single file
git diff -- '*.adoc' # Glob pattern (quote it!)
git diff HEAD~3 -- docs/ # Directory against 3 commits ago
# DIFF BETWEEN REFS
git diff main feature # Between branches
git diff main...feature # Changes since branches diverged
git diff v1.0..v2.0 # Between tags
git diff HEAD~5 HEAD # Last 5 commits
# WORD-LEVEL DIFF
git diff --word-diff # Show word changes inline
git diff --word-diff=color # Colored word diff
git diff --color-words # Compact colored words
# IGNORE WHITESPACE
git diff -w # Ignore all whitespace
git diff --ignore-space-change # Ignore space amount changes
git diff --ignore-blank-lines # Ignore blank line changes
# DIFF NAMES ONLY
git diff --name-only # Just filenames
git diff --name-status # Filenames with status (M/A/D)
git diff --name-only HEAD~5 # Changed files in last 5 commits
# DIFF FOR CODE REVIEW
git diff origin/main...HEAD # What this branch adds
git diff origin/main --stat # Summary vs remote main
Log Fundamentals
# BASIC LOG
git log # Full log (q to quit)
git log --oneline # Hash + message
git log --oneline -10 # Last 10 commits
git log -n 5 # Last 5 commits (verbose)
# LOG WITH GRAPH
git log --oneline --graph # ASCII branch graph
git log --oneline --graph --all # All branches
git log --oneline --graph --decorate # Show refs (branches, tags)
# LOG FORMATTING
git log --pretty=format:"%h %s" # Custom: hash + subject
git log --pretty=format:"%h %an %ar %s" # Hash, author, relative time, subject
git log --format="%H" # Full hashes only
git log --format="%ae" # Author emails only
# COMMON FORMATS
git log --pretty=fuller # Author + committer dates
git log --pretty=short # Just author + subject
git log --pretty=reference # For changelog style
# LOG FILTERING
git log --author="Evan" # By author
git log --grep="fix" # By message content
git log --since="2 weeks ago" # Time-based
git log --after="2024-01-01" # After date
git log --until="yesterday" # Before time
# LOG PATH FILTERING
git log -- path/to/file # Single file history
git log --follow -- file.txt # Track renames
git log -- docs/ # Directory history
git log -- '*.adoc' # By extension
# LOG FOR AUDIT
git log --stat # With file changes
git log -p # With full diff (patch)
git log -p -1 # Last commit with diff
git log --name-only # Just changed filenames
git log --name-status # Filenames with M/A/D status
# USEFUL ALIASES
# git config --global alias.lg "log --oneline --graph --all --decorate"
# git config --global alias.last "log -1 HEAD --stat"
# git config --global alias.recent "log --oneline -15"
Add Strategies
# BASIC ADD
git add file.txt # Single file
git add dir/ # Directory
git add . # Current directory (recursive)
git add -A # All changes (adds, mods, deletes)
git add --all # Same as -A
# ADD VS ADD -A VS ADD .
# git add . = adds/modifies from current dir, doesn't stage deletes
# git add -A = adds/modifies/deletes from entire repo
# PREFER: git add -A (most predictable)
# INTERACTIVE ADD
git add -p # Patch mode (hunk by hunk)
# y = stage this hunk
# n = skip this hunk
# s = split into smaller hunks
# e = edit hunk manually
# q = quit
# ADD WITH PATTERNS
git add '*.adoc' # All .adoc files (quote!)
git add docs/*.md # All .md in docs/
git add docs/**/*.adoc # Recursive .adoc in docs/
# ADD SPECIFIC CHANGES
git add -N file.txt # Intent to add (track, no content)
git add --ignore-removal . # Adds and modifies only (no deletes)
# UNSTAGE (opposite of add)
git reset HEAD file.txt # Unstage file (keep changes)
git restore --staged file.txt # Modern: unstage file
git reset # Unstage everything
# ADD IGNORED FILES (careful!)
git add -f ignored_file # Force add ignored file
# WHY: Temporarily track something in .gitignore
# DRY RUN
git add -n . # Show what would be added
git add --dry-run -A # Preview all changes
Commit Patterns
# BASIC COMMIT
git commit -m "message" # Quick commit
git commit # Opens editor for message
# COMMIT WITH BODY
git commit -m "subject" -m "body" # Separate subject and body
# OR use heredoc (preferred for multi-line):
git commit -m "$(cat <<'EOF'
subject line here
Body paragraph explaining the change in detail.
Can span multiple lines.
- Bullet point 1
- Bullet point 2
EOF
)"
# ADD AND COMMIT
git commit -am "message" # Stage tracked + commit (-a)
# NOTE: -a only stages TRACKED files, not new ones
# PREFER: git add -A && git commit -m "message"
# AMEND COMMIT (unpushed only!)
git commit --amend # Edit last commit (message + files)
git commit --amend -m "new message" # Just change message
git commit --amend --no-edit # Add staged files, keep message
# EMPTY COMMIT (CI triggers, markers)
git commit --allow-empty -m "trigger CI"
# COMMIT CONVENTIONS (Conventional Commits)
# <type>(<scope>): <subject>
# Types: feat, fix, docs, style, refactor, test, chore
# Examples:
git commit -m "feat(auth): add OAuth2 login support"
git commit -m "fix(api): handle null response in parser"
git commit -m "docs(readme): update installation section"
git commit -m "refactor(db): extract connection pool logic"
# COMMIT MESSAGE BEST PRACTICES
# - Subject: imperative mood ("Add feature" not "Added feature")
# - Subject: 50 chars max, no period
# - Body: wrap at 72 chars
# - Body: explain WHAT and WHY, not HOW
# SIGN COMMITS (GPG)
git commit -S -m "signed commit" # GPG sign
git commit -s -m "signed-off commit" # Add Signed-off-by line
# FIX COMMIT (squash later)
git commit --fixup=<commit> # Marks for fixup
git commit --squash=<commit> # Marks for squash
# Then: git rebase -i --autosquash
Infrastructure Repository Workflows
# DOMUS-* REPO WORKFLOW
# Standard pattern for documentation repos
# 1. Start work (check status across repos)
for repo in domus-docs domus-infra-ops domus-captures; do
echo "=== $repo ==="
git -C ~/atelier/_bibliotheca/$repo status --short
done
# 2. Make changes, stage, verify
git add -A
git diff --staged --stat # Review what's staged
# 3. Commit with descriptive message
git commit -m "$(cat <<'EOF'
docs(runbook): Add Vault SSH CA troubleshooting section
- Add PTY allocation failure fix
- Document clock skew symptoms
- Add principal mismatch debugging
EOF
)"
# 4. Push (with SSH agent)
SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket git push origin main
# MULTI-REPO PUSH (all domus-* repos)
for repo in domus-docs domus-infra-ops domus-captures domus-ise-linux; do
echo "=== Pushing $repo ==="
SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket \
git -C ~/atelier/_bibliotheca/$repo push origin main
done
# ANTORA BUILD TRIGGER
# Spoke repo changes need aggregator push:
git -C ~/atelier/_bibliotheca/domus-docs commit --allow-empty \
-m "chore: Trigger rebuild for $spoke_repo updates"
git -C ~/atelier/_bibliotheca/domus-docs push origin main
# CHECK ALL REPOS STATUS
for repo in ~/atelier/_bibliotheca/domus-*/; do
name=$(basename "$repo")
ahead=$(git -C "$repo" rev-list --count origin/main..HEAD 2>/dev/null)
behind=$(git -C "$repo" rev-list --count HEAD..origin/main 2>/dev/null)
dirty=$(git -C "$repo" status --porcelain | wc -l)
echo "$name: +$ahead -$behind ~$dirty"
done
# BATCH OPERATION PATTERN
# Safe way to run commands across repos
for repo in domus-infra-ops domus-captures domus-ise-linux; do
echo "=== $repo ==="
git -C ~/atelier/_bibliotheca/$repo fetch --all
git -C ~/atelier/_bibliotheca/$repo status --short
done
Advanced Status Techniques
# MACHINE-READABLE STATUS
git status --porcelain # Stable format for scripting
git status --porcelain=v2 # Even more detailed format
# PARSE FOR AUTOMATION
# Find all untracked files:
git status --porcelain | awk '/^\?\?/{print $2}'
# Find all modified (not staged):
git status --porcelain | awk '/^ M/{print $2}'
# Find all staged files:
git status --porcelain | awk '/^[MARCD]/{print $2}'
# CHECK IF DIRTY (for scripts)
if [ -n "$(git status --porcelain)" ]; then
echo "Uncommitted changes exist"
fi
# CHECK IF AHEAD/BEHIND
git status -sb | head -1
# Output: ## main...origin/main [ahead 3, behind 1]
# PARSE AHEAD/BEHIND
git rev-list --left-right --count origin/main...HEAD
# Output: behind ahead
# BRANCH TRACKING STATUS
git branch -vv # Show tracking branches
git for-each-ref --format='%(refname:short) %(upstream:short) %(upstream:track)' refs/heads
# STASH STATUS
git stash list | wc -l # Count stashes
# WORKTREE STATUS
git worktree list # All worktrees
# SUBMODULE STATUS
git submodule status # Submodule state
Git Basics Gotchas
# WRONG: Using add . expecting all changes
git add .
# Only adds from current directory! Doesn't stage deletes!
# CORRECT: Use add -A for all changes
git add -A
# Stages adds, modifications, AND deletions from entire repo
# WRONG: Committing without checking staged content
git commit -m "quick fix"
# May include unintended changes!
# CORRECT: Review before commit
git diff --staged --stat
git commit -m "quick fix"
# WRONG: Amending pushed commits
git push origin main
git commit --amend
git push origin main
# ERROR: rejected (non-fast-forward)
# CORRECT: Never amend pushed commits (unless force-push OK)
git commit --amend
git push --force-with-lease origin main # Only if you know what you're doing!
# WRONG: Committing secrets
git add .env
git commit -m "add config"
# SECRET NOW IN HISTORY FOREVER!
# CORRECT: Use .gitignore + pre-commit hooks
echo ".env" >> .gitignore
# Better: use git-filter-repo to remove if already committed
# WRONG: Assuming git diff shows all changes
git diff
# Only shows UNSTAGED changes!
# CORRECT: Know your diff targets
git diff # Unstaged vs staging
git diff --staged # Staged vs last commit
git diff HEAD # All changes vs last commit
# WRONG: git log without limits
git log
# Scrolls forever in large repos!
# CORRECT: Always limit or use pager
git log --oneline -20
git log --since="1 week ago"
# WRONG: Expecting -m to handle newlines
git commit -m "line1\nline2"
# Creates literal \n in message!
# CORRECT: Use heredoc or multiple -m flags
git commit -m "line1" -m "line2"
# Or: git commit -m "$(cat <<'EOF' ... EOF)"
Quick Reference
# ESSENTIAL BASICS
git status -sb # Short status + branch
git diff --stat # Change summary
git log --oneline -10 # Recent commits
git add -A # Stage everything
git commit -m "message" # Commit
# STATUS CODES (git status --porcelain)
# ?? = untracked M = staged modified
# M = unstaged mod MM = both staged+unstaged
# A = staged added D = deleted
# R = renamed U = unmerged (conflict)
# DIFF TARGETS
git diff # Working vs staging
git diff --staged # Staging vs commit
git diff HEAD # Working vs commit
git diff main...HEAD # Branch changes
# LOG FORMATS
git log --oneline --graph --all # ASCII graph
git log --pretty=format:"%h %an %s" # Custom format
git log -p -1 # Last commit with diff
git log --name-only # Changed files list
# ADD PATTERNS
git add -A # All changes (preferred)
git add -p # Interactive hunks
git add '*.adoc' # By extension
# COMMIT PATTERNS
git commit -m "type(scope): message" # Conventional
git commit --amend --no-edit # Add to last (unpushed!)
git commit --allow-empty -m "trigger" # CI trigger