Skip to main content

Git Cheatsheet

A comprehensive quick reference for Git commands, workflows, and best practices. Perfect for developers who need fast access to Git syntax and common operations.

Table of Contents


Prerequisites

Git Version: This cheatsheet requires Git 2.23+. Some commands use newer syntax (e.g., git restore, git switch) introduced in Git 2.23.

Terminal window
# Check Git version
git --version
# β†’ git version 2.43.0
# Verify minimum version (2.23+)
git --version | grep -oE '[0-9]+\.[0-9]+' | awk -F. '{if ($1 > 2 || ($1 == 2 && $2 >= 23)) exit 0; else exit 1}'

Installation:

  • macOS: brew install git or download from git-scm.com
  • Linux: sudo apt install git (Debian/Ubuntu) or sudo yum install git (RHEL/CentOS)
  • Windows: Download Git for Windows from git-scm.com

Configuration

Initial Setup πŸ”§

Terminal window
# Set username and email (required for commits)
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# Verify configuration
git config --global user.name
# β†’ Your Name
# Set default branch name (Git 2.28+)
git config --global init.defaultBranch main
# Set default editor
git config --global core.editor "code --wait" # VS Code
git config --global core.editor "vim" # Vim
git config --global core.editor "nano" # Nano
# Set default pull strategy
git config --global pull.rebase false # merge (default)
git config --global pull.rebase true # rebase

Configuration Levels

Terminal window
# System-wide (all users on machine)
git config --system <key> <value>
# Example: git config --system core.editor vim
# Global (current user, all repos)
git config --global <key> <value>
# Example: git config --global user.name "John Doe"
# Local (current repository only)
git config --local <key> <value>
# Example: git config --local user.email "work@company.com"
# View all settings
git config --list
git config --list --show-origin # Show where each setting comes from
# View specific setting
git config user.name
git config --global user.email
# Remove setting
git config --global --unset user.name

Useful Aliases ⚑

Terminal window
# 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.unstage 'reset HEAD --'
# Advanced aliases
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual 'log --graph --oneline --all --decorate'
git config --global alias.lg 'log --oneline --graph --all --decorate'
git config --global alias.amend 'commit --amend --no-edit'
# Use aliases
git st # β†’ git status
git co main # β†’ git checkout main
git ci -m "message" # β†’ git commit -m "message"

Common Configuration Options

Terminal window
# Enable colored output
git config --global color.ui auto
# Set line ending handling (Windows)
git config --global core.autocrlf true
# macOS/Linux
git config --global core.autocrlf input
# Set default push behavior
git config --global push.default simple # Push current branch only
git config --global push.default current # Push current branch to matching name
# Enable credential helper (cache credentials)
git config --global credential.helper cache
git config --global credential.helper 'cache --timeout=3600' # 1 hour
# Set merge tool
git config --global merge.tool vimdiff
git config --global diff.tool vimdiff

Repository Initialization

Create New Repository πŸ“

Terminal window
# Initialize in current directory
git init
# β†’ Initialized empty Git repository in /path/to/repo/.git/
# Initialize with specific branch name
git init --initial-branch=main
git init -b main # Shorthand
# Create new directory and initialize
git init project-name
# β†’ Creates project-name/ directory and initializes repo
# Initialize with initial commit
git init && git commit --allow-empty -m "Initial commit"

Clone Existing Repository

Terminal window
# Basic clone (creates repo/ directory)
git clone https://github.com/user/repo.git
# β†’ Cloning into 'repo'...
# Clone into specific directory
git clone https://github.com/user/repo.git my-folder
# β†’ Cloning into 'my-folder'...
# Clone specific branch
git clone -b branch-name https://github.com/user/repo.git
git clone --branch branch-name https://github.com/user/repo.git
# Shallow clone (faster, less history)
git clone --depth 1 https://github.com/user/repo.git
git clone --depth 5 https://github.com/user/repo.git # Last 5 commits
# Clone without checking out working directory
git clone --no-checkout https://github.com/user/repo.git
# Clone with submodules
git clone --recurse-submodules https://github.com/user/repo.git
git clone --recursive https://github.com/user/repo.git # Shorthand

Verify Repository Status

Terminal window
# Check if directory is a Git repository
git rev-parse --is-inside-work-tree
# β†’ true (if inside repo) or error (if not)
# Show repository root
git rev-parse --show-toplevel
# β†’ /path/to/repository
# Check repository status
git status

Staging & Commits

Basic Operations πŸ’Ύ

