🌿 git Quick Reference

git cheat sheet / survival guide

Video: git Quick Reference (DevOps video)
Prefer to watch? This short video runs this page's main commands in a real lab, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
git <command> [options]
git status

Git is a distributed version control system: it records snapshots of your project so you can see what changed,
go back in time, work on ideas in branches, and collaborate through remote copies (GitHub, GitLab, your own server).
Every clone holds the full history. Most commands work offline. The mental model: your files (working tree)
> the staging area (index) > commits (history) > a remote copy. Commands move changes between those places.

Install and first-time setup

sudo apt install git                        # Debian/Ubuntu       sudo dnf install git     Fedora/RHEL family
brew install git                            # macOS               winget install Git.Git   Windows
git --version
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main         # new repos start on "main"
git config --global core.editor "vim"               # or nano, "code --wait"
git config --global pull.rebase false               # "git pull" merges (use true to rebase instead)
git config --global push.autoSetupRemote true       # first "git push" sets the upstream for you (Git 2.37+)
git config --global fetch.prune true                # drop remote branches that were deleted on the server
git config --global merge.conflictstyle zdiff3      # clearer conflict markers (Git 2.35+)
git config --global rerere.enabled true             # remember how you resolved a conflict and reuse it
git config --list --show-origin                     # see every setting and where it came from
Config levels: --system (whole machine), --global (~/.gitconfig, you), --local (this repository, the default when you leave the flag off).

Start a repository

git init                                    # turn the current folder into a repository
git init -b main                            # ...with the branch named main
git clone https://github.com/user/repo.git  # copy a remote repository
git clone git@github.com:user/repo.git      # over SSH
git clone --depth 1 URL                     # shallow clone: only the latest snapshot (fast, small)
git clone --branch dev URL mydir            # a specific branch, into a named folder
git clone --recurse-submodules URL          # also fetch submodules
git clone --filter=blob:none URL            # partial clone: fetch file contents on demand

The everyday loop

git status                                  # what changed?      git status -sb    short form with the branch
git add file.txt                            # stage one file
git add src/ docs/                          # stage folders
git add -p                                  # stage piece by piece (review each hunk)
git add -A                                  # stage everything (new, changed, deleted). Check status first
git commit -m "Add login form"              # save a snapshot with a message
git commit                                  # opens your editor for a longer message
git commit -am "Fix typo"                   # stage tracked files and commit (does not add NEW files)
git commit --amend                          # change the last commit (message or content). Do not amend pushed commits
git commit --amend --no-edit                # add staged changes to the last commit, keep the message
git push                                    # send commits to the remote
git pull                                    # fetch and merge remote changes
Write good messages: a short summary (about 50 characters, imperative: "Fix crash on empty input"),
a blank line, then the why. One logical change per commit.

Look at changes and history

git diff                                    # unstaged changes
git diff --staged                           # staged changes (what the next commit will contain)
git diff HEAD                               # everything since the last commit
git diff main..feature                      # differences between two branches
git diff main...feature                     # changes on feature since it split from main
git diff --stat                             # summary of files and line counts
git diff --name-only                        # just the file names
git log                                     # full history
git log --oneline                           # one line per commit
git log --oneline --graph --decorate --all  # a picture of all branches
git log -p                                  # with the changes in each commit
git log -5                                  # last five commits
git log --stat                              # files changed per commit
git log --author="Alice"                    git log --since="2 weeks ago"        git log --until="2026-01-01"
git log --grep="login"                      # search commit messages
git log -S"apiKey"                          # commits that added or removed this text (the "pickaxe")
git log -- path/to/file                     # history of one file       git log --follow -- file    follow renames
git log main..feature                       # commits on feature that main does not have
git show                                    # the last commit           git show abc123    a specific commit
git show HEAD~2:src/app.py                  # a file as it was two commits ago
git blame file.py                           # who last changed each line     git blame -L 10,20 file.py    lines 10 to 20
git shortlog -sn                            # commit counts per author
git reflog                                  # every place HEAD has been (your safety net, see Undo)

Branches

git branch                                  # list local branches (* marks the current one)
git branch -a                               # include remote branches       git branch -vv    show tracking and last commit
git branch feature                          # create a branch (does not switch to it)
git switch feature                          # move to a branch (older: git checkout feature)
git switch -c feature                       # create and switch (older: git checkout -b feature)
git switch -                                # jump back to the previous branch
git branch -m new-name                      # rename the current branch
git branch -d feature                       # delete a merged branch          git branch -D feature    force delete
git branch --merged                         # branches already merged into the current one
git branch --no-merged
git push -u origin feature                  # publish a branch and set its upstream
git push origin --delete feature            # delete a remote branch
git remote prune origin                     # forget remote branches that no longer exist
HEAD is "where you are". A branch is just a movable label on a commit.

