Skip to Content

Git Cheat Sheet

A practical reference covering day-to-day Git usage, config templates, and bulk automation scripts for GitHub and Azure DevOps.

Versions: Git 2.40+ · gh CLI 2.x

Last reviewed: May 2026


Setup & Configuration

Linux / WSL .gitconfig 🏠 Personal config

Place this at ~/.gitconfig. The credential helper paths assume gh CLI is installed at /usr/bin/gh - adjust if your install location differs.

INI
[alias]
    a = add --all
    c = commit
    p = push
 
[core]
    editor = nano
    longpaths = true
 
[credential]
    helper = /mnt/c/Users/<you>/scoop/apps/git/current/mingw64/bin/git-credential-manager.exe
    useHttpPath = true
    azreposCredentialType = oauth
 
[user]
    name = Your Name
    email = you@example.com
 
[filter "lfs"]
    process = git-lfs filter-process
    required = true
    clean  = git-lfs clean -- %f
    smudge = git-lfs smudge -- %f
 
[credential "https://github.com"]
    helper =
    helper = !/usr/bin/gh auth git-credential
 
[credential "https://gist.github.com"]
    helper =
    helper = !/usr/bin/gh auth git-credential

Windows .gitconfig 🏠 Personal config

Place this at %USERPROFILE%\.gitconfig. The gh.exe path assumes Scoop - adjust to your package manager.

INI
[alias]
    a = add --all
    c = commit
    p = push
 
[core]
    editor = nano
    longpaths = true
 
[credential]
    useHttpPath = true
    azreposCredentialType = oauth
 
[user]
    name = Your Name
    email = you@example.com
 
[filter "lfs"]
    process = git-lfs filter-process
    required = true
    clean  = git-lfs clean -- %f
    smudge = git-lfs smudge -- %f
 
[credential "https://github.com"]
    helper =
    helper = !'C:\Users\<you>\scoop\apps\gh\current\bin\gh.exe' auth git-credential
 
[credential "https://gist.github.com"]
    helper =
    helper = !'C:\Users\<you>\scoop\apps\gh\current\bin\gh.exe' auth git-credential

Useful global config one-liners

Quick commands to configure Git globally without editing the file manually.

Bash
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global core.editor nano
git config --global init.defaultBranch main
git config --global pull.rebase false        # merge on pull (safe default)
git config --global core.autocrlf input      # LF on commit, leave checkouts alone (Linux/macOS)
git config --global core.autocrlf true       # CRLF on checkout, LF on commit (Windows)

Signing commits (GPG) ✅

Signed commits prove authorship and integrity - GitHub shows a Verified badge and branch protection can require signatures. Use the same email for the key, your Git config, and your verified account identity, or verification fails.

1. Generate a key

Bash
# Linux / WSL / macOS (gnupg is usually preinstalled; else `sudo apt install gnupg`)
gpg --full-generate-key
# Choose: (1) RSA and RSA · 4096 bits · expiry to taste · your name + commit email
PowerShell
# Windows - install Gpg4win (bundles gpg + Kleopatra), then generate the key
winget install GnuPG.Gpg4win      # or: scoop install gpg4win
gpg --full-generate-key

2. Find the key ID and export the public key

Bash
# Key ID is the hex after the algorithm, e.g. rsa4096/ABCD1234EF567890
gpg --list-secret-keys --keyid-format=long
 
KEYID=ABCD1234EF567890                              # substitute yours (PowerShell: $KEYID="...")
 
gpg --armor --export "$KEYID"                       # PUBLIC key - paste into the platform
gpg --armor --export "$KEYID" > public-key.asc
gpg --armor --export-secret-keys "$KEYID" > private-key.asc   # PRIVATE key - back up securely, never commit

3. Add the public key to the platform

  • GitHub: Settings → SSH and GPG keys → New GPG key (paste the block), or gh gpg-key add public-key.asc.
  • Azure DevOps (Azure Repos): no GPG key upload or Verified badge - the signature is still embedded in the commit and checkable locally with git verify-commit.

4. Sign automatically

Bash
git config --global user.signingkey "$KEYID"
git config --global commit.gpgsign true             # sign every commit
git config --global tag.gpgsign true                # sign every annotated tag
 
# Windows: point Git at the gpg binary if it isn't auto-detected
git config --global gpg.program "C:/Program Files (x86)/GnuPG/bin/gpg.exe"
 
# WSL / headless: let gpg prompt for the passphrase
echo 'export GPG_TTY=$(tty)' >> ~/.bashrc

5. Verify

Bash
git commit -S -m "signed commit"   # -S is implicit once commit.gpgsign=true
git log --show-signature -1         # expect "Good signature"
git verify-commit HEAD