Terminal window
# Check status (detailed)
git status
# β†’ On branch main
# β†’ Changes not staged for commit: ...
# Short status format
git status -s
# β†’ M file.txt (Modified)
# β†’ A new.txt (Added)
# β†’ D old.txt (Deleted)
# β†’ ?? untracked.txt (Untracked)
# Stage specific file
git add filename.txt
# Stage multiple files
git add file1.txt file2.txt
# Stage all changes in current directory
git add .
# Stage all changes in repository
git add -A
git add --all
# Stage with pattern matching
git add *.js # All .js files
git add src/**/*.ts # All .ts files in src/
# Interactive staging (patch mode) - review changes
git add -p
git add --patch
# β†’ Stage this hunk [y,n,q,a,d,/,e,?]?
# Unstage files (Git 2.23+)
git restore --staged filename.txt
# Unstage files (older syntax)
git reset HEAD filename.txt
git reset HEAD -- filename.txt # Safer with --
# Unstage all files
git restore --staged .
git reset HEAD .

Committing Changes

Terminal window
# Basic commit
git commit -m "Add user authentication"
# Commit with detailed message
git commit -m "feat: add user authentication" -m "Implements JWT-based auth with refresh tokens"
# Stage and commit in one step (only tracked files)
git commit -am "Update dependencies"
# Equivalent to: git add -A && git commit -m "..."
# Amend last commit (change message or add files)
git commit --amend -m "New commit message"
git commit --amend --no-edit # Keep same message, add staged changes
# Amend with file changes
git add forgotten-file.txt
git commit --amend --no-edit
# Empty commit (useful for CI triggers)
git commit --allow-empty -m "Trigger CI build"
# Commit with specific author (override config)
git commit -m "Message" --author="John Doe <john@example.com>"
# Commit with date override
git commit -m "Message" --date="2024-01-01T00:00:00"

Commit Message Best Practices

TypeFormatExample
feat:New featurefeat: add user authentication
fix:Bug fixfix: resolve null pointer exception
docs:Documentationdocs: update API documentation
style:Formattingstyle: format code with prettier
refactor:Code refactoringrefactor: simplify payment logic
test:Teststest: add unit tests for user service
chore:Maintenancechore: update dependencies
perf:Performanceperf: optimize database queries
ci:CI/CDci: add GitHub Actions workflow

Conventional Commits Format:

<type>(<scope>): <subject>
<body>
<footer>

Example:

feat(auth): add JWT token refresh
Implement automatic token refresh mechanism to improve
user experience and security. Tokens refresh 5 minutes
before expiration.
Closes #123

Branch Management

Creating & Switching Branches 🌿

Terminal window
# List local branches
git branch
# β†’ * main
# β†’ feature/login
# List remote branches
git branch -r
# β†’ origin/main
# β†’ origin/feature/login
# List all branches (local + remote)
git branch -a
# β†’ * main
# β†’ feature/login
# β†’ remotes/origin/main
# List with last commit info
git branch -v
# β†’ * main abc1234 Initial commit
# β†’ feature/login def5678 Add login form
# List branches merged into current branch
git branch --merged
# List branches not merged into current branch
git branch --no-merged
# Create new branch (doesn't switch)
git branch feature-name
# Switch to branch (Git 2.23+)
git switch feature-name
# Switch to branch (older syntax)
git checkout feature-name
# Create and switch in one command (Git 2.23+)
git switch -c feature-name
git switch --create feature-name
# Create and switch (older syntax)
git checkout -b feature-name
# Create branch from specific commit
git branch feature-name abc123
git switch -c feature-name abc123
# Create branch from remote branch
git branch feature-name origin/feature-name
git switch -c feature-name origin/feature-name
# Create branch tracking remote
git branch --track feature-name origin/feature-name
git switch --track origin/feature-name # Auto-name branch

Deleting Branches

Terminal window
# Delete local branch (safe - only if merged)
git branch -d branch-name
git branch --delete branch-name
# Force delete local branch (even if not merged)
git branch -D branch-name
git branch --delete --force branch-name
# Delete remote branch
git push origin --delete branch-name
git push origin :branch-name # Older syntax
# Delete multiple remote branches
git push origin --delete branch1 branch2 branch3
# Prune remote-tracking branches (delete local refs to deleted remote branches)
git fetch --prune
git fetch -p # Shorthand

Renaming Branches

Terminal window
# Rename current branch
git branch -m new-name
git branch --move new-name
# Rename specific branch
git branch -m old-name new-name
# Update remote after rename
git push origin -u new-name
git push origin --delete old-name
# Rename and update tracking
git branch -m old-name new-name
git push origin -u new-name
git push origin --delete old-name

Branch Information

Terminal window
# Show current branch name
git branch --show-current
# β†’ main
# Show branch tracking info
git branch -vv
# β†’ * main abc1234 [origin/main] Initial commit
# β†’ feature def5678 [origin/feature: ahead 2] Add feature
# Show which branches contain a commit
git branch --contains abc123
git branch -r --contains abc123 # Remote branches only