Merge and rebase

git merge feature                           # merge feature into the current branch
git merge --no-ff feature                   # always create a merge commit
git merge --squash feature                  # combine all of feature into one staged change, then commit it yourself
git merge --abort                           # stop a merge that has conflicts
git rebase main                             # replay your commits on top of main (linear history)
git rebase -i HEAD~4                        # interactive: reorder, squash, reword, drop the last 4 commits
git rebase --continue                       # after fixing a conflict          git rebase --abort    give up
git rebase --onto main old-base feature     # move a branch from one base to another
Golden rule of rebasing: never rebase or force-push commits other people already have.
Merge keeps the true history. Rebase gives a clean, straight line. Pick one team habit and stick to it.

Fix a merge conflict

1. git status                               # shows the files marked "both modified"
2. Open each file and look for the markers:
     <<<<<<< HEAD          your version
     =======
     >>>>>>> feature       their version
   Edit the file to what it should be and DELETE all three marker lines
3. git add path/to/fixed-file
4. git commit                               # (or git rebase --continue during a rebase)
Useful: git checkout --ours file / --theirs file    take one side entirely (careful: in a rebase, ours and theirs swap)
        git mergetool                              open a visual merge tool
        git diff --name-only --diff-filter=U       list files still in conflict

Remotes

git remote -v                               # list remotes
git remote add origin git@github.com:user/repo.git
git remote set-url origin NEWURL            # change a remote's address
git remote rename origin upstream
git fetch                                   # download new commits, do NOT change your files
git fetch --all --prune                     # every remote, and drop deleted branches
git pull                                    # fetch + merge          git pull --rebase    fetch + rebase
git pull --ff-only                          # only fast-forward: refuse to create a merge commit
git push                                    # send the current branch
git push -u origin main                     # first push: set the upstream
git push --force-with-lease                 # a safer force push: fails if someone else pushed in the meantime
git push origin --tags                      # push all tags
Forks: keep the original as "upstream":  git remote add upstream URL   then   git fetch upstream && git merge upstream/main

Undo things (from safest to scariest)

git restore file.txt                        # throw away unstaged changes in a file (cannot be undone!)
git restore --staged file.txt               # unstage a file, keep the changes
git restore --source=HEAD~2 file.txt       # bring back a file as it was two commits ago
git commit --amend                          # fix the last commit (only before you push it)
git revert abc123                           # make a NEW commit that undoes commit abc123 (safe for shared history)
git revert -m 1 MERGE_SHA                   # undo a merge commit
git reset --soft HEAD~1                     # undo the last commit, keep changes STAGED
git reset HEAD~1                            # undo the last commit, keep changes in your files (unstaged) (--mixed is the default)
git reset --hard HEAD~1                     # undo the last commit AND delete the changes (dangerous)
git reset --hard origin/main                # make your branch exactly match the remote (drops local work)
git clean -n                                # DRY RUN: list untracked files that would be deleted
git clean -fd                               # delete untracked files and folders (cannot be undone)
git stash                                   # shelve changes to get a clean tree (see Stash)
Rescue with reflog: nearly nothing is truly lost for a while.
git reflog                                  # find the commit you were on before the mistake, for example HEAD@{3}
git reset --hard HEAD@{3}                   # or:  git switch -c rescue abc123    to keep it on a new branch
Rule: use revert for commits that are already shared, and reset only for local work.

Stash (a shelf for unfinished work)

git stash                                   # save tracked changes and clean the tree
git stash push -m "wip login"               # with a name
git stash -u                                # include untracked files
git stash list                              # see the shelf
git stash show -p stash@{0}                 # view a stash as a patch
git stash pop                               # re-apply the latest stash and remove it
git stash apply stash@{1}                   # re-apply but keep it on the shelf
git stash branch newbranch                  # turn a stash into a branch
git stash drop stash@{0}                    git stash clear    # delete one / all (careful)

Tags and releases

git tag                                     # list tags
git tag v1.0.0                              # lightweight tag on the current commit
git tag -a v1.0.0 -m "Release 1.0.0"        # annotated tag (recommended for releases)
git tag -a v0.9.0 abc123                    # tag an older commit
git push origin v1.0.0                      # push one tag         git push origin --tags   all tags
git tag -d v1.0.0                           # delete locally       git push origin --delete v1.0.0    delete on the remote
git describe --tags                         # nearest tag, for example v1.0.0-5-gabc1234

Find things and fix bugs