Lighter alternative - SSH signing. Git 2.34+ can sign with an existing SSH key (no GPG): git config --global gpg.format ssh and git config --global user.signingkey ~/.ssh/id_ed25519.pub, then add it to GitHub as a Signing key. Simpler to manage; reach for GPG when you need keyservers, X.509/S-MIME, or a hardware token (YubiKey).


Core Operations

Daily workflow

Bash
git status                          # what's changed
git diff                            # unstaged changes
git diff --staged                   # staged changes ready to commit
 
git add .                           # stage everything
git add -p                          # interactively stage hunks
 
git commit -m "your message"
git commit --amend --no-edit        # add staged changes to last commit (unpushed only)
 
git push
git push -u origin main             # push and set upstream on first push
git pull

Initialise a repo

Bash
git init
git init --bare                     # bare repo (for remotes/servers)
git clone https://github.com/org/repo.git
git clone --depth 1 https://github.com/org/repo.git   # shallow clone (faster, no history)

Branching & Merging

Bash
git branch                          # list local branches
git branch -a                       # list all branches (local + remote)
git branch feature/my-feature       # create branch
git switch feature/my-feature       # switch to branch
git switch -c feature/my-feature    # create and switch in one step
 
git merge feature/my-feature        # merge into current branch
git merge --no-ff feature/my-feature  # always create a merge commit
 
git branch -d feature/my-feature    # delete merged branch
git branch -D feature/my-feature    # force delete (even if unmerged)
git push origin --delete feature/my-feature  # delete remote branch

Rebase

Bash
git rebase main                     # rebase current branch onto main
git rebase -i HEAD~3                # interactively squash/edit last 3 commits
git rebase --abort                  # bail out of an in-progress rebase
git rebase --continue               # continue after resolving a conflict

Remote Operations

Bash
git remote -v                       # list remotes
git remote add origin https://github.com/org/repo.git
git remote set-url origin https://github.com/org/new-repo.git
 
git fetch origin                    # download without merging
git fetch --all                     # fetch all remotes
git pull origin main
git push origin main
git push --force-with-lease         # ✅ safer force push - fails if remote has moved since last fetch

Undoing Changes

Discard working directory changes

Bash
git restore file.txt                # discard unstaged changes to a file
git restore .                       # discard ALL unstaged changes
git clean -fd                       # delete untracked files and directories

Unstage files

Bash
git restore --staged file.txt       # unstage a file (keep working copy)
git reset HEAD file.txt             # older equivalent

Undo commits

Bash
git revert HEAD                     # create a new commit that undoes HEAD (safe for shared branches)
git reset --soft HEAD~1             # undo last commit, keep changes staged
git reset --mixed HEAD~1            # undo last commit, keep changes unstaged (default)
git reset --hard HEAD~1             # 🚨 undo last commit and discard all changes (destructive)

Recover lost commits (git reflog) ✅

reflog records every move of HEAD - including commits “lost” to a hard reset, a botched rebase, or a deleted branch. It is the primary undo-almost-anything tool. Entries are local-only and expire (≈90 days for reachable, 30 for unreachable), so recover sooner rather than later.

Bash
git reflog                          # every position HEAD has held, newest first
git reflog show feature/my-feature  # reflog for a specific branch
 
# Undo a bad reset/rebase - move HEAD back to where it was N moves ago
git reset --hard HEAD@{1}
 
# Recover a specific dropped commit onto a fresh branch (non-destructive)
git checkout -b recovered HEAD@{2}
 
# Restore an accidentally deleted branch: find its old tip, then recreate it
git reflog | grep "branch-name"
git branch branch-name <sha-from-reflog>

History & Inspection

Bash
git log                             # full commit history
git log --oneline --graph --all     # visual branch graph
git log --oneline -20               # last 20 commits, one line each
git log -- path/to/file             # history for a specific file
 
git show HEAD                       # show last commit diff
git show abc1234                    # show a specific commit
 
git diff main..feature/my-feature   # diff between two branches
git diff HEAD~2 HEAD                # diff between commits
 
git blame file.txt                  # annotate each line with its last commit
git bisect start                    # binary search for a bug-introducing commit

Stashing

Bash
git stash                           # stash current changes
git stash push -m "wip: my description"
git stash list                      # list all stashes
git stash pop                       # apply latest stash and drop it
git stash apply stash@{2}           # apply a specific stash without dropping
git stash drop stash@{0}            # delete a specific stash
git stash clear                     # delete all stashes

Tags

Bash
git tag                             # list tags
git tag v1.0.0                      # create lightweight tag on HEAD
git tag -a v1.0.0 -m "Release 1.0.0"  # annotated tag with message
git push origin v1.0.0              # push a specific tag
git push origin --tags              # push all tags
git tag -d v1.0.0                   # delete local tag
git push origin --delete v1.0.0    # delete remote tag

.gitignore Reference

A general-purpose .gitignore covering Python projects, Terraform, and common IDE artefacts.