Remote Repositories

Managing Remotes 🌐

Terminal window
# List remotes
git remote
# β†’ origin
# List remotes with URLs
git remote -v
# β†’ origin https://github.com/user/repo.git (fetch)
# β†’ origin https://github.com/user/repo.git (push)
# Show remote details
git remote show origin
# β†’ * remote origin
# β†’ Fetch URL: https://github.com/user/repo.git
# β†’ Push URL: https://github.com/user/repo.git
# β†’ HEAD branch: main
# Add remote
git remote add origin https://github.com/user/repo.git
git remote add upstream https://github.com/original/repo.git
# Change remote URL
git remote set-url origin https://github.com/user/new-repo.git
# Change remote URL for specific operation
git remote set-url --push origin https://github.com/user/repo.git
# Remove remote
git remote remove origin
git remote rm origin # Shorthand
# Rename remote
git remote rename origin upstream
# Update remote URL (HTTPS to SSH)
git remote set-url origin git@github.com:user/repo.git

Fetching & Pulling

Terminal window
# Fetch updates from remote (doesn't merge)
git fetch origin
# β†’ Fetching origin...
# β†’ remote: Counting objects: 5, done.
# Fetch all remotes
git fetch --all
git fetch -a # Shorthand
# Fetch specific branch
git fetch origin main
# Fetch and prune (remove deleted remote branches)
git fetch --prune origin
git fetch -p origin # Shorthand
# Pull updates (fetch + merge)
git pull origin main
# Pull current branch from tracked remote
git pull
# Pull with rebase instead of merge
git pull --rebase origin main
git pull -r origin main # Shorthand
# Pull specific branch
git pull origin feature-branch
# Pull with no fast-forward (always create merge commit)
git pull --no-ff origin main
# Pull and squash commits
git pull --squash origin main

Pushing Changes

Terminal window
# Push to remote
git push origin main
# Push current branch to tracked remote
git push
# Push and set upstream (first push)
git push -u origin main
git push --set-upstream origin main
# Push all branches
git push --all origin
# Push all tags
git push --tags
git push origin --tags
# Push specific tag
git push origin v1.0.0
# Force push (use with caution!) ⚠️
git push --force origin main
git push -f origin main # Shorthand
# Safer force push (fails if remote has new commits)
git push --force-with-lease origin main
# Push with specific remote branch name
git push origin local-branch:remote-branch
# Push and delete remote branch
git push origin :remote-branch

⚠️ Warning: git push --force rewrites remote history and can cause data loss. Use --force-with-lease instead, which fails if someone else has pushed commits you don’t have locally.


History & Inspection

Viewing History πŸ“œ

Terminal window
# Basic log (full details)
git log
# Compact one-line format
git log --oneline
# β†’ abc1234 feat: add authentication
# β†’ def5678 fix: resolve login bug
# Graph view with all branches
git log --oneline --graph --all
# β†’ * abc1234 (HEAD -> main) feat: add auth
# β†’ | * def5678 (feature/login) fix: login bug
# β†’ |/
# β†’ * ghi9012 Initial commit
# Last N commits
git log -n 5
git log -5 # Shorthand
git log --oneline -5
# Specific author
git log --author="John Doe"
git log --author="John" # Partial match
# Date range
git log --since="2024-01-01"
git log --after="2 weeks ago"
git log --before="2024-01-01"
git log --since="2024-01-01" --until="2024-01-31"
# File history
git log -- filename.txt
git log --follow -- filename.txt # Includes renames
# Search commit messages
git log --grep="bug fix"
git log --grep="bug" -i # Case insensitive
# Search code content
git log -S "function_name"
git log -S "TODO" --source --all
# Commits affecting specific line range
git log -L 10,20:filename.txt
git log -L :function_name:filename.txt

Advanced Log Formatting

Terminal window
# Pretty formatting with custom format
git log --pretty=format:"%h - %an, %ar : %s"
# β†’ abc1234 - John Doe, 2 hours ago : Add feature
# Common format placeholders
# %h = abbreviated commit hash
# %an = author name
# %ar = author date, relative
# %s = subject (commit message)
# %d = ref names
# %cr = committer date, relative
# Custom graph view with colors
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
# Show files changed
git log --stat
# β†’ Shows file names and line counts
git log --name-only
# β†’ Shows only file names
git log --name-status
# β†’ Shows file names with status (A/M/D)
# Combine options
git log --oneline --graph --all --decorate --stat

Inspecting Changes