git grep "TODO"                             # search the files in the repository (fast)
git grep -n -i "password" -- '*.py'
git log -S"function_name" --oneline         # when did this text appear or disappear?
git bisect start                            # binary-search for the commit that broke something
git bisect bad                              # the current commit is broken
git bisect good v1.0.0                      # this older one worked
#   Git checks out a middle commit. Test it, then run: git bisect good   or   git bisect bad   (repeat)
git bisect run ./test.sh                    # automate it: the script exits 0 for good, non-zero for bad
git bisect reset                            # finish and return to where you were
git blame -w -C file.py                     # ignore whitespace, detect moved lines
git cherry-pick abc123                      # copy one commit onto the current branch
git cherry-pick A..B                        # a range
git cherry-pick --abort

.gitignore (files Git should never track)
# .gitignore
node_modules/
__pycache__/
*.log
.env
build/
!important.log                              # ! re-includes a file
Patterns: *.log any log file, dir/ a folder, /todo.txt only in the repo root, **/temp anywhere.
git rm --cached file                        # stop tracking a file but keep it on disk (then add it to .gitignore)
git rm -r --cached .                        # re-apply .gitignore to everything, then git add . and commit
git check-ignore -v path/to/file            # which rule ignores this file?
git status --ignored                        # show ignored files
Global ignore for your editor and OS files:  git config --global core.excludesfile ~/.gitignore_global
Never commit secrets: .env files, private keys, tokens and passwords.

Move, rename, delete

git mv old.txt new.txt                      # rename and stage it
git rm file.txt                             # delete and stage the deletion
git rm -r folder/
git restore --staged --worktree file        # get a deleted file back before committing

Worktrees and submodules

git worktree add ../hotfix hotfix-branch    # a second folder with another branch checked out (no need to stash)
git worktree list                           git worktree remove ../hotfix
git submodule add URL path/to/sub           # add another repository inside this one
git submodule update --init --recursive     # fetch submodules after a clone
git submodule update --remote               # move submodules to their latest commit
git submodule foreach 'git pull'

Authentication (SSH and tokens)

ssh-keygen -t ed25519 -C "you@example.com"  # create a key (press Enter for the default location)
cat ~/.ssh/id_ed25519.pub                   # copy this into GitHub/GitLab: Settings > SSH keys
ssh -T git@github.com                       # test the connection
git remote set-url origin git@github.com:user/repo.git      # switch a repo from HTTPS to SSH
HTTPS with a token: create a personal access token in your account and use it as the password.
git config --global credential.helper cache             # remember it in memory for a while
git config --global credential.helper store             # save in plain text (avoid on shared machines)
Git Credential Manager (Windows/macOS) and libsecret (Linux) store credentials securely.
GitHub CLI:  gh auth login    gh repo clone user/repo    gh pr create    gh pr list    gh pr checkout 42

Aliases (shortcuts)

git config --global alias.st "status -sb"
git config --global alias.co "switch"
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.unstage "restore --staged"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.amend "commit --amend --no-edit"
Then use:  git st    git lg    git last

Hooks (run scripts automatically)

.git/hooks/pre-commit                       # runs before each commit (chmod +x). Exit non-zero to block the commit
.git/hooks/commit-msg                       # check the commit message
.git/hooks/pre-push                         # run tests before pushing
git config core.hooksPath .githooks         # keep hooks in the repo so the team shares them
Frameworks such as pre-commit (pre-commit.com) manage hooks for linting, formatting and secret scanning.

Common team workflows

Feature branch    main is always deployable. Work on a branch, open a pull/merge request, review, merge, delete the branch
                  git switch -c feature/login   ...commits...   git push -u origin feature/login   (open the PR)
Trunk-based       Very short-lived branches, small changes merged to main daily, behind feature flags
Git Flow          main + develop + feature/release/hotfix branches. Heavier: good for versioned releases
Forking           Fork the project, branch in your fork, PR back to the original ("upstream")
Keep your branch up to date:  git fetch origin && git rebase origin/main     (or git merge origin/main)
Before opening a PR:  git status, git diff origin/main, run the tests, and rewrite messy history with git rebase -i

Commit message conventions

Conventional Commits:  type(scope): summary      feat(auth): add password reset      fix(api): handle empty body      docs: update README
Common types: feat, fix, docs, style, refactor, perf, test, build, ci, chore. Add "!" or a BREAKING CHANGE: footer for breaking changes.
Reference issues:      Fixes #123     (closes the issue when the PR merges on GitHub and GitLab)

Big files and cleanup

git lfs install                             # Git Large File Storage: keep big binaries out of normal history
git lfs track "*.psd"                       # then commit .gitattributes
git count-objects -vH                       # repository size
git gc --aggressive --prune=now             # repack and clean up (rarely needed)
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | sort -k3 -n -r | head    # biggest objects
GitHub rejects single files over 100 MB. Do not commit build output, videos, databases or dependencies.