GITIGNORE
# IDE
.idea/
.vscode/

# Python
__pycache__/
*.py[cod]
*.so
.Python
build/
dist/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
pip-log.txt
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
*.cover
.hypothesis/
.pytest_cache/
.mypy_cache/
.dmypy.json
dmypy.json
.pyre/
.python-version
.env
.venv
env/
venv/
ENV/

# Terraform
**/.terraform/*
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfvars
*.tfvars.json
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraformrc
terraform.rc

# Jupyter
.ipynb_checkpoints

# macOS
.DS_Store

See also: Security - Git Secret Hunting for scanning history and detecting credentials that slipped through.


Automation Scripts

Clone all repos in a GitHub org 🏠 Personal workflow helper

Uses the gh CLI - run gh auth login first.

Bash
#!/usr/bin/env bash
 
org="libre-devops"
 
gh repo list "${org}" --limit 1000 | while read -r repo _; do
  gh repo clone "$repo" "$repo"
done

Clone all repos in an Azure DevOps project 🏠 Personal workflow helper

Uses the az CLI with the azure-devops extension. The script logs in if needed and skips repos that are already cloned locally.

PowerShell
#!/usr/bin/env pwsh
 
$Org  = "https://dev.azure.com/your_org"
$Proj = "your_project"
 
if ($Org -notmatch '^https?://dev\.azure\.com/[\w-]+') {
    $Org = "https://dev.azure.com/$Org"
}
 
$AccountInfo = az account show 2>&1
try {
    $AccountInfo = $AccountInfo | ConvertFrom-Json -ErrorAction Stop
} catch {
    az login --allow-no-subscriptions
}
 
$DevOpsExtension = az extension list --query '[?name == ''azure-devops''].name' -o tsv
if ($null -eq $DevOpsExtension) {
    $null = az extension add --name 'azure-devops'
}
 
$Repos = az repos list --organization $Org --project $Proj | ConvertFrom-Json
foreach ($Repo in $Repos) {
    if (-not (Test-Path -Path $Repo.name -PathType Container)) {
        Write-Warning "Cloning $Proj\$($Repo.name)"
        git clone $Repo.webUrl
    }
}

See also: Bash for scripting bulk Git operations. Azure - Azure DevOps CLI for az repos equivalents when working with Azure DevOps instead of GitHub.


Advanced

Reset git history to a single initial commit 🚨

Rewrites the entire branch history to a single commit. Useful for sanitising a repo before making it public. This is destructive and rewrites remote history - only use it on repos where you control all forks or there are none.

Bash
git checkout --orphan temp_branch \
  && git add -A \
  && git commit -m "Initial commit" \
  && git branch -D main \
  && git branch -m main \
  && git push -f origin main

Find which commit introduced a string

Bash
git log -S "function myFunction" --oneline
git log --all --full-history -- "**/my-file.ts"

Cherry-pick a commit from another branch

Bash
git cherry-pick abc1234             # apply a single commit to the current branch
git cherry-pick abc1234^..def5678   # apply a range inclusive of abc1234 through def5678
git cherry-pick --no-commit abc1234 # apply changes without committing

Work on multiple branches at once (git worktree)

A worktree checks another branch out into a separate directory that shares the same .git - no stashing, no second clone. Ideal for reviewing a PR or hotfixing main while a long build or test run holds your main checkout.

Bash
git worktree add ../app-hotfix main      # check out main into a sibling directory
git worktree add -b fix/bug ../app-bug   # create and check out a new branch there
git worktree list                        # show all linked worktrees
git worktree remove ../app-hotfix        # remove when done
git worktree prune                       # clean up stale entries (manually deleted dirs)

Anti-patterns

  • 🚨 git push --force without --lease - can overwrite commits pushed by others since your last fetch. Always use --force-with-lease, which fails if the remote has moved.
  • ⚠️ Amending or rebasing already-pushed commits - rewrites history that others may have fetched; causes divergence and requires force-pushing. Only rewrite commits that have never left your machine.
  • 🚨 Committing secrets or credentials - once pushed, they’re in history forever (and likely cached by forks/mirrors). Use environment variables or a vault, and rotate immediately if leaked.
  • ⚠️ Committing directly to main/master - bypasses review and CI. Use short-lived feature branches and PRs even for solo projects; it preserves a clean audit trail.
  • 🔬 git stash as long-term storage - stashes are local and unnamed by default; easy to lose track of or apply to the wrong branch. Use a WIP branch instead for anything lasting more than a few minutes.
  • 🚨 git clean -fd without -n (dry-run) first - irreversibly deletes untracked files and directories including build artefacts you might want. Always run -n to preview first.
  • 🚨 git reset --hard with uncommitted work - permanently discards all changes with no recovery path. Stash or commit first.
Last updated on