Commands, Branching, Rebasing, Conflict Resolution, Hooks, Workflows — version control mastery.
# ── Repository Setup ──
git init # initialize a new repo in current dir
git init my-project # initialize in a new directory
git clone https://github.com/user/repo.git # clone via HTTPS
git clone git@github.com:user/repo.git # clone via SSH
git clone --depth 1 https://github.com/user/repo.git # shallow clone (latest only)
git clone --branch v2.0 https://github.com/user/repo.git # clone specific branch
git clone --single-branch --branch main https://github.com/user/repo.git
# ── Staging & Committing ──
git add file.txt # stage a single file
git add src/ # stage all in directory
git add . # stage all changes (tracked + new)
git add -p # interactive staging (hunk by hunk)
git add -A # stage all changes everywhere
git add --patch # same as -p, stage interactively
git commit -m "feat: add login page" # commit with message
git commit -am "fix: typo in header" # stage tracked + commit
git commit --amend -m "better message" # amend last commit message
git commit --allow-empty -m "trigger CI" # empty commit (useful for CI)
# ── Status & Inspection ──
git status # show working tree status
git status --short # compact output (M, A, D, ??)
git status --porcelain # machine-readable output
git status -uall # show all untracked files
git log # show commit history
git log --oneline # one-line per commit
git log --oneline --graph --all # ASCII graph of all branches
git log --decorate --oneline # show branch/tag names
git log -5 # last 5 commits
git log --since="2 weeks ago" # commits from last 2 weeks
git log --author="alice" # commits by author
git log --grep="fix" # search commit messages
git log -p # show patch for each commit
git log --stat # show changed files with stats
git log --format="%h %s (%cr)" # custom format (hash, subject, relative date)
git log --follow -- file.txt # show renames too
git diff # unstaged changes
git diff --staged # staged changes
git diff HEAD # all changes vs last commit
git diff main..feature # diff between branches
git diff --color-words # word-level diff highlighting
git diff --stat # summary of changed files# ── Viewing Commits & Objects ──
git show HEAD # show latest commit details + diff
git show HEAD~3 # 3 commits before HEAD
git show abc1234 # show specific commit
git show abc1234:path/to/file # show file at that commit
git show --stat abc1234 # commit summary only
# ── Remote Management ──
git remote # list remotes
git remote -v # list with URLs
git remote add origin https://github.com/user/repo.git
git remote remove origin
git remote rename old-name new-name
git remote set-url origin git@github.com:user/repo.git
git remote show origin # detailed remote info
git fetch origin # fetch from remote
git fetch --all # fetch from all remotes
git fetch --prune # fetch + delete stale remote branches
git pull origin main # fetch + merge
git pull --rebase origin main # fetch + rebase (cleaner history)
git pull --ff-only origin main # only pull if fast-forward possible
git push origin main # push to remote
git push origin feature # push local branch to remote
git push -u origin feature # push + set upstream tracking
git push --force-with-lease # safer force push (checks remote)
git push --force # force push (OVERWRITE remote history)
git push --delete origin old-branch # delete remote branch
git push origin --tags # push all tags
git push origin v1.0.0 # push single tag
# ── Tags ──
git tag # list tags
git tag -l "v2.*" # list tags matching pattern
git tag v1.0.0 # lightweight tag
git tag -a v1.0.0 -m "Release 1.0.0" # annotated tag
git tag -a v1.0.0 abc1234 -m "Tag specific commit"
git tag -d v1.0.0 # delete local tag
git push origin --delete v1.0.0 # delete remote tag# ── System, Global, Local ──
# System: /etc/gitconfig
git config --system core.editor vim
# Global: ~/.gitconfig
git config --global user.name "Alice Chen"
git config --global user.email "alice@example.com"
git config --global core.editor "code --wait"
git config --global init.defaultBranch main
git config --global pull.rebase true
git config --global push.autoSetupRemote true
git config --global rerere.enabled true
# Local: .git/config (repo-specific)
git config user.name "Work Alias"
git config commit.template .gitmessage
# ── Useful Settings ──
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 --stat"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.unstage "reset HEAD --"
git config --global alias.visual "!gitk --all"
git config --global diff.external difftool# ── Common .gitignore ──
# Dependencies
node_modules/
vendor/
__pycache__/
*.pyc
# Build output
dist/
build/
*.o
*.so
*.dll
# Environment & secrets
.env
.env.local
.env.*.local
*.pem
credentials.json
# IDE & OS files
.vscode/
.idea/
*.swp
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
# Coverage
coverage/
.nyc_output/
# Patterns
*.tmp
*.bak
cache/
temp/
# Negation (track this despite rules)
!important.log
!config/default.env# ── Git Help ──
git help # show general help
git help commit # help for specific command
git help --web log # open in browser
git log --help # help for log (alternative)
# ── Useful Shortcuts ──
git stash # stash changes (see stashing section)
git clean -fd # remove untracked files & directories
git clean -fdx # also remove ignored files
git archive HEAD --format=zip -o latest.zip # export repo as zip
git rev-parse HEAD # get full SHA of HEAD
git rev-parse --short HEAD # get short SHA
git rev-parse --abbrev-ref HEAD # get current branch name
git count-objects -vH # repository size info| Object | Description |
|---|---|
| blob | File content (stored by SHA-1 hash) |
| tree | Directory listing (blob + tree refs) |
| commit | Snapshot: tree, parent(s), author, message |
| tag | Annotated tag object (tagger, message) |
| State | Command to Reach It |
|---|---|
| Untracked | New file, not in index or repo |
| Staged (index) | git add |
| Committed | git commit |
| Modified (unstaged) | Edit after commit/add |
| Stashed | git stash |
--global for personal repos, and repo-local config for work projects. Set init.defaultBranch to main to avoid confusion with master.# ── Creating & Listing Branches ──
git branch # list local branches
git branch -a # list all (local + remote)
git branch -r # list remote branches only
git branch -v # list with last commit on each
git branch -vv # list with tracking info
git branch --sort=-committerdate # list sorted by date (newest first)
git branch --merged # branches merged into current
git branch --no-merged # branches not yet merged
git branch feature/login # create new branch (stay on current)
git branch feature/login abc1234 # create branch at specific commit
# ── Switching Branches ──
git checkout feature/login # switch to branch
git checkout -b feature/signup # create + switch in one step
git switch feature/login # modern way to switch (Git 2.23+)
git switch -c feature/signup # create + switch (modern)
git switch - # switch to previous branch
git switch main # switch to main
# ── Deleting Branches ──
git branch -d feature/old # delete (only if fully merged)
git branch -D feature/old # force delete (even if unmerged)
git push origin --delete feature/old # delete remote branch
git remote prune origin # clean up stale remote-tracking refs# ── Renaming Branches ──
git branch -m old-name new-name # rename current branch
git branch -m feature/old feature/new # rename any branch
# After renaming, update remote:
git push origin -u feature/new
git push origin --delete feature/old
# ── Tracking Branches ──
# Set up tracking (link local to remote)
git branch --set-upstream-to=origin/feature/login
git branch -u origin/main # short form
git branch --unset-upstream main # remove tracking
# Checkout remote branch (creates local tracking branch)
git checkout -b feature/login origin/feature/login
git switch -c feature/login origin/feature/login
# ── Worktree: Work on multiple branches simultaneously ──
git worktree add ../hotfix-dir hotfix/urgent # checkout branch in new dir
git worktree add ../feature-dir -b new-feature # create + checkout in dir
git worktree list # list all worktrees
git worktree remove ../hotfix-dir # remove worktree
git worktree prune # clean up stale refs# ── Branch Comparison ──
git log main..feature # commits in feature not in main
git log feature..main # commits in main not in feature
git log main...feature # commits in either, but not both (symmetric)
git log --left-right main...feature # show which side each commit is on
git diff main...feature # diff of changed files (merge base)
git diff main feature # direct diff between branch tips
git merge-base main feature # find common ancestor commit
git merge-base --is-ancestor main feature # check if main is ancestor
git cherry -v main feature # commits on feature not yet in main
git range-diff main...feature # compare rebased branch changes
# ── Branch Topology Visualization ──
git log --oneline --graph --all
git log --oneline --graph --decorate --all
git log --all --graph --decorate --oneline --simplify-by-decoration
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
# ── Moving / Copying Commits ──
git cherry-pick abc1234 # apply specific commit to current branch
git cherry-pick abc1234 def5678 # apply multiple commits
git cherry-pick feature~2..feature # apply range of commits
git cherry-pick --no-commit abc1234 # apply changes without committing
git cherry-pick --abort # abort in-progress cherry-pick
git cherry-pick --continue # continue after resolving conflicts| Command | Description |
|---|---|
| git branch name | Create branch at current HEAD |
| git branch -d name | Delete merged branch |
| git branch -D name | Force delete branch |
| git checkout -b name | Create and switch |
| git switch -c name | Create and switch (modern) |
| git switch - | Switch to previous branch |
| git branch -m old new | Rename a branch |
| git branch -a | List all branches |
| git branch --merged | Show merged branches |
| git branch --no-merged | Show unmerged branches |
| Feature | checkout | switch (Git 2.23+) |
|---|---|---|
| Switch branch | Yes | Yes |
| Create + switch | checkout -b | switch -c |
| Restore files | checkout -- file | restore file |
| Detached HEAD | checkout commit | switch --detach |
| Orphan branch | checkout --orphan | switch --orphan |
| Recommendation | Legacy | Preferred ✅ |
git switch over git checkout for branch switching. The switch command was introduced in Git 2.23 to separate branch operations from file restoration, reducing the risk of accidental file overwrites.git worktree when you need to work on multiple branches at once without stashing. Each worktree is a separate checkout directory linked to the same repository, allowing you to quickly context-switch between tasks.# ── Basic Merge ──
git checkout main
git merge feature/login # merge feature into main
git merge --no-ff feature/login # merge with merge commit (even if fast-forward)
git merge --ff-only feature/login # only merge if fast-forward
git merge --squash feature/login # squash all into one commit (don't commit)
git merge --abort # abort a conflicted merge
# ── Fast-Forward vs No-Fast-Forward ──
# Fast-forward (default): moves branch pointer, no merge commit
# A---B---C (main)
# \
# D---E (feature) → A---B---C---D---E (main)
# No-ff (--no-ff): always creates merge commit, preserves history
# A---B---C-----------F (main, merge commit)
# \ /
# D---E (feature)
# ── Rebase ──
git checkout feature/login
git rebase main # rebase feature onto main
git rebase --continue # continue after resolving conflicts
git rebase --abort # abort rebase
git rebase --skip # skip a conflicting commit
git rebase --onto main feature~3 feature # rebase subset of commits
git rebase -i HEAD~5 # interactive rebase last 5 commits# ── Interactive Rebase Commands ──
# git rebase -i HEAD~5 opens editor with:
# pick abc1234 feat: add user model
# squash def5678 fix: typo in model
# reword ghi9012 refactor: clean up validators
# edit jkl3456 test: add unit tests
# drop mno7890 wip: experimental code
# Commands:
# pick → keep commit as-is
# squash → combine with previous commit (keep both messages)
# fixup → combine with previous commit (discard this message)
# reword → keep commit but edit the message
# edit → pause to amend or split the commit
# drop → remove the commit entirely
# ── Common Interactive Rebase Scenarios ──
# 1. Squash last 3 commits into one:
git rebase -i HEAD~3
# Change pick to squash for commits 2 and 3
# 2. Reorder commits (swap lines in editor):
# pick abc1234 commit A
# pick def5678 commit B ← swap these
# pick ghi9012 commit C
# Result: A, C, B (instead of A, B, C)
# 3. Edit a commit deep in history:
git rebase -i HEAD~10
# Change pick to edit on target commit
# Make changes, then:
git add .
git rebase --continue# ── Merge Conflict Resolution ──
# When conflicts occur, Git marks files:
# <<<<<<< HEAD
# your changes here
# =======
# their changes here
# >>>>>>> feature/login
# Manual resolution options:
git status # see conflicted files
git diff --name-only --diff-filter=U # list only conflicted files
# Option 1: Edit manually, then:
git add resolved-file.txt
git commit # complete the merge
# Option 2: Accept one side entirely
git checkout --ours file.txt # use our version (current branch)
git checkout --theirs file.txt # use their version (incoming branch)
# Option 3: Use merge tool
git mergetool # opens configured merge tool (vimdiff, meld, etc.)
git config --global merge.tool vscode # set default merge tool
# ── Cherry-pick ──
git cherry-pick abc1234 # apply specific commit
git cherry-pick -x abc1234 # add original commit hash to message
git cherry-pick --no-commit abc1234 # apply without committing| Aspect | Merge | Rebase |
|---|---|---|
| History | Preserves full history | Linear, rewritten history |
| Merge commits | Creates merge commit | No merge commits |
| Branch topology | Shows branch structure | Linear, single line |
| Safety | Safe (non-destructive) | Rewrites history (dangerous) |
| Conflict handling | Resolve once | May resolve multiple times |
| Remote branches | Safe for shared | Never rebase shared branches |
| Best for | Feature merges to main | Cleaning up local feature branch |
| Cleanup | Can be noisy | Clean, readable log |
| Rule | Reason |
|---|---|
| Never rebase public/shared branches | Rewrites others' history |
| Only rebase local feature branches | Only your own commits affected |
| Force push after rebase | Remote has old commits |
| Use --force-with-lease | Safer than --force |
| Pull with --rebase | Avoids merge commits on main |
| Communicate with team | Others may have based work on old commits |
# ── Advanced Merge Techniques ──
# Merge a specific file from another branch
git checkout feature -- path/to/file.txt
# Undo a merge (keep changes staged)
git reset --soft HEAD~1
# Revert a merge commit (safely undo on shared branch)
git revert -m 1 <merge-commit-sha>
# -m 1: keep main's side (use parent 1)
# -m 2: keep feature's side (use parent 2)
# ── Rebase Pitfalls & Recovery ──
# Problem: Rebasing changes commit SHAs
# If someone based work on your original commits, they'll have conflicts
# Solution: Coordinate with team, or use merge instead
# Recovery after bad rebase:
git reflog # find original HEAD before rebase
git reset --hard abc1234 # restore to pre-rebase state
# ── Subtree Merge (vendor dependencies) ──
git remote add vendor https://github.com/vendor/lib.git
git fetch vendor
git merge -s ours --no-commit --allow-unrelated-histories vendor/main
git read-tree --prefix=vendor-lib/ -u vendor/main
git commit -m "Merge vendor library as subtree"git push --force-with-lease instead of --force. The --force-with-leaseflag ensures you don't overwrite commits that others may have pushed while you were rebasing.# ── git reset: Move HEAD and branch pointer ──
# --soft: move HEAD, keep changes staged
# --mixed: move HEAD, unstage changes (DEFAULT)
# --hard: move HEAD, discard ALL changes
# Example: Undo last commit, keep changes staged
git reset --soft HEAD~1
# State: commit is undone, files are still in staging area
# Example: Undo last commit, unstage changes
git reset --mixed HEAD~1
# State: commit is undone, changes are in working directory (unstaged)
# Example: Undo last commit, DISCARD all changes
git reset --hard HEAD~1
# State: commit and all changes are GONE (use with caution)
# Undo last 3 commits (soft)
git reset --soft HEAD~3
# Reset to a specific commit
git reset --hard abc1234
# ── git revert: Create a NEW commit that undoes changes ──
# Safe for shared branches (doesn't rewrite history)
git revert HEAD # revert last commit
git revert abc1234 # revert specific commit
git revert HEAD~3..HEAD # revert last 3 commits (3 new commits)
git revert --no-commit HEAD~3..HEAD # revert but don't auto-commit
git revert -m 1 <merge-sha> # revert a merge commit (keep main side)# ── Restoring & Discarding File Changes ──
# Discard working directory changes (unstaged)
git checkout -- file.txt # restore file to last committed state
git restore file.txt # modern equivalent (Git 2.23+)
git restore --source=HEAD~2 file.txt # restore from specific commit
git restore --staged file.txt # unstage a file (keep changes)
git restore --worktree file.txt # discard working changes (keep staged)
# ── Unstaging ──
git reset HEAD file.txt # unstage file (keep modifications)
git reset HEAD -- . # unstage everything
git restore --staged file.txt # modern way to unstage
git restore --staged . # unstage all files
# ── Amending Commits ──
git commit --amend -m "new message" # change last commit message
git commit --amend --no-edit # add staged changes to last commit
git commit --amend --author="New Name <new@email.com>" # change author
# Add forgotten files to last commit:
git add forgotten-file.txt
git commit --amend --no-edit
# ── git clean: Remove untracked files ──
git clean -n # dry run (show what would be deleted)
git clean -f # delete untracked files
git clean -fd # delete untracked files + directories
git clean -fdx # also delete ignored files
git clean -fX # delete only ignored files
git clean -e "*.log" -f # exclude pattern from deletion# ── git reflog: The Safety Net ──
# Records EVERY HEAD movement (resets, checkouts, merges, rebases)
git reflog # show reference log
git reflog --date=relative # with relative dates
git reflog --date=iso # with ISO dates
git reflog show HEAD # only HEAD changes
git reflog show feature/login # only specific branch
# ── Recovering Lost Commits ──
# Step 1: Find the lost commit in reflog
git reflog
# Output:
# abc1234 HEAD@{0} reset: moving to HEAD~1
# def5678 HEAD@{1} commit: fix: important bug fix
# ghi9012 HEAD@{2} commit: feat: new feature
# Step 2: Restore to the lost state
git reset --hard def5678 # restore to lost commit
# Or create a new branch from it:
git branch recovered-fix def5678
# ── Recovering a dropped stash
git reflog stash # show stash history
git stash apply stash@{2} # recover specific stash
# ── Recovering after git reset --hard
# You accidentally ran: git reset --hard HEAD~5
git reflog # find pre-reset commit
git reset --hard HEAD@{1} # restore to before the reset| Command | Affects History? | Safe for Shared? | Use Case |
|---|---|---|---|
| reset --soft | Yes | No | Undo commit, keep staged |
| reset --mixed | Yes | No | Undo commit + unstage |
| reset --hard | Yes | No | Discard everything |
| revert | No (new commit) | Yes ✅ | Safely undo on shared |
| restore | No | N/A | Discard file changes |
| checkout -- | No | N/A | Legacy restore files |
| amend | Yes | No | Fix last commit |
git reset --hard, you can recover lost commits using git reflog. Use git config --global gc.reflogExpire 90 to adjust the retention period.git reset --hard on shared branches. It rewrites history and will cause problems for all collaborators. Use git revert instead to safely undo commits without rewriting history.# ── Basic Stash Operations ──
git stash # stash current changes (tracked files)
git stash push # same as git stash (explicit form)
git stash push -m "WIP: login form" # stash with a descriptive message
git stash push -m "wip auth" -- src/auth/ # stash only specific paths
# ── Applying Stashes ──
git stash pop # apply latest stash AND remove it
git stash apply # apply latest stash but KEEP it
git stash apply stash@{2} # apply specific stash
git stash pop stash@{1} # pop specific stash
# ── Viewing Stashes ──
git stash list # list all stashes
git stash show # show summary of latest stash
git stash show stash@{1} # show summary of specific stash
git stash show -p # show full diff of latest stash
git stash show -p stash@{2} # show full diff of specific stash
# ── Managing Stashes ──
git stash drop stash@{0} # remove specific stash
git stash drop # remove latest stash
git stash clear # remove ALL stashes
git stash branch feature/login stash@{0} # create branch from stash# ── Advanced Stash Options ──
# Keep staged files staged, stash only unstaged changes
git stash push --keep-index
# Useful when you want to commit staged changes but save WIP
# Include untracked files in stash
git stash push -u # --include-untracked
git stash push --all # include untracked + ignored files
# Stash only specific files
git stash push -- src/components/Login.tsx
git stash push -- src/ utils/ -m "WIP: refactored utils"
# ── Real-World Scenarios ──
# Scenario 1: Emergency hotfix while working on feature
git stash push -m "WIP: user profile page"
git checkout main
git pull origin main
git checkout -b hotfix/critical-bug
# ... fix bug, commit, push, merge ...
git checkout feature/user-profile
git stash pop
# Continue where you left off
# Scenario 2: Test your WIP changes without committing
git stash push -m "WIP: refactoring"
# ... test on clean tree ...
git stash pop
# Scenario 3: Move changes between branches
git stash push -m "partial work"
git checkout other-branch
git stash pop
# Changes now applied on other-branch
# Scenario 4: Stash with keep-index for partial commit
git add server.js # stage only server changes
git stash push --keep-index -m "stashed client WIP"
git commit -m "feat: update server config" # commit staged only
git stash pop # restore client WIP| Command | Description |
|---|---|
| git stash | Stash tracked changes |
| git stash push -m "msg" | Stash with message |
| git stash push -u | Include untracked files |
| git stash push --keep-index | Keep staged, stash unstaged |
| git stash list | Show all stashes |
| git stash show -p | Show stash diff |
| git stash pop | Apply + remove stash |
| git stash apply | Apply + keep stash |
| git stash drop | Remove latest stash |
| git stash clear | Remove all stashes |
| git stash branch name | Create branch from stash |
# Stash entries are regular commits:
git log --oneline stash@{0}
# Shows the stash commit tree
# Each stash has 2-3 commits:
# 1. Working directory state
# 2. Index state (if --keep-index)
# 3. Untracked files (if -u)git stash push -m "descriptive message". After a few days you won't remember what stash@3 contains. Good messages save time and prevent accidental drops.git stash pop can cause conflicts. If your branch has changed since stashing, the pop may fail. Use git stash apply first to test, resolve conflicts, then git stash drop when satisfied.# ── Hook Locations ──
# Local: .git/hooks/
# Global: ~/.gitconfig [core] hooksPath = ~/.githooks/
# Init template: /usr/share/git-core/templates/hooks/
# ── Available Client-Side Hooks ──
# pre-commit → before commit (linting, formatting)
# prepare-commit-msg → before commit message editor
# commit-msg → validate commit message
# post-commit → after commit (notifications)
# pre-rebase → before rebase
# post-checkout → after checkout
# post-merge → after merge
# pre-push → before push (run tests)
# pre-receive → server-side, before accepting push
# post-receive → server-side, after accepting push
# ── Example: pre-commit hook (lint + format) ──
# .git/hooks/pre-commit
#!/bin/sh
# Lint staged JavaScript/TypeScript files
npx lint-staged
# Run type checking
npx tsc --noEmit
echo "✅ Pre-commit checks passed"#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# ── pre-commit hook with Husky ──
echo "🔍 Running pre-commit checks..."
# Run linter on staged files
npx lint-staged
# Run tests
npm run test:unit -- --passWithNoTests
# Prevent commits to main branch directly
branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$branch" = "main" ]; then
echo "❌ Direct commits to main are not allowed. Use a feature branch."
exit 1
fi
echo "✅ All pre-commit checks passed!"#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# ── commit-msg hook: validate commit message format ──
# Enforce Conventional Commits: type(scope): description
commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .{1,100}"
if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo "❌ Invalid commit message format."
echo ""
echo " Expected: type(scope): description"
echo " Example: feat(auth): add OAuth2 login flow"
echo ""
echo " Types: feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert"
echo " Scope: optional, e.g. (auth), (api), (ui)"
echo " Max length: 100 characters"
echo ""
echo " Your message: $commit_msg"
exit 1
fi
# Check for ticket number (optional, team-specific)
# if ! echo "$commit_msg" | grep -qE "#[0-9]+"; then
# echo "⚠️ Warning: No ticket number found (e.g. #123)"
# fi
echo "✅ Commit message is valid"#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# ── pre-push hook: run full test suite before push ──
echo "🚀 Running pre-push checks..."
# Run full test suite
npm run test
if [ $? -ne 0 ]; then
echo "❌ Tests failed. Push aborted."
exit 1
fi
# Check for large files (> 5MB)
echo "📁 Checking for large files..."
max_size=5242880 # 5MB in bytes
large_files=$(find . -type f -not -path './.git/*' -size +${max_size}c)
if [ -n "$large_files" ]; then
echo "❌ Found files larger than 5MB:"
echo "$large_files"
echo "Consider using Git LFS for large files."
exit 1
fi
# Prevent pushing secrets
echo "🔐 Checking for potential secrets..."
if git diff --cached --name-only | xargs grep -lE '(password|secret|api_key|token).*=.*['\'"]' 2>/dev/null; then
echo "❌ Potential secrets detected. Push aborted."
exit 1
fi
echo "✅ Pre-push checks passed!"# ── Install and configure Husky ──
npm install -D husky lint-staged
# Initialize Husky
npx husky init
# This creates .husky/ directory and sets
# core.hooksPath in .git/config
# Configure lint-staged in package.json
# {
# "lint-staged": {
# "*.{ts,tsx}": [
# "eslint --fix",
# "prettier --write"
# ],
# "*.{json,md,css}": [
# "prettier --write"
# ],
# "*.{py}": [
# "black",
# "ruff check --fix"
# ]
# }
# }
# Add hooks
npx husky add .husky/pre-commit "npx lint-staged"
npx husky add .husky/commit-msg 'npx commitlint --edit "$1"'
npx husky add .husky/pre-push "npm run test"# ── Skip hooks (use with caution!) ──
# Bypass ALL hooks for a single command
git commit --no-verify -m "hotfix"
git commit -n -m "hotfix" # -n is short for --no-verify
git push --no-verify
git push -n
# ── Bypass specific hooks ──
# There's no built-in way to skip a specific hook.
# Workarounds:
# 1. Use environment variables in your hook script:
# if [ "$SKIP_LINT" = "1" ]; then exit 0; fi
# SKIP_LINT=1 git commit -m "message"
# 2. Temporarily rename the hook:
# mv .husky/pre-commit .husky/pre-commit.bak
# git commit -m "message"
# mv .husky/pre-commit.bak .husky/pre-commit| Hook | When | Common Use |
|---|---|---|
| pre-receive | Before accepting any ref | Enforce policies, block bad commits |
| update | Once per ref being updated | Per-branch validation |
| post-receive | After refs updated | Deploy, notify, trigger CI |
| pre-push | Client, before push | Run tests, check commit format |
pre-receive, update) cannot be bypassed with --no-verify. Use them to enforce mandatory policies like branch naming conventions, commit message format, and preventing direct pushes to protected branches.# ── Git Flow ──
# Branches: main, develop, feature/*, release/*, hotfix/*
git checkout -b develop main
git checkout -b feature/user-auth develop
# ... develop feature, commit regularly ...
# When feature is done:
git checkout develop
git merge --no-ff feature/user-auth # merge into develop with merge commit
git branch -d feature/user-auth
# Release branch (stabilize before production):
git checkout -b release/1.2.0 develop
# ... fix bugs, bump version, update changelog ...
git checkout main
git merge --no-ff release/1.2.0 # merge to main (tag this!)
git tag -a v1.2.0 -m "Release 1.2.0"
git checkout develop
git merge --no-ff release/1.2.0 # merge back to develop
git branch -d release/1.2.0
# Hotfix (urgent fix on production):
git checkout -b hotfix/security-patch main
# ... fix the bug ...
git checkout main
git merge --no-ff hotfix/security-patch
git tag -a v1.2.1 -m "Hotfix 1.2.1"
git checkout develop
git merge --no-ff hotfix/security-patch
git branch -d hotfix/security-patch# ── GitHub Flow (Simpler Alternative) ──
# Only: main + feature branches + pull requests
git checkout main
git pull origin main
git checkout -b feature/add-search
# ... develop feature, push regularly ...
git push -u origin feature/add-search
# Create Pull Request on GitHub
# Review, discuss, run CI
# After approval:
git checkout main
git pull origin main
git merge --no-ff feature/add-search # or use GitHub merge button
git push origin main
git branch -d feature/add-search
git push origin --delete feature/add-search
# ── Trunk-Based Development ──
# All developers commit to main (or short-lived feature branches < 1 day)
# Feature flags control unreleased features
git checkout main
git pull origin main
# Small, incremental commits
git checkout -b feature/checkout-flow
# ... work for a few hours, keep commits small ...
git checkout main
git merge feature/checkout-flow
git push origin main
# Feature flag hides the feature until ready
# Short-lived branches (< 24 hours) or branch by abstraction# ── Fork & Pull Request Workflow (Open Source) ──
# 1. Fork the repository on GitHub
# 2. Clone your fork locally
git clone https://github.com/YOUR_USERNAME/repo.git
cd repo
git remote add upstream https://github.com/ORIGINAL_OWNER/repo.git
# 3. Create a feature branch
git checkout -b fix/issue-123
# ... make changes, commit ...
git push -u origin fix/issue-123
# 4. Create Pull Request on GitHub
# 5. Keep your fork up-to-date
git fetch upstream
git checkout main
git merge upstream/main
git push origin main
# 6. After PR is merged, clean up
git branch -d fix/issue-123
git push origin --delete fix/issue-123
# ── Conventional Commits ──
# Format: type(scope): description
# Types:
# feat: new feature
# fix: bug fix
# docs: documentation only
# style: formatting, semicolons, no code change
# refactor: code change that neither fixes nor adds
# perf: performance improvement
# test: adding or updating tests
# build: build system or dependencies
# ci: CI/CD configuration
# chore: maintenance, tooling, configs
# revert: revert a previous commit
# Examples:
git commit -m "feat(auth): add OAuth2 login with Google"
git commit -m "fix(api): handle null response from user endpoint"
git commit -m "docs(readme): update installation instructions"
git commit -m "perf(db): add index to users.email column"
git commit -m "refactor(utils): extract validation logic to helper"
git commit -m "chore(deps): upgrade Next.js to 15.0.0"
git commit -m "feat!: breaking change to API response format" # ! = breaking# ── Semantic Versioning (SemVer) ──
# MAJOR.MINOR.PATCH (e.g., 2.1.0)
# MAJOR: breaking changes
# MINOR: new features (backward compatible)
# PATCH: bug fixes (backward compatible)
# Pre-release: 2.0.0-alpha.1, 2.0.0-beta.3, 2.0.0-rc.1
# ── Git Bisect: Find the commit that introduced a bug ──
git bisect start # start bisect session
git bisect bad # current commit is bad
git bisect good v1.5.0 # v1.5.0 was good
# Git checks out middle commit — test it
git bisect good # or: git bisect bad
# Repeat until the bad commit is found
git bisect reset # end bisect session
# Automated bisect with a test script:
git bisect start HEAD v1.0.0
git bisect run npm run test:regression
# Git automatically marks each commit as good/bad
# ── Git Blame: Who wrote each line ──
git blame file.txt # blame entire file
git blame -L 10,30 file.txt # blame lines 10-30
git blame -M file.txt # detect moved lines within file
git blame -C -C file.txt # detect copied lines from other files
git blame -e file.txt # show email instead of name
git blame --since="2025-01-01" file.txt # only recent changes
git log --format="%h %an %s" -- file.txt # simpler alternative
# ── Git Log Advanced ──
git log --grep="JIRA-123" --oneline # search commit messages
git log --all --author="alice" --oneline
git shortlog -sn # commit count by author
git shortlog -sn --since="6 months ago"
git shortlog -sn --all # include all branches
git log --format="%h | %an | %s | %ar" --date=short
git log --diff-filter=A -- "*.ts" # only added files
git log --diff-filter=D -- "*.ts" # only deleted files
git log --follow -- README.md # track renames| Aspect | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| Complexity | High | Low | Low |
| Branches | 5 types | main + feature | main + ephemeral |
| Release process | release branches | continuous | continuous |
| Hotfix process | dedicated branch | feature branch | direct to main |
| Best for | Release-based software | SaaS / web apps | High-velocity teams |
| CI requirement | Moderate | Important | Essential |
| Feature flags | Optional | Optional | Required |
# ── Semantic Release Automation ──
npm install -D semantic-release
# Analyzes commit messages to determine version bump
# feat: → minor bump
# fix: → patch bump
# feat!: → major bump (breaking)
# ── Manual Version Bump ──
# Using npm:
npm version patch # 1.0.0 → 1.0.1
npm version minor # 1.0.0 → 1.1.0
npm version major # 1.0.0 → 2.0.0
# Git tags are created automatically:
npm version patch -m "v%s" # custom message
# ── List releases / tags ──
git tag -l --sort=-version:refname
git tag -l "v*" --sort=-v:refnamegit bisect is a powerful debugging tool that uses binary search to find the exact commit that introduced a bug. Combined with a test script (git bisect run npm test), it can automatically pinpoint the problematic commit in seconds.# ── Detached HEAD State ──
# Happens when you checkout a specific commit instead of a branch
# HEAD (no branch)
# You're in 'detached HEAD' state.
# Fix 1: Create a branch from detached HEAD
git checkout -b feature/saved-work
# Fix 2: Attach to existing branch
git branch -f main HEAD
git checkout main
# Fix 3: Go back to your branch
git checkout main # or git switch main
# ── Merge Conflicts ──
# Check which files have conflicts:
git status
git diff --name-only --diff-filter=U
# Visualize conflict markers:
git diff
# Open in merge tool:
git mergetool
# After resolving each file:
git add resolved-file.txt
git commit # merge commit is auto-created
# Abort the merge entirely:
git merge --abort
# ── Accidentally Committed Secrets ──
# Step 1: Remove the secret from the file
# Step 2: Remove from Git history (use git-filter-repo)
pip install git-filter-repo
git filter-repo --invert-paths --path config/secrets.json
# Or for a single file removal from all commits:
git filter-repo --path secret.key --invert-paths
# Step 3: Rotate the compromised credentials!
# Step 4: Force push (warn collaborators)
git push --force --all
git push --force --tags
# Alternative for single commit:
git filter-branch --tree-filter 'rm -f config/secrets.json' HEAD
# (git-filter-repo is preferred over filter-branch)# ── Large Files (Git LFS) ──
# Install Git LFS
git lfs install
# Track specific file types
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "assets/videos/*"
git lfs track "dist/*.tar.gz"
# This creates .gitattributes
git add .gitattributes
git commit -m "chore: configure Git LFS tracking"
# Normal git workflow — LFS handles large files automatically
git add large-file.zip
git commit -m "add design assets"
git push origin main
# Migrate existing large files to LFS
git lfs migrate import --include="*.psd,*.zip" --everything
# Check what LFS is tracking
git lfs ls-files
git lfs track # list tracked patterns
# ── Corrupted Repository ──
# Check repository integrity
git fsck # check for corruption
git fsck --full # thorough check
git fsck --unreachable # find dangling objects
git fsck --dangling # find dangling commits/blobs
# Repair common issues
git gc # garbage collection
git gc --prune=now # aggressive GC (no grace period)
git gc --aggressive # thorough re-pack
# Recover dangling commits
git fsck --dangling --no-reflogs
git fsck --lost-found # save dangling objects
# Recovered objects are in .git/lost-found/# ── Force Push Recovery ──
# Someone force pushed and you lost your commits:
git fetch origin
git reflog # find your lost commits
git reset --hard HEAD@{n} # restore to your version
# Or create a safety branch first:
git branch my-work HEAD@{5}
# ── Prune & Clean ──
# Remove stale remote-tracking branches
git fetch --prune
git remote prune origin
git remote prune --dry-run origin # preview
# Remove dangling objects
git prune # remove unreachable loose objects
git prune --dry-run # preview
# Full cleanup
git gc --prune=now
git reflog expire --expire=now --all
git gc --aggressive --prune=now
# ── Repository Size Management ──
# Check what's taking space
git count-objects -vH
# Example output:
# count: 15432
# size: 42.67 MiB
# in-pack: 28765
# pack-size: 125.43 MiB
# prune-packable: 0
# garbage: 0
# size-garbage: 0 bytes
# Find largest files in history
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sed -n 's/^blob //p' | \
sort --numeric-sort --key=2 | \
tail -20
# Reduce repo size
git gc --aggressive
git repack -a -d -f --depth=250 --window=250# ── Common Errors & Fixes ──
# "fatal: not a git repository"
cd /path/to/repo # navigate to repo root
git init # or initialize a new one
# "error: pathspec 'branch' did not match any file(s) known to git"
git fetch origin # sync remote branches first
git branch -a # verify branch exists
# "fatal: refusing to merge unrelated histories"
git pull origin main --allow-unrelated-histories
# Use when combining two unrelated repositories
# "Your local changes would be overwritten by checkout"
git stash # stash changes first
git checkout other-branch
git stash pop # or: git checkout -f (force, discards changes)
# "commit your changes or stash them before you can merge"
git stash # stash and merge
# or: git add . && git commit (commit then merge)
# "Updates were rejected because the tip of your current branch is behind"
git pull --rebase origin main # rebase instead of merge
# or: git push --force-with-lease (if intentional)
# "fatal: The current branch feature has no upstream branch"
git push -u origin feature # set upstream on first push
# or: git branch --set-upstream-to=origin/feature
# "Automatic merge failed; fix conflicts and then commit the result"
# See merge conflict section above
# ── Using bisect to find bugs ──
git bisect start
git bisect bad HEAD # current state is broken
git bisect good v2.0.0 # this version was fine
# Git checks out a middle commit
# Test it: npm run test
git bisect bad # if it's broken
# or: git bisect good # if it works
# Repeat until the bad commit is found
git bisect reset # go back to original branch| Command | Purpose |
|---|---|
| git fsck | Check repository integrity |
| git gc | Garbage collection (optimize storage) |
| git gc --aggressive | Thorough re-pack and compression |
| git prune | Remove unreachable loose objects |
| git reflog expire | Clean up old reflog entries |
| git fetch --prune | Remove stale remote branches |
| git repack -a -d | Re-pack all objects into single pack |
| git count-objects -vH | Show repository size stats |
git-filter-repo can clean history, but assume the secret is already leaked.git fsck periodically to catch repository corruption early. If git gc --aggressive doesn't fix size issues, use git-filter-repo to remove large files or sensitive data from history.Git is a distributed version control system (DVCS) where every developer has a full copy of the repository, including the complete history. SVN is centralized — developers check out working copies and need the server for most operations.
| Aspect | Git (DVCS) | SVN (Centralized) |
|---|---|---|
| Repository | Full clone locally | Working copy only |
| Offline work | Full history, commits, branches | Limited (no history) |
| Speed | Fast (local operations) | Network-dependent |
| Branching | Cheap, fast (pointer) | Expensive (directory copy) |
| Learning curve | Steeper | Gentler |
| Storage | Full repo on every machine | Only server has full history |
merge combines two branches by creating a new merge commit that has two parent commits. It preserves the complete history including branch topology.
rebase replays your branch's commits on top of another branch, creating a linear history. It rewrites commit hashes.
# Merge: creates merge commit, preserves history
git checkout main
git merge feature
# A---B---C---M (M has 2 parents: C and feature tip)
# Rebase: linear history, rewrites commits
git checkout feature
git rebase main
# A---B---C---D'---E' (D' and E' are new commits)Best practice: Use rebase on local feature branches to keep history clean. Use merge for integrating features into shared branches (like main).
Detached HEAD means you're not on any branch — HEAD points directly to a specific commit instead of a branch reference. Any commits you make won't belong to any branch and may be lost.
# How it happens:
git checkout abc1234 # checkout specific commit → detached HEAD
# Fix: create a branch to save your work
git checkout -b feature/saved-work
# Fix: return to a branch (discard detached work)
git checkout mainKey insight: In Git, HEAD is a pointer to the current branch reference, and branches are pointers to commits. Detached HEAD means HEAD points to a commit directly, bypassing the branch.
All three move HEAD to a different commit. The difference is what happens to the staging area and working directory:
| Flag | HEAD Moved? | Index (Staged) | Working Dir |
|---|---|---|---|
| --soft | Yes | Preserved (staged) | Preserved |
| --mixed (default) | Yes | Cleared (unstaged) | Preserved |
| --hard | Yes | Cleared | Cleared (discarded!) |
# --soft: undo commit, keep everything staged (for recommitting)
git reset --soft HEAD~1
# --mixed: undo commit + unstage (default, for re-staging selectively)
git reset HEAD~1
git reset --mixed HEAD~1
# --hard: obliterate everything (for starting over)
git reset --hard HEAD~1Warning: --hard is irreversible for uncommitted work. --soft and --mixed preserve your file changes.
Conflicts occur when two branches modify the same lines in the same file. Git marks conflicts with <<<<<<< , =======, and >>>>>>> markers.
# 1. Identify conflicted files
git status
git diff --name-only --diff-filter=U
# 2. Open and resolve manually, or use a merge tool
git mergetool # opens configured tool (vimdiff, meld, etc.)
# 3. After resolving each file
git add resolved-file.txt
# 4. Complete the merge
git commit
# Alternative: accept one side entirely
git checkout --ours file.txt # current branch version
git checkout --theirs file.txt # incoming branch version
# Abort if needed
git merge --abortThe staging area (also called the index) is an intermediate zone between your working directory and the repository. It lets you compose commits atomically by choosing exactly which changes to include.
# Working Directory → Staging Area (index) → Repository
# Stage specific files
git add auth/login.ts
git add auth/register.ts
# Stage part of a file (interactive)
git add -p components/UserForm.tsx
# See what's staged vs unstaged
git status # shows both
git diff # unstaged changes
git diff --staged # staged changes
# Why it matters:
# - Compose logical commits (one feature per commit)
# - Review what will be committed
# - Selectively stage parts of filesAnalogy: Think of the staging area as a shopping cart. You pick specific items (changes) before checkout (commit), rather than buying everything in the store at once.
git cherry-pick applies a specific commit from one branch onto another. It creates a new commit with the same changes and (by default) the same message.
# Cherry-pick a single commit
git cherry-pick abc1234
# Cherry-pick multiple commits
git cherry-pick abc1234 def5678
# Cherry-pick a range (not including start commit)
git cherry-pick feature~3..feature
# Cherry-pick without committing (edit first)
git cherry-pick --no-commit abc1234
git commit -m "custom message for picked commit"
# Common use cases:
# 1. Hotfix: apply a bug fix from develop to release branch
git checkout release/1.2
git cherry-pick abc1234 # the fix commit
# 2. Backport: apply specific changes across versions
git checkout v1.x
git cherry-pick feature~2..featureThe reflog (reference log) records every movement of HEAD and branch references. It's like a journal of everything you've done in the repository, including resets, checkouts, merges, and rebases.
# View reflog
git reflog
# abc1234 HEAD@{0} checkout: moving from feature to main
# def5678 HEAD@{1} commit: feat: add user profile
# ghi9012 HEAD@{2} rebase finished
# ...
# Recovery scenario:
# You ran: git reset --hard HEAD~5 (oops!)
git reflog
# Find the commit before the bad reset, e.g. HEAD@{6}
git reset --hard HEAD@{6} # fully recovered!
# Reflog defaults:
# - Retained for 90 days
# - Unreachable refs: 30 days
git config --global gc.reflogExpire 90
git config --global gc.reflogExpireUnreachable 30Key takeaway: The reflog is your ultimate safety net. Almost anything that seems "lost" in Git can be recovered through the reflog. It keeps entries for 90 days by default.
Git stores everything as objects identified by SHA-1 hashes. There are four types:
| Object | Content | Example |
|---|---|---|
| blob | File contents (no filename) | const x = 1; → SHA-abc... |
| tree | List of blob/tree refs + names | src/ → {main.ts: blob-def..., utils/ → tree-ghi...} |
| commit | Tree ref + parent(s) + metadata | tree: abc, parent: def, author: Alice, msg: "fix" |
| tag | Points to commit + tagger + message | object: abc, type: commit, tag: v1.0 |
# Inspect objects directly
git cat-file -t abc1234 # object type (blob, tree, commit, tag)
git cat-file -p abc1234 # print contents
git cat-file -s abc1234 # object size
# Explore the object store
git ls-tree HEAD # show root tree
git ls-tree -r HEAD # recursive tree listing
git ls-files # list all tracked filesgit fetch downloads new data from the remote but does not modify your working directory or branches. git pull = git fetch + git merge (or rebase).
# fetch: download only, safe
git fetch origin
git log HEAD..origin/main # see what's new (not yet merged)
# pull: fetch + merge
git pull origin main # default (merge)
git pull --rebase origin main # preferred (cleaner history)
# When to use fetch vs pull:
# fetch → review changes before integrating, script safety
# pull → quick update, one-step workflowRecommendation: Use git fetch followed by git log to review incoming changes, then git merge or git rebase explicitly. This gives you more control than git pull.