Terminal window
# Show specific commit (full diff)
git show abc123
# Show commit without diff
git show --stat abc123
git show --name-only abc123
# Show commit message only
git show --no-patch abc123
git show -s abc123 # Shorthand
# Diff between commits
git diff abc123 def456
# Diff between branches
git diff main feature-branch
# Diff staged changes
git diff --staged
git diff --cached # Alias
# Diff working directory vs staged
git diff
# Diff specific file
git diff HEAD filename.txt
git diff HEAD~1 HEAD -- filename.txt
# Word-level diff (instead of line-level)
git diff --word-diff
# Ignore whitespace in diff
git diff -w
git diff --ignore-all-space
# Show summary of changes
git diff --stat
git diff --shortstat

Finding Commits

Terminal window
# Find commit that introduced a bug (use bisect)
git bisect start
git bisect bad # Current commit is bad
git bisect good abc123 # This commit was good
# Git checks out middle commit, test it:
git bisect good # or git bisect bad
# Repeat until found
git bisect reset # Return to original branch
# Search commits by content
git log --all --full-history -- "path/to/file"
git log --all --grep="search term"
# Find when a line was added/removed
git log -p -S "search string" -- filename.txt
git log -p --all -S "function_name"

Undoing Changes

Discarding Changes ↩️

Terminal window
# Discard changes in working directory (Git 2.23+)
git restore filename.txt
git restore . # All files
# Discard changes (older syntax)
git checkout -- filename.txt
git checkout -- . # All files
# Discard changes in specific directory
git restore src/
git checkout -- src/
# Unstage file (Git 2.23+)
git restore --staged filename.txt
git restore --staged . # All files
# Unstage file (older syntax)
git reset HEAD filename.txt
git reset HEAD -- filename.txt # Safer
# Discard both staging and working directory changes
git restore --source=HEAD --staged --worktree filename.txt

Reverting Commits

Terminal window
# Create new commit that undoes changes (safe for shared history)
git revert abc123
# β†’ Creates new commit that reverses abc123
# Revert without committing (stage changes only)
git revert --no-commit abc123
git revert --no-commit abc123 def456 # Multiple commits
git commit -m "Revert changes"
# Revert multiple commits (in order)
git revert abc123..def456
git revert --no-commit abc123^..def456
# Revert merge commit
git revert -m 1 abc123 # -m 1 keeps first parent (main branch)
git revert -m 2 abc123 # -m 2 keeps second parent (feature branch)
# Revert with custom message
git revert -m "Revert: reason for revert" abc123

Resetting Commits

Terminal window
# Soft reset (keep changes staged)
git reset --soft HEAD~1
# β†’ Moves HEAD back, keeps changes in staging area
# Mixed reset (keep changes unstaged) - DEFAULT
git reset HEAD~1
git reset --mixed HEAD~1
# β†’ Moves HEAD back, keeps changes in working directory
# Hard reset (discard all changes) ⚠️
git reset --hard HEAD~1
# β†’ Moves HEAD back, discards all changes
# Reset to specific commit
git reset --hard abc123
git reset --soft abc123
git reset --mixed abc123
# Reset specific file to HEAD
git reset HEAD filename.txt
git checkout HEAD -- filename.txt
# Reset to remote branch state
git reset --hard origin/main
# Undo reset with reflog
git reflog
# β†’ abc1234 HEAD@{0}: reset: moving to HEAD~1
# β†’ def5678 HEAD@{1}: commit: previous commit
git reset --hard HEAD@{1} # Restore to before reset

⚠️ Warning: git reset --hard permanently deletes uncommitted changes. Always commit or stash important work before using hard reset. Use git reflog to recover from accidental resets.

Recovering Lost Commits

Terminal window
# View reflog (history of HEAD movements)
git reflog
# β†’ abc1234 HEAD@{0}: commit: latest commit
# β†’ def5678 HEAD@{1}: reset: moving to HEAD~1
# β†’ ghi9012 HEAD@{2}: commit: previous commit
# Recover commit from reflog
git checkout abc1234
git checkout -b recovered-branch abc1234
# Recover deleted branch
git reflog | grep "branch-name"
git branch recovered-branch abc1234

Stashing

Save Work in Progress πŸ“¦

Terminal window
# Stash changes (default message)
git stash
git stash save # Explicit
# Stash with message
git stash save "Work in progress on feature X"
# Stash including untracked files
git stash -u
git stash --include-untracked
# Stash including ignored files
git stash -a
git stash --all
# Stash specific files
git stash push -m "message" -- file1.txt file2.txt
# List stashes
git stash list
# β†’ stash@{0}: WIP on main: abc1234 Latest commit
# β†’ stash@{1}: On feature: def5678 Previous commit
# Show stash contents (summary)
git stash show
git stash show stash@{0}
# Show stash diff
git stash show -p
git stash show -p stash@{0}

Applying Stashes

