Knowledge/Git/Introduction
20 Chapters
120+ Commands
Living Document
Free License
Engineering Publication · Syed Omar Ibn Feroj← Back to portfolio
Git
Engineering Notes · Open Knowledge Repository

Git — Engineering
Notes for Operators

A field-tested Git reference distilled from operating engineering teams across telecom, enterprise and infrastructure programs. Written as I use it day-to-day — terse on theory, dense on the commands and patterns that actually move work forward.

20
Chapters
120+
Commands
Living
Document
Free
License
Ch. 01
Getting Started with Git
Creating repositories, adding files, making commits, and connecting to remote origins.
1.1 — Create your first repository, then add and commit files

Navigate to the directory you want to place under version control and initialise an empty Git repository:

Shell
$ git init
# Creates a hidden .git folder — the repo's plumbing

$ git status
# Review what Git will add before staging anything

$ git add <file1> <file2>   # Stage specific files
$ git add .                  # Stage everything in current directory

$ git commit -m "Initial commit"
# Creates a snapshot of staged changes
1.2 — Clone a repository
Shell
$ git clone https://example.com/user/repo.git
# Full clone with history

$ git clone https://example.com/user/repo.git my-folder
# Clone into a specific folder name

$ git clone --depth 1 https://example.com/user/repo.git
# Shallow clone — latest snapshot only, faster download

$ git clone --branch develop https://example.com/user/repo.git
# Clone and check out a specific branch
1.3–1.5 — Setting up username, email, and upstream remote
Shell
# Set identity for all repos (stored in ~/.gitconfig)
$ git config --global user.name  "Your Name"
$ git config --global user.email "you@example.com"

# Add a remote (e.g. after git init)
$ git remote add origin https://example.com/owner/repo.git

# Set up upstream fork tracking
$ git remote -v
# origin  https://example.com/myusername/repo.git (fetch)
$ git remote set-url upstream https://example.com/projectusername/repo.git

# Push and track remote branch
$ git push -u origin main
💡
Run git config --list to view all your current configuration settings. Use --global for user-wide settings and omit it for repo-specific overrides.
1.6–1.8 — Learning about a command & SSH setup
Shell
# Get full docs for any command
$ git help diff
$ git diff --help
$ git checkout -h    # Quick flag reference

# SSH key setup
$ ls -al ~/.ssh        # Check for existing keys
$ ssh-keygen -t ed25519 -C "you@example.com"
$ cat ~/.ssh/id_ed25519.pub
# Copy this output → your Git host's SSH key settings
$ ssh -T git@example.com   # Test connection
Ch. 02
Browsing the History
Viewing logs, searching commits, filtering output, and understanding the history graph.
2.1 — Regular Git Log
Shell
$ git log                        # Full history, most recent first
$ git log -5                      # Last 5 commits only
$ git log --oneline              # Compact one-line-per-commit view
$ git log --author="Alice"       # Filter by author
$ git log --since="2024-01-01"  # Since a date
$ git log --grep="bugfix"       # Search commit messages
$ git log -p README.md          # Show patches for a specific file
$ git log --follow old-name.txt  # Track file through renames
2.2–2.3 — Pretty log & colorized output
Shell
# Beautiful graph view — assign an alias
$ git log --decorate --oneline --graph

# Save as alias 'git lol'
$ git config --global alias.lol "log --decorate --oneline --graph"
$ git lol
$ git lol HEAD develop origin/main  # Compare multiple refs
$ git lol --all                     # All branches

# Colorized custom format
$ git log --graph --pretty=format:'%C(red)%h%Creset -%C(yellow)%d%Creset %s %C(green)(%cr) %C(yellow)<%an>%Creset'
2.4–2.8 — Log filtering and search
Shell
# Log between two branches
$ git log main..feature-branch    # Commits in feature not in main
$ git log --left-right main...feature-branch

# Show files changed in each commit
$ git log --stat
$ git log --name-only

# Search for when a string was introduced
$ git log -S "function myFunc"

# Log for a range of lines in a file
$ git log -L 10,25:src/main.js

# Filter by file extension (--)
$ git log -- '*.js'
Ch. 03
Working with Remotes
Managing remote repositories, pushing, pulling, fetching, and tracking remote branches.
3.1–3.5 — Remote management
Shell
$ git remote -v                          # List remotes with URLs
$ git remote add upstream https://...   # Add a new remote
$ git remote rename origin newname        # Rename a remote
$ git remote remove upstream              # Remove a remote
$ git remote show origin                  # Detailed info about a remote
$ git remote set-url origin git@example.com:user/repo.git