Removing a secret that was committed

1. Assume it is compromised. REVOKE and ROTATE the key or password first. This matters more than any cleanup
2. Remove it from the current files and commit
3. Rewrite history to remove it from old commits, using git filter-repo (a separate tool: pip install git-filter-repo):
   git filter-repo --invert-paths --path secrets.txt          # or use --replace-text for a string
   (BFG Repo-Cleaner is an alternative)
4. Force-push all branches and tags, and tell everyone to re-clone. Old clones and forks may still hold the secret
Rewriting history breaks other people's copies, and does not un-leak a secret that was already public.

Practical one-liners

# Undo the last commit but keep the work
git reset --soft HEAD~1

# I committed on the wrong branch (not pushed yet): move the commit to a new branch
git branch feature-x; git reset --hard HEAD~1; git switch feature-x

# Change the last commit message
git commit --amend -m "Better message"

# Discard ALL local changes and match the remote
git fetch origin && git reset --hard origin/main

# Show what you would push
git log origin/main..HEAD --oneline

# List branches sorted by most recent commit
git for-each-ref --sort=-committerdate refs/heads --format='%(committerdate:short) %(refname:short)'

# Delete all local branches already merged into main
git branch --merged main | grep -v '^\*\|main' | xargs -r git branch -d

# Squash the last 3 commits into one
git reset --soft HEAD~3 && git commit -m "One combined commit"

# See who changed a file and why
git log --follow -p -- path/to/file

# Count commits per author this year
git shortlog -sn --since="1 January 2026"

# Find the commit that deleted a file
git log --diff-filter=D --summary -- path/to/file

# Save a patch and apply it elsewhere
git format-patch -1 HEAD        then      git am 0001-*.patch

# Export a clean copy of the project (no .git folder)
git archive --format=zip -o project.zip HEAD

# Search every commit for a word (slow on big repos)
git grep "search term" $(git rev-list --all)

# What is on disk that git does not track?
git status --ignored --short

Common problems

"fatal: not a git repository"                    You are not inside a repository. cd into it, or run git init
"Please tell me who you are"                     Set user.name and user.email (see setup)
"detached HEAD"                                  You checked out a commit, not a branch. Keep work with:  git switch -c my-branch
"rejected ... non-fast-forward" / "fetch first"  The remote has commits you do not. git pull --rebase (or git pull), then push again
"Your local changes would be overwritten"        Commit or stash first, then pull or switch
"CONFLICT (content): Merge conflict in ..."      Edit the file, remove the markers, git add, then commit or rebase --continue
"refusing to merge unrelated histories"          Two repos with no common ancestor. If sure:  git pull --allow-unrelated-histories
"fatal: Unable to create '.git/index.lock'"      Another git process died. Make sure none is running, then delete .git/index.lock
"Permission denied (publickey)"                  SSH key not added to your account or the agent: ssh-add ~/.ssh/id_ed25519, and test ssh -T
"Authentication failed" over HTTPS               Passwords no longer work on most hosts: use a personal access token or a credential manager
Whole file shows as changed (line endings)       CRLF vs LF. Set core.autocrlf (true on Windows, input on Linux/macOS) or use .gitattributes
Mode changes on every file (chmod)               git config core.fileMode false (on filesystems that do not keep permissions)
File is still tracked after adding to .gitignore  git rm --cached file, then commit
"pathspec did not match any file(s)"             Typo, wrong folder, or the file is not tracked
Pushed the wrong thing / a secret                See "Removing a secret" above, and rotate the credential immediately
Lost commits after a reset or rebase             git reflog, then git reset --hard HEAD@{n} or git switch -c rescue SHA
Repository is huge or slow                       Look for big files (see Big files), use --depth or partial clones, and git gc

Tips

    Commit small and often, with clear messages. Small commits are easy to review, revert and bisect.

    Run git status before every add, commit and push. Run git diff --staged before you commit.

    Never work directly on main for shared projects. Use a branch and a pull request.

    Use git switch and git restore (clearer than the old git checkout). Use git revert for shared history, and reset only for local work.

    Use git push --force-with-lease, never plain --force, and never force-push shared branches.

    Before anything risky (rebase, reset --hard, clean -fd), make a safety branch:  git branch backup-before-rebase

    git reflog is your undo history. Almost nothing is lost for a few weeks.

    Keep secrets out of the repository with .gitignore, environment variables and secret scanning. If one leaks, rotate it first.

    Pull or rebase often so your branch does not drift, and conflicts stay small.

    Add a .gitignore and a README at the start of every project, and use aliases for the commands you type all day.