Terminal window
# Apply most recent stash (keeps stash)
git stash apply
git stash apply stash@{0}
# Apply specific stash
git stash apply stash@{2}
# Apply and remove stash
git stash pop
git stash pop stash@{0}
# Apply stash to different branch
git checkout other-branch
git stash apply stash@{0}
# Create branch from stash
git stash branch new-branch-name stash@{0}
# β†’ Creates branch, applies stash, deletes stash if successful

Managing Stashes

Terminal window
# Delete specific stash
git stash drop stash@{0}
# Delete most recent stash
git stash drop
# Delete all stashes
git stash clear
# Show stash as patch (for sharing)
git stash show -p stash@{0} > stash.patch
# Apply patch file
git apply stash.patch

Tags

Creating Tags 🏷️

Terminal window
# Lightweight tag (just a pointer)
git tag v1.0.0
# Annotated tag (recommended - includes metadata)
git tag -a v1.0.0 -m "Release version 1.0.0"
# Tag specific commit
git tag -a v1.0.0 abc123 -m "Tag message"
# Tag with signature (GPG)
git tag -s v1.0.0 -m "Signed release"
# Verify tag signature
git tag -v v1.0.0
# List tags
git tag
# β†’ v1.0.0
# β†’ v1.1.0
# β†’ v2.0.0
# List tags with pattern matching
git tag -l "v1.*"
# β†’ v1.0.0
# β†’ v1.1.0
# List tags sorted by version
git tag -l --sort=-version:refname "v*"

Managing Tags

Terminal window
# Show tag details
git show v1.0.0
# Show tag message only
git tag -l -n1 v1.0.0
git tag -l -n3 # Show first 3 lines of each tag
# Push tag to remote
git push origin v1.0.0
# Push all tags
git push --tags
git push origin --tags
# Push all tags with commits
git push --follow-tags
# Delete local tag
git tag -d v1.0.0
git tag --delete v1.0.0
# Delete remote tag
git push origin --delete v1.0.0
git push origin :refs/tags/v1.0.0 # Older syntax
# Checkout tag (creates detached HEAD)
git checkout v1.0.0
# Create branch from tag
git checkout -b release-1.0.0 v1.0.0

Tagging Best Practices

Semantic Versioning:

  • MAJOR.MINOR.PATCH (e.g., 1.2.3)
  • MAJOR: incompatible API changes
  • MINOR: backwards-compatible functionality
  • PATCH: backwards-compatible bug fixes
Terminal window
# Example: Tagging a release
git tag -a v1.2.3 -m "Release 1.2.3: Bug fixes and performance improvements"
git push origin v1.2.3

Merging & Rebasing

Merging Branches πŸ”€

Terminal window
# Merge branch into current branch
git merge feature-branch
# Merge with commit message
git merge feature-branch -m "Merge feature X into main"
# Merge without fast-forward (always create merge commit)
git merge --no-ff feature-branch
# Merge with fast-forward only (fails if not possible)
git merge --ff-only feature-branch
# Squash merge (combine all commits into one)
git merge --squash feature-branch
git commit -m "Add feature X"
# Abort merge (during conflicts)
git merge --abort
# Continue after resolving conflicts
git add resolved-file.txt
git merge --continue
git commit # Alternative

Handling Merge Conflicts

Terminal window
# Check conflict status
git status
# β†’ Unmerged paths:
# β†’ both modified: file.txt
# See conflicts in files
# Look for conflict markers:
# <<<<<<< HEAD
# current branch changes
# =======
# incoming branch changes
# >>>>>>> feature-branch
# After manually resolving conflicts
git add resolved-file.txt
git commit
git merge --continue
# Use theirs strategy (accept incoming changes)
git checkout --theirs filename.txt
git add filename.txt
# Use ours strategy (keep current changes)
git checkout --ours filename.txt
git add filename.txt
# Abort merge and return to pre-merge state
git merge --abort
# List conflicted files
git diff --name-only --diff-filter=U

Rebasing ⚑

Terminal window
# Rebase current branch onto main
git rebase main
# Rebase onto specific branch
git rebase origin/main
# Interactive rebase (last 3 commits)
git rebase -i HEAD~3
# Interactive rebase from specific commit
git rebase -i abc123
# Abort rebase
git rebase --abort
# Continue after resolving conflicts
git add resolved-file.txt
git rebase --continue
# Skip commit during rebase
git rebase --skip
# Edit commit during rebase
git rebase -i HEAD~3
# Change 'pick' to 'edit' for commit
# Make changes, then:
git add .
git commit --amend
git rebase --continue

Interactive Rebase Commands

CommandAction
pickKeep commit as is
rewordChange commit message
editPause to amend commit
squashCombine with previous commit
fixupLike squash, discard message
dropRemove commit
execRun command