# Fetch all remotes
$ git fetch --all

# Pull with rebase (cleaner history)
$ git pull --rebase origin main

# Set upstream tracking for current branch
$ git push -u origin feature-branch
3.6–3.10 — Advanced remote operations
Shell
# Delete a remote branch
$ git push origin --delete old-feature

# List all remote branches
$ git branch -r

# Remove stale remote-tracking refs
$ git remote prune origin
$ git fetch --prune

# Force push (after rebase) — use with caution!
$ git push --force-with-lease origin main

# Update from upstream fork
$ git fetch upstream
$ git merge upstream/main
⚠️
Prefer --force-with-lease over --force. It will fail if someone else has pushed to the branch since your last fetch, preventing accidental overwrites.
Ch. 04
Staging
Staging all changes, partial staging, hunks, unstaging, and interactive add.
4.1–4.5 — Staging commands
Shell
$ git add .                  # Stage all changes
$ git add -A                  # Stage all including deletions
$ git add -p                  # Interactive: choose hunks to stage
$ git add -u                  # Stage only already-tracked files

# Stage a file that contains changes
$ git add src/app.js

# Unstage a file (keep changes in working tree)
$ git restore --staged src/app.js

# Show staged changes (what will be committed)
$ git diff --cached

# Stage deleted files
$ git rm old-file.txt
Ch. 05
Ignoring Files and Folders
Using .gitignore to exclude files, global ignores, and removing already-tracked files.
5.1–5.5 — .gitignore fundamentals
.gitignore
# Ignore a specific file
secrets.env

# Ignore all .log files
*.log

# Ignore the build/ directory
build/

# Ignore node_modules everywhere
**/node_modules/

# But don't ignore this specific file
!important.log

