Skip to content

Best Practices

The habits of effective developers: Git best practices are like the habits of a good housekeeper — they keep your repository clean, your history readable, and your team productive. Small commits, clear messages, and consistent workflows prevent the chaos that accumulates in messy repositories.

Why it matters: Following best practices prevents common disasters — losing work, introducing bugs through messy merges, and spending hours deciphering cryptic commit messages from six months ago.

The key insight: Write commit messages for your future self — when you are debugging at 2 AM, a clear commit message like “fix: handle null user in login flow” saves more time than any optimisation.

Terminal window
## Identity
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
## Default branch
git config --global init.defaultBranch main
# Pull strategy: rebase instead of merge (cleaner history)
git config --global pull.rebase true
# Rebase autosquash (for fixup workflow)
git config --global rebase.autoSquash true
# Default editor
git config --global core.editor "vim"
# Credential caching
git config --global credential.helper cache --timeout=3600
# Push: only push the current branch
git config --global push.default current
# Diff: use better diff algorithm
git config --global diff.algorithm histogram
# Rerere: remember conflict resolutions
git config --global rerere.enabled true
git config --global rerere.autoupdate true
# Transfer: optimize for large repos
git config --global core.compression 9
git config --global pack.threads 0
# Status: show submodule summary
git config --global status.submoduleSummary true
# Alias: common shortcuts
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"
git config --global alias.last "log -1 HEAD"
git config --global alias.unstage "restore --staged"
git config --global alias.amend "commit --amend --no-edit"

Each commit should represent one logical change. This means:

Terminal window
# Bad: one commit with unrelated changes
git add src/auth.c src/utils.c docs/README.md
git commit -m "Various improvements"
# Good: separate commits for each logical change
git add src/auth.c
git commit -m "feat(auth): add JWT token validation"
git add src/utils.c
git commit -m "fix(utils): handle null pointer in string_split"
git add docs/README.md
git commit -m "docs: update installation instructions"
ReasonExplanation
Bisectabilitygit bisect can pinpoint the exact commit that introduced a bug
Revertibilitygit revert <hash> undoes one specific change without affecting others
ReviewabilityReviewers can understand each change in isolation
Cherry-pickingIndividual fixes can be cherry-picked to release branches
History archaeologyFuture developers can understand why each change was made

Use git add -p to stage specific hunks of a file:

Terminal window
$ git add -p src/auth.c
# Stage this hunk [y,n,q,a,d,/,s,e,?]?
# y - stage this hunk
# n - do not stage this hunk
# s - split the hunk into smaller pieces
# e - manually edit the hunk
# Build artifacts
/build/
/dist/
*.o
*.so
*.dll
*.exe
# Dependencies
/node_modules/
/vendor/
.venv/
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Environment
.env
.env.local
.env.*.local
# Secrets
*.pem
*.key
credentials.json
  • Configuration files that others need (.eslintrc.json``tsconfig.json).
  • Lockfiles (package-lock.json``yarn.lock``Cargo.lock).
  • .gitignore itself.

For files that should be ignored in all repositories (OS-specific files, editor files):

~/.gitignore_global
# Configure a global gitignore
$ git config --global core.excludesfile ~/.gitignore_global
.DS_Store
Thumbs.db
*.swp
.vscode/

Secrets (API keys, passwords, tokens) should never be committed to a Git repository, even in private repos:

Terminal window
# Use environment variables
export API_KEY="your-key-here"
# Or use a secrets manager
# - HashiCorp Vault
# - AWS Secrets Manager
# - .env files (gitignored)
Terminal window
# Use git-secrets (AWS)
$ git secrets --install
$ git secrets --register-aws
# Use truffleHog (open source)
$ trufflehog git file://.git --only-verified

Sign commits with GPG or SSH to verify authorship:

Terminal window
# GPG signing
$ git config --global user.signingkey YOUR_GPG_KEY_ID
$ git config --global commit.gpgsign true
# SSH signing (Git 2.34+)
$ git config --global gpg.format ssh
$ git config --global user.signingkey ~/.ssh/id_ed25519.pub
$ git config --global commit.gpgsign true
Terminal window
# Partial clone (fetch only needed objects)
$ git clone --filter=blob:none https://github.com/user/large-repo.git
# Shallow clone (limited history)
$ git clone --depth=1 https://github.com/user/repo.git
# Sparse checkout (only specific directories)
$ git clone --sparse https://github.com/user/monorepo.git
$ cd monorepo
$ git sparse-checkout set src/packages/core
# File system monitoring (Git 2.36+)
$ git config core.fsmonitor true # Uses watchman or fsmonitor-watchman
Terminal window
# Enable untracked cache
$ git config core.untrackedCache true
# Enable fsmonitor for faster status (requires watchman)
$ git config core.fsmonitor true

git worktree allows you to have multiple working directories from the same repository, each on a different branch:

Terminal window
# Create a worktree for a feature branch
$ git worktree add ../feature-auth feature-auth
# Now you can work in both directories simultaneously
$ cd ../feature-auth
# ... make changes, commit ...
$ cd ../main-repo
# ... continue working on main ...

Use cases:

  • Run long tests on one branch while developing on another.
  • Compare two branches side-by-side in your IDE.
  • Hotfix production while continuing feature work.
Terminal window
# List worktrees
$ git worktree list
# Remove a worktree
$ git worktree remove ../feature-auth
Terminal window
# Shortcuts
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
# Enhanced log
git config --global alias.lg "log --oneline --graph --all --decorate"
# Show the last commit
git config --global alias.last "log -1 HEAD --stat"
# Diff staged changes
git config --global alias.dc "diff --cached"
# Unstage a file
git config --global alias.unstage "restore --staged"
# Amend without editing message
git config --global alias.amend "commit --amend --no-edit"
# Show which branches contain a commit
git config --global alias.contains "branch --contains"
# Quick stash with message
git config --global alias.save "stash push -m"
  1. Neglecting to normalise database designs, leading to data redundancy and update anomalies.

  2. Confusing an algorithm with a program. An algorithm is a step-by-step procedure, not its implementation in code.

  3. Writing pseudocode that is too language-specific rather than using standard algorithmic constructs.

  4. Forgetting that O(nlogn)O(n \log n) average-case for quicksort becomes O(n2)O(n^2) worst-case on already sorted input.

The key principles covered in this topic are linked in the sub-pages above. Focus on understanding the definitions, applying the formulas or frameworks, and evaluating strengths and limitations of each approach.

Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.