Example Interactive Rebase:

Terminal window
git rebase -i HEAD~3
# Opens editor with:
# pick abc1234 First commit
# reword def5678 Second commit
# squash ghi9012 Third commit

⚠️ Warning: Never rebase commits that have been pushed to a shared branch. Rebasing rewrites history and can cause issues for other developers.


Advanced Operations

Cherry Picking πŸ’

Terminal window
# Apply specific commit to current branch
git cherry-pick abc123
# Cherry-pick multiple commits
git cherry-pick abc123 def456
git cherry-pick abc123..def456 # Range (exclusive of abc123)
# Cherry-pick without committing
git cherry-pick --no-commit abc123
git add .
git commit -m "Custom message"
# Cherry-pick and edit commit message
git cherry-pick -e abc123
git cherry-pick --edit abc123
# Abort cherry-pick
git cherry-pick --abort
# Continue after resolving conflicts
git add resolved-file.txt
git cherry-pick --continue
# Skip commit (if conflicts can't be resolved)
git cherry-pick --skip

Bisect (Find Bugs) πŸ”

Terminal window
# Start bisect session
git bisect start
# Mark current commit as bad
git bisect bad
git bisect bad HEAD # Explicit
# Mark known good commit
git bisect good abc123
git bisect good v1.0.0 # Can use tag
# After testing each commit
git bisect good # Current commit is good
git bisect bad # Current commit is bad
# Automate with script
git bisect run test-script.sh
# Script should exit 0 for good, non-zero for bad
# Skip commit (can't test)
git bisect skip
# Visualize bisect progress
git bisect log
# End bisect and return to original branch
git bisect reset

Submodules

Terminal window
# Add submodule
git submodule add https://github.com/user/repo.git path/to/submodule
# Clone repo with submodules
git clone --recurse-submodules https://github.com/user/repo.git
git clone --recursive https://github.com/user/repo.git # Shorthand
# Initialize submodules in existing clone
git submodule init
git submodule update
# Initialize and update in one command
git submodule update --init --recursive
# Update all submodules to latest
git submodule update --remote
# Update specific submodule
git submodule update --remote path/to/submodule
# Remove submodule
git submodule deinit path/to/submodule
git rm path/to/submodule
rm -rf .git/modules/path/to/submodule
git commit -m "Remove submodule"
# Show submodule status
git submodule status

Working with Archives

Terminal window
# Create ZIP archive
git archive --format=zip --output=project.zip main
# Create tarball
git archive --format=tar.gz --output=project.tar.gz main
# Create tar archive
git archive --format=tar --output=project.tar main
# Archive specific commit
git archive --format=zip abc123 --output=snapshot.zip
# Archive with prefix (adds directory prefix)
git archive --format=zip --prefix=project-v1.0/ --output=project.zip v1.0.0
# Archive specific directory
git archive --format=zip main --output=src.zip src/

Git Hooks

Terminal window
# List available hooks
ls .git/hooks/
# β†’ applypatch-msg.sample
# β†’ pre-commit.sample
# β†’ post-commit.sample
# β†’ ...
# Create pre-commit hook (example)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
# Run linter before commit
npm run lint
EOF
chmod +x .git/hooks/pre-commit
# Bypass hooks (use with caution)
git commit --no-verify
git push --no-verify

Common Workflows

Feature Branch Workflow πŸš€

Terminal window
# 1. Create feature branch from main
git checkout main
git pull origin main
git checkout -b feature/new-feature
# 2. Make changes and commit
git add .
git commit -m "feat: implement new feature"
# 3. Keep branch updated with main
git fetch origin
git rebase origin/main
# Or merge: git merge origin/main
# 4. Push to remote
git push -u origin feature/new-feature
# 5. After PR approval, merge to main
git checkout main
git pull origin main
git merge --no-ff feature/new-feature
git push origin main
# 6. Clean up local and remote branches
git branch -d feature/new-feature
git push origin --delete feature/new-feature

Hotfix Workflow πŸ”₯

Terminal window
# 1. Create hotfix from main/production
git checkout main
git pull origin main
git checkout -b hotfix/critical-bug
# 2. Fix and commit
git add .
git commit -m "fix: resolve critical security issue"
# 3. Test thoroughly, then merge to main
git checkout main
git merge --no-ff hotfix/critical-bug
# 4. Tag release
git tag -a v1.0.1 -m "Hotfix release v1.0.1"
# 5. Merge to develop (if using Git Flow)
git checkout develop
git merge hotfix/critical-bug
# 6. Push everything
git push origin main develop --tags
git branch -d hotfix/critical-bug
git push origin --delete hotfix/critical-bug