# Ignore files in subdirectory only
docs/*.pdf
Shell
# Global ignore file (all repos)
$ git config --global core.excludesfile ~/.gitignore_global

# Untrack a file already committed (keep local copy)
$ git rm --cached secrets.env

# Check if a file is being ignored
$ git check-ignore -v path/to/file

# Ignore local changes temporarily
$ git update-index --assume-unchanged config.local.js
ℹ️
Patterns in .gitignore only apply to untracked files. To stop tracking a previously committed file, you must run git rm --cached first.
Ch. 06
Git Diff
Showing differences in working branch, between commits, staged files, and across branches.
6.1–6.8 — Diff command reference
git diff
Working directory vs staging area (unstaged changes)
git diff --cached
Staging area vs last commit (staged changes)
git diff HEAD
All changes vs last commit (staged + unstaged)
git diff main..feature
Differences between two branches
git diff abc123 def456
Difference between two specific commits
git diff HEAD~1
Changes since the previous commit
git diff --stat
Summary of changed files and line counts
git diff --word-diff
Word-level diff (great for prose)
Shell
# Diff a specific file only
$ git diff -- src/index.js

# Difference between current version and last release
$ git diff v1.0..HEAD

# Use external diff tool (meld, vimdiff, etc)
$ git difftool -t meld
Ch. 07
Undoing
Returning to previous commits, undoing changes, using reflog, and reverting commits.
7.1–7.6 — Undo strategies
Shell
# Discard unstaged changes in a file
$ git restore src/main.js
$ git checkout -- src/main.js   # Older syntax

# Undo last commit, keep changes staged
$ git reset --soft HEAD~1

# Undo last commit, keep changes unstaged
$ git reset HEAD~1

# Undo last commit AND discard all changes (DESTRUCTIVE!)
$ git reset --hard HEAD~1

# Safely revert a public commit (creates new commit)
$ git revert abc1234

# Recover using reflog (Git's safety net)
$ git reflog                      # Shows all recent HEAD movements
$ git reset --hard HEAD@{2}       # Go back 2 reflog entries
🚫
git reset --hard permanently discards uncommitted changes. Use git revert for commits already pushed to a shared branch — it creates a new "undo" commit instead of rewriting history.
Ch. 08
Merging
Automatic merging, resolving conflicts, aborting merges, and merge strategies.
8.1–8.6 — Merge operations
Shell
# Standard merge (creates a merge commit)
$ git merge feature-branch

# Merge without a commit (review before committing)
$ git merge --no-commit feature-branch

# Squash all feature commits into one
$ git merge --squash feature-branch
$ git commit -m "Add feature X"

# Abort a merge in progress
$ git merge --abort

# Keep only our changes when conflicting
$ git checkout --ours conflicted-file.js
$ git checkout --theirs conflicted-file.js

# Find branches with no merged changes
$ git branch --no-merged main
Ch. 09
Submodules
Cloning repos with submodules, updating, adding, and removing submodules.
9.1–9.6 — Submodule workflows
Shell
# Clone a repo that has submodules
$ git clone --recurse-submodules https://example.com/user/repo.git

# If you forgot --recurse-submodules
$ git submodule update --init --recursive

# Add a new submodule
$ git submodule add https://example.com/lib/helper.git lib/helper

# Update all submodules to latest
$ git submodule update --remote

# Remove a submodule
$ git submodule deinit lib/helper
$ git rm lib/helper

# Set submodule to follow a branch
$ git config -f .gitmodules submodule.lib/helper.branch main
Ch. 10
Committing
Stage and commit changes, good commit messages, amending, and commit options.
10.1 — Commit flags reference
ParameterDetails
--message, -mCommit message inline. Skips editor.
--amendModify the most recent commit (message or files)
--no-editAmend without changing the commit message
--all, -aAutomatically stage all tracked modified files
--patch, -pInteractive: choose which hunks to commit
--fixupCreate a fixup commit for a specific commit hash
--squashCreate a squash commit for interactive rebase
--allow-emptyCommit with no changes (useful for CI triggers)
--no-verifySkip pre-commit and commit-msg hooks
Shell
# Stage and commit in one step (tracked files only)
$ git commit -am "Fix typo in README"

# Amend last commit message
$ git commit --amend -m "Better message"

# Add forgotten file to last commit
$ git add forgotten.js
$ git commit --amend --no-edit

# Commit with a specific date
$ git commit --date="Mon 20 Aug 2024 20:19:19" -m "Backdated commit"
Good commit messages
Follow the 7 rules: (1) Separate subject from body with a blank line. (2) Limit subject to 50 chars. (3) Capitalize the subject. (4) No period at end of subject. (5) Use imperative mood ("Fix bug" not "Fixed bug"). (6) Wrap body at 72 chars. (7) Explain what and why, not how.
Ch. 11
Aliases
Creating simple and advanced Git aliases to speed up your workflow.
11.1–11.3 — Useful aliases
Shell
# Common time-saving aliases
$ git config --global alias.st   status
$ git config --global alias.co   checkout
$ git config --global alias.br   branch
$ git config --global alias.ci   commit
$ git config --global alias.lol  "log --decorate --oneline --graph"
$ git config --global alias.unstage "restore --staged"
$ git config --global alias.last  "log -1 HEAD --stat"

# Shell command alias (prefix with !)
$ git config --global alias.trim "!git branch --merged | grep -v '\*' | xargs -n 1 git branch -d"

# List all aliases
$ git config --get-regexp alias
Ch. 12
Rebasing
Interactive rebase, squashing, reordering commits, and rebase onto.
Rebase fundamentals
Shell
# Rebase current branch on top of main
$ git rebase main

# Interactive rebase: edit last 3 commits
$ git rebase -i HEAD~3

# Interactive rebase commands (in editor):
# pick   = keep commit as-is
# reword = keep but edit message
# edit   = keep but stop to amend
# squash = merge into previous commit
# fixup  = squash, discard message
# drop   = remove commit entirely

# Continue/skip/abort a rebase
$ git rebase --continue
$ git rebase --skip
$ git rebase --abort

# Move branch to different base
$ git rebase --onto newbase oldbase branch
⚠️
Never rebase commits that have been pushed to a shared remote branch. Rebasing rewrites history — this causes problems for anyone else who has those commits.
Ch. 13
Configuration
Setting editor, line endings, SSH proxy, multiple configs, and more.
13.1–13.8 — Config options
Shell
# Set default editor
$ git config --global core.editor "code --wait"   # VS Code
$ git config --global core.editor "vim"

# Line ending handling
$ git config --global core.autocrlf true    # Windows
$ git config --global core.autocrlf input   # macOS/Linux

# Configure merge tool
$ git config --global merge.tool vimdiff

# Coloring output
$ git config --global color.ui auto

# Set up a proxy
$ git config --global http.proxy http://proxy:8080

# Configure for one command only
$ git config --global alias.ignore.one "!git config core.fileMode false"

# List + edit config
$ git config --list
$ git config --global -e    # Open global config in editor
Ch. 14
Branching
Creating, listing, switching, renaming, deleting, and pushing branches.
14.1–14.11 — Branch operations
Shell
$ git branch                       # List local branches
$ git branch -a                    # List all branches (local + remote)
$ git branch -v                    # Show last commit on each branch

# Create and switch
$ git branch feature/auth          # Create branch
$ git switch feature/auth          # Switch to it
$ git switch -c feature/auth       # Create AND switch (new syntax)
$ git checkout -b feature/auth    # Same (old syntax)

# Delete branches
$ git branch -d feature/auth      # Delete (safe — merged only)
$ git branch -D feature/auth      # Force delete

# Rename current branch
$ git branch -m new-name

# Search branches
$ git branch -a | grep "feature"

# Move HEAD to arbitrary commit
$ git branch -f main abc1234
Git Branch Simulator
Enter a branch name above and click SIMULATE
Ch. 15
Cherry Picking
Applying specific commits from one branch to another.
15.1–15.4 — Cherry-pick operations
Shell
# Apply a single commit to current branch
$ git cherry-pick abc1234

# Apply a range of commits
$ git cherry-pick abc1234..def5678

# Cherry-pick without auto-committing
$ git cherry-pick --no-commit abc1234

# Check if cherry-pick is needed
$ git cherry -v main          # + = not in main, - = is in main

# Find commits to apply upstream
$ git cherry -v upstream/main

# Continue after conflict resolution
$ git cherry-pick --continue
$ git cherry-pick --abort
Ch. 16
Recovering
Recovering from reset, recovering lost commits, restoring deleted branches, and git stash.
16.1–16.6 — Recovery strategies
Shell
# Recover from accidental git reset --hard
$ git reflog                        # Find the commit hash you lost
$ git reset --hard HEAD@{1}        # Go back

# Recover a deleted branch
$ git reflog | grep "deleted-branch"
$ git checkout -b deleted-branch abc1234

# Restore a deleted file after commit
$ git log --all -- deleted-file.js
$ git checkout abc1234 -- deleted-file.js

# Restore file to previous version
$ git restore --source HEAD~2 -- src/api.js

# Stash work in progress
$ git stash                         # Stash tracked changes
$ git stash -u                      # Include untracked files
$ git stash pop                     # Apply and remove stash
$ git stash list                    # Show all stashes
$ git stash apply stash@{2}        # Apply specific stash
Ch. 17
Squashing
Combining multiple commits into one for a cleaner history.
17.1–17.5 — Squash strategies
Shell
# Squash last N commits interactively
$ git rebase -i HEAD~4
# In editor: change 'pick' to 'squash' for commits to merge

# Squash recent commits without rebasing
$ git reset --soft HEAD~4           # Uncommit but keep staged
$ git commit -m "Feature: auth module"

# Squash during merge
$ git merge --squash feature-branch
$ git commit -m "Add feature X (squashed)"

# Autosquash fixup commits
$ git commit --fixup abc1234        # Create fixup commit
$ git rebase -i --autosquash HEAD~5 # Auto-reorder and squash
Ch. 18
Git Statistics
Commit counts, lines per developer, commits by date, and repository insights.
18.1–18.8 — Useful stats commands
Shell
# Total commits in repo
$ git rev-list --count HEAD

# Commits per author
$ git shortlog -s -n HEAD

# Lines of code per developer
$ git log --author="Alice" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2 } END { printf "added: %s, removed: %s\n", add, subs }'

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

# Last commit on each branch
$ git branch -v

# Find all local Git repos on machine
$ find ~ -name ".git" -type d 2>/dev/null | sed 's/\/.git//'

# Total commits per author (pretty format)
$ git log --format="%aN" | sort | uniq -c | sort -rn
Git Stats Explorer
Click RUN to see example statistics output…
Ch. 19
Worktrees
Parallel checkouts of the same repository — review a PR without disturbing your in-progress branch.
19.1–19.5 — Worktree fundamentals

Worktrees let you check out a different branch of the same repo into a separate directory. Indispensable when you have uncommitted work and need to quickly inspect another branch.

Shell
# Create a new worktree for an existing branch
$ git worktree add ../repo-review review/pr-128

# Create a worktree AND a new branch in one shot
$ git worktree add -b hotfix/auth ../repo-hotfix main

# List active worktrees
$ git worktree list

# Remove a worktree (after the work is merged or discarded)
$ git worktree remove ../repo-review

# Prune entries for directories that no longer exist
$ git worktree prune
💡
Worktrees share the same .git/objects store, so creating one is cheap — there's no second clone, just a second checkout. Perfect for long-running review sessions or running tests on two branches simultaneously.
19.6 — A practical operator workflow

You're deep in a feature branch, mid-refactor. Someone asks you to verify a bug fix on main right now. Without worktrees you'd stash, switch, build, switch back, unstash. With worktrees:

Shell
# From inside the repo, spin up a clean main checkout next to it
$ git worktree add ../current-main main

$ cd ../current-main
$ npm install && npm test

# Done — get rid of it, keep your feature branch untouched
$ cd -
$ git worktree remove ../current-main
Ch. 20
Hooks
Local automation that runs at well-defined points in the Git lifecycle — pre-commit, commit-msg, pre-push, post-receive.
20.1 — Where hooks live

Hooks are executable scripts in .git/hooks/. Each one is named after the event it fires on. Git ships sample files with a .sample suffix — rename to activate.

pre-commit
Runs before the commit is created — lint, format, fast tests
commit-msg
Validates / rewrites the commit message before finalising
pre-push
Last gate before refs leave the machine — full test suite, secret scans
post-commit
Fires after commit lands locally — notify, tag, archive
post-receive
Server-side, after refs are accepted — deploy, mirror, broadcast
pre-rebase
Block rebases against published branches
20.2 — A working pre-commit hook

Block commits that contain obvious secrets, run a lint pass, and prevent committing to main directly.

.git/hooks/pre-commit
#!/usr/bin/env bash
# Fail fast — any error aborts the commit
set -euo pipefail

branch="$(git symbolic-ref --short HEAD)"
if [[ "$branch" == "main" ]]; then
  echo "Refusing to commit directly to main. Open a branch." >&2
  exit 1
fi

# Block obvious secrets
if git diff --cached | grep -Ei '(AWS_SECRET|BEGIN RSA PRIVATE KEY|password\\s*=)'; then
  echo "Possible secret in staged diff — aborting." >&2
  exit 1
fi

# Run a fast lint pass on staged files
files=$(git diff --cached --name-only --diff-filter=ACM | grep '\\.\\(ts\\|tsx\\|js\\)$' || true)
if [[ -n "$files" ]]; then
  npx eslint --max-warnings=0 $files
fi
Shell
# Make it executable so Git will run it
$ chmod +x .git/hooks/pre-commit
20.3 — Sharing hooks across the team

Hooks under .git/hooks/ are not version-controlled. Pin a shared directory with core.hooksPath so the whole team gets the same gates:

Shell
# Repo-local: commit a hooks/ directory and point Git at it
$ git config core.hooksPath .githooks
$ git add .githooks/pre-commit
$ git commit -m "Add shared pre-commit hook"

# Skip hooks for a one-off commit (use sparingly)
$ git commit --no-verify -m "Bypass once"
⚠️
Hooks run with the developer's full shell privileges. Keep them small, vendored from your own repo, and code-reviewed like any other production script.
REF
Command Cheatsheet
The most frequently used Git commands, organised by category.
Setup & Init
git init
Initialize a new repo in current directory
git clone [url]
Clone a remote repository locally
git config --global
Set global configuration (name, email, editor)
Stage & Snapshot
git status
Show modified files in working directory
git add [file]
Stage a file as it looks now for your next commit
git add .
Stage all changes in current directory
git commit -m
Commit staged changes with a message
git commit --amend
Amend the last commit's message or content
Branch & Merge
git branch
List all local branches (* = current)
git switch -c [name]
Create and switch to new branch
git merge [branch]
Merge a branch into the current one
git rebase [branch]
Rebase current branch onto another
git cherry-pick
Apply a specific commit to current branch
Inspect & Compare
git log --oneline
Compact one-line-per-commit history
git diff
Show unstaged changes in working directory
git diff --cached
Show staged changes ready for commit
git show [sha]
Show metadata and content changes of a commit
git blame [file]
Show who changed what and when in a file
Share & Update
git fetch
Download all history from remote
git pull
Fetch and merge remote tracking branch
git push
Upload local branch to remote repository
git remote -v
List all configured remote connections
Rewrite History
git rebase -i
Interactive rebase — squash, reorder, edit
git reset --soft
Undo commit(s), keep changes staged
git reset --hard
Undo commit(s) AND discard changes (destructive!)
git revert [sha]
Create new commit that undoes specified commit
git reflog
Show log of all HEAD movements (recovery lifeline)
Operator Extras
git worktree add
Create a parallel checkout of another branch
git worktree list
List active worktrees attached to this repo
core.hooksPath
Point Git at a versioned hooks directory
git commit --no-verify
Skip pre-commit / commit-msg hooks for one commit