Sync Fork with Upstream ⬆️

Terminal window
# 1. Add upstream remote (one-time setup)
git remote add upstream https://github.com/original/repo.git
# 2. Verify remotes
git remote -v
# β†’ origin https://github.com/your-username/repo.git (fetch)
# β†’ origin https://github.com/your-username/repo.git (push)
# β†’ upstream https://github.com/original/repo.git (fetch)
# β†’ upstream https://github.com/original/repo.git (push)
# 3. Fetch upstream changes
git fetch upstream
# 4. Merge into local main
git checkout main
git merge upstream/main
# 5. Push to your fork
git push origin main
# Alternative: Rebase instead of merge
git checkout main
git rebase upstream/main
git push origin main --force-with-lease

Release Workflow 🎯

Terminal window
# 1. Create release branch from main
git checkout main
git pull origin main
git checkout -b release/v1.2.0
# 2. Update version numbers, changelog, etc.
# Edit files, then:
git add .
git commit -m "chore: bump version to 1.2.0"
# 3. Merge to main
git checkout main
git merge --no-ff release/v1.2.0
# 4. Tag release
git tag -a v1.2.0 -m "Release version 1.2.0"
# 5. Merge to develop
git checkout develop
git merge --no-ff release/v1.2.0
# 6. Push everything
git push origin main develop --tags
# 7. Clean up
git branch -d release/v1.2.0

Troubleshooting

Common Issues & Solutions πŸ”§

Problem: Committed to wrong branch

Terminal window
# Move commits to new branch
git branch new-branch # Create branch at current commit
git reset --hard HEAD~3 # Remove 3 commits from current branch
git checkout new-branch # Switch to new branch with commits
# Alternative: Cherry-pick approach
git checkout correct-branch
git cherry-pick abc123 def456 ghi789 # Pick commits from wrong branch
git checkout wrong-branch
git reset --hard HEAD~3 # Remove commits from wrong branch

Problem: Need to edit commit history

Terminal window
# Interactive rebase (edit last 5 commits)
git rebase -i HEAD~5
# Change 'pick' to 'edit' for commits to modify
# Make changes, then:
git add .
git commit --amend
git rebase --continue

Problem: Pushed sensitive data

Terminal window
# Remove file from history (Git 2.23+)
git filter-repo --path secrets.txt --invert-paths
# Or use filter-branch (older method)
git filter-branch --tree-filter 'rm -f secrets.txt' HEAD
git filter-branch --index-filter 'git rm --cached --ignore-unmatch secrets.txt' HEAD
# Force push (coordinate with team!)
git push origin --force --all
git push origin --force --tags
# Better: Use BFG Repo-Cleaner (faster)
bfg --delete-files secrets.txt
git reflog expire --expire=now --all
git gc --prune=now --aggressive

Problem: Detached HEAD state

Terminal window
# Create branch from detached HEAD
git checkout -b new-branch
# Or return to previous branch
git checkout main
git checkout - # Switch to previous branch
# Find which branch you were on
git reflog | grep checkout

Problem: Large repository / performance issues

Terminal window
# Clean up unnecessary files
git gc # Garbage collection
git prune # Remove unreachable objects
# Aggressive cleanup
git gc --aggressive --prune=now
# Clean up reflog
git reflog expire --expire=now --all
git gc --prune=now
# Find large files in history
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print substr($0,6)}' | \
sort --numeric-sort --key=2 | \
tail -10

Problem: Merge conflict in binary file

Terminal window
# Use theirs version
git checkout --theirs binary-file.pdf
git add binary-file.pdf
# Use ours version
git checkout --ours binary-file.pdf
git add binary-file.pdf
# Or manually resolve and add
# (download correct version, then:)
git add binary-file.pdf

Problem: Accidentally deleted branch

Terminal window
# Find commit hash from reflog
git reflog | grep "branch-name"
# Recreate branch
git branch branch-name abc1234

Useful Inspection Commands πŸ”

Terminal window
# View commit graph
git log --graph --oneline --all --decorate
# Find commit that introduced bug
git bisect start
git bisect bad
git bisect good abc123
# Search commits by message
git log --all --grep="bug fix"
# Search commits by code content
git log -S "function_name" --all
# Find who changed a line (blame)
git blame filename.txt
git blame -L 10,20 filename.txt # Specific line range
# Show file history with renames
git log --follow -- filename.txt
# Show branch relationships
git show-branch
# View reflog (recovery tool)
git reflog
git reflog show branch-name
# Find large files
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
awk '/^blob/ {print substr($0,6)}' | \
sort --numeric-sort --key=2 --reverse | head -10

Best Practices

Do’s βœ…

  • βœ… Write clear, descriptive commit messages following Conventional Commits
  • βœ… Commit often with logical, atomic changes
  • βœ… Push regularly to backup your work
  • βœ… Use branches for features, fixes, and experiments
  • βœ… Review changes before committing (git diff, git status)
  • βœ… Pull before starting new work to stay updated
  • βœ… Use .gitignore for build artifacts, dependencies, and IDE files
  • βœ… Tag releases with semantic versioning (e.g., v1.2.3)
  • βœ… Keep commits focused on a single concern
  • βœ… Write meaningful commit messages (what and why, not just what)
  • βœ… Use --force-with-lease instead of --force when force pushing
  • βœ… Test before pushing to shared branches
  • βœ… Use pull requests for code review
  • βœ… Keep main/master branch stable and deployable

Don’ts ❌

  • ❌ Commit large binary files (use Git LFS or external storage)
  • ❌ Force push to shared branches (rewrites history)
  • ❌ Commit credentials, secrets, or API keys
  • ❌ Make changes directly on main/master branch
  • ❌ Use git add . without reviewing changes first
  • ❌ Rewrite public/shared history (causes issues for others)
  • ❌ Ignore merge conflicts (resolve them properly)
  • ❌ Commit commented-out code or debug statements
  • ❌ Write vague commit messages like β€œfix” or β€œupdate”
  • ❌ Mix unrelated changes in a single commit
  • ❌ Commit generated files or dependencies
  • ❌ Skip testing before pushing
  • ❌ Delete branches others might be using

Common Pitfalls ⚠️

⚠️ Force Pushing: Never force push to shared branches. It rewrites history and can cause serious issues for other developers. If you must force push, use --force-with-lease which fails if someone else has pushed commits.

⚠️ Hard Reset: git reset --hard permanently deletes uncommitted changes. Always commit or stash important work before using hard reset. Use git reflog to recover from accidental resets.

⚠️ Rebasing Shared Branches: Never rebase commits that have been pushed to a shared branch. Rebasing rewrites commit history and will cause conflicts for anyone who has pulled those commits.

⚠️ Credentials in History: If you accidentally commit secrets, they remain in Git history even if deleted in later commits. Use git filter-repo or BFG Repo-Cleaner to remove them, then force push (coordinate with your team first!).

Real-World Examples 🌍

Example 1: Feature Development

Terminal window
# Start feature
git checkout -b feature/user-profile main
# Make changes, commit
git commit -am "feat: add user profile page"
# Keep updated
git fetch origin && git rebase origin/main
# Push for review
git push -u origin feature/user-profile
# After PR merge, clean up
git checkout main && git pull && git branch -d feature/user-profile

Example 2: Hotfix

Terminal window
# Emergency fix
git checkout -b hotfix/security-patch main
# Fix and test
git commit -am "fix: patch security vulnerability"
# Merge and tag
git checkout main
git merge --no-ff hotfix/security-patch
git tag -a v1.0.1 -m "Security patch"
git push origin main --tags

Example 3: Recovering Lost Work

Terminal window
# Accidentally deleted branch
git reflog | grep "deleted branch"
# β†’ abc1234 HEAD@{5}: checkout: moving from deleted-branch to main
git branch recovered-branch abc1234

Quick Reference Tables

Git States

StateDescriptionCommand to reach
Working DirectoryModified files, not stagededit file
Staging AreaFiles ready to commitgit add
Local RepositoryCommitted changesgit commit
Remote RepositoryShared commitsgit push

Common Git Aliases

AliasFull CommandPurpose
git stgit statusCheck status
git cogit checkoutSwitch branches
git brgit branchList branches
git cigit commitCommit changes
git unstagegit reset HEAD --Unstage files
git lastgit log -1 HEADShow last commit
git visualgit log --graph --oneline --allVisual history

Git vs GitHub/GitLab

GitGitHub/GitLab
Version control systemWeb-based hosting service
Runs locallyRuns on remote servers
Command-line toolWeb interface + CLI
Core functionalityAdditional features (PR, Issues, CI/CD)
Open sourceProprietary (GitHub) / Open source (GitLab)

Commit Message Types

TypeUse CaseExample
featNew featurefeat: add dark mode toggle
fixBug fixfix: resolve memory leak
docsDocumentationdocs: update README
styleFormattingstyle: format with prettier
refactorCode refactoringrefactor: extract auth logic
testTeststest: add unit tests
choreMaintenancechore: update dependencies
perfPerformanceperf: optimize queries
ciCI/CDci: add GitHub Actions

Additional Resources πŸ“š

Git Version: Commands in this cheatsheet are compatible with Git 2.23+. Some commands use newer syntax (e.g., git restore, git switch) introduced in Git 2.23. For older Git versions, use the alternative syntax provided in comments.