Git: a cheat sheet in questions
A few years ago I ran some Git/GitLab/GitHub workshops for colleagues. I decided to revisit the slide content — which had aged a bit — and reframe it differently.
The post is split into two parts. First, common problems — phrased the way you actually encounter them, ranging from simple to trickier. Then a few thematic references to go deeper on each topic. It’s far from exhaustive, but hopefully you’ll find what you need!
It also serves — above all — as a personal cheat sheet so I don’t have to look up the same commands over and over. That means the content gets regularly updated with new finds, or corrected when needed.
Common problems
Getting started
I have all my work on my hard drive and I want to save it
cd my-project/
git init # Initialise a local repository
git add . # Stage all files
git commit -m "Initial commit" # First snapshot
To push it to a remote server (GitHub, GitLab…):
git remote add origin git@github.com:user/repo.git
git push -u origin main
From here on, each git commit + git push is a timestamped, versioned save.
I want to get a colleague’s code
When you want to grab some code you’re interested in from GitHub, or a colleague’s repo, it couldn’t be simpler.
git clone git@github.com:user/repo.git # Via SSH (recommended)
git clone https://github.com/user/repo.git # Via HTTPS
I don’t know where I stand with my changes
git status # Modified, staged, and untracked files
git diff # Details of unstaged changes
git diff --staged # Details of staged changes
git log --oneline -10 # The last 10 commits
Working alone
Git opened Vim and I don’t know how to get out
Type :q! to quit without saving, or :wq to save and quit.
To avoid this altogether:
git config --global core.editor "nano"
git config --global core.editor "code --wait" # VS Code
(Vim is great, though…)
I forgot a file in my last commit
git add forgotten_file.py
git commit --amend --no-edit # Adds the file without changing the message
WARNING: Never amend a commit that has already been pushed to a shared repository.
I want to fix the message of my last commit
git commit --amend -m "Correct new message"
I committed on main instead of my branch
git branch my-feature # Create the branch with the commit
git reset HEAD~1 --mixed # Undo the commit on main (changes preserved)
git switch my-feature # Switch to the right branch
My history is a mess (10 commits saying “fix”, “wip”, “test”)
Rewrite the last few commits before pushing:
git rebase -i HEAD~10
In the editor: squash to merge, reword to rename, drop to delete.
I want to undo my local changes and go back to the last commit
git restore file.py # Undo changes to a file (irreversible)
git restore . # Undo all local changes (irreversible)
git clean -fd # Also removes untracked files
I did a reset --hard and lost my work
The reflog keeps a history of all actions on HEAD for 90 days.
git reflog # Find the hash of the lost commit
git checkout abc1234 # Go back to it
git branch recovery abc1234 # Turn it into a branch
I want to set my work aside without committing
git stash # Push current changes onto the stash stack
git stash pop # Reapply the last stash
git stash list # List all stashes
Working as a team
My push was rejected
# Typical message: "rejected [...] non-fast-forward"
git pull --rebase # Fetch remote changes and replay yours on top
git push
I want to get my colleagues’ changes without breaking anything
git fetch origin # Fetch without touching local code
git log origin/main --oneline # Inspect what came in
git merge origin/main # Integrate explicitly
Prefer fetch + inspection + explicit merge over a blind git pull.
Two colleagues modified the same file
git status # See files in conflict
# Edit the files: resolve the <<<<, ====, >>>> markers
git add resolved_file.py
git merge --continue
# To abort everything:
git merge --abort
With a visual tool:
git mergetool # Opens the configured tool (meld, VS Code…)
I want to sync my fork with the original repository
git remote add upstream git@github.com:original/repo.git
git fetch upstream
git switch main
git merge upstream/main
git push origin main
My push --force overwrote a colleague’s work
Always use --force-with-lease instead — it checks that nobody has pushed in the meantime.
git push --force-with-lease
I pushed a sensitive file (.env, API key)
Step 1 — Revoke the key immediately. The remote already has a copy.
Step 2 — Remove the file from the entire history:
pip install git-filter-repo
git filter-repo --path .env --invert-paths
git push --force-with-lease
Step 3 — Add the file to .gitignore.
My repository is in a bad state
I want to find out who changed this line
git blame file.py
git blame -L 10,25 file.py # Lines 10 to 25 only
I don’t know which commit introduced this bug
git bisect performs a binary search through history.
git bisect start
git bisect bad # The current commit is broken
git bisect good v1.2.0 # This tag was working
# Git checks out a middle commit → test it, then:
git bisect good # or: git bisect bad
# Repeat until identified. Then:
git bisect reset
With an automated test script:
git bisect run python tests/test_regression.py
My merge is a disaster, I want to undo everything
During the merge (before the merge commit):
git merge --abort
After the merge commit:
git revert -m 1 HEAD # Creates a commit that undoes the merge
My repository has grown huge
Identify large objects in history:
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print $3, $4}' \
| sort -rn \
| head -20
git gc --aggressive --prune=now # Clean up and compress
# For large binary files: Git LFS
git lfs install
git lfs track "*.fits"
git add .gitattributes
I want to go back to a clean version without losing history
git revert abc1234 # Undo a specific commit (creates a new commit)
git revert HEAD~3..HEAD # Undo the last 3 commits
To hard-reset to a past state (destructive — only use when working alone):
git reset --hard abc1234
git push --force-with-lease
Configuration
Setting your identity
git config --global user.name "First Last"
git config --global user.email "you@example.com"
Understanding configuration levels
Git applies configuration in this order (highest to lowest priority):
| Level | File | Scope |
|---|---|---|
--local | .git/config | Current repository only |
--global | ~/.gitconfig | All repositories for the user |
--system | /etc/gitconfig | All users on the machine |
git config --local user.email "pro@lab.fr" # Override for this repo
git config --list --show-origin # See all active values
Ignoring files with .gitignore
# Compiled Python files
__pycache__/
*.pyc
# Virtual environments
.venv/
# Large data files
*.fits
*.hdf5
# Secrets
.env
git check-ignore -v file.txt # Check which rule applies
Setting up aliases
git config --global alias.st "status"
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.undo "reset HEAD~1 --mixed"
Excluding files locally without touching .gitignore
Edit .git/info/exclude — same syntax as .gitignore, not versioned, not shared.
Normalising line endings with .gitattributes
See the section Common problems → My line endings are causing conflicts.
Daily workflow
The basic cycle: modify → stage → commit
git add file.py # Stage a file
git add -p # Stage interactively by chunk
git commit -m "Short, precise description"
Practical rule for messages: complete the sentence “This commit…”.
Create a branch and switch to it
git switch -c my-feature # Create + switch
git switch main # Switch to an existing branch
git branch # List local branches
git branch -d my-feature # Delete a merged branch
Merge a branch
git switch main
git merge my-feature # Fast-forward if possible
git merge --no-ff my-feature # Force a merge commit (keeps the trace)
merge vs rebase: when to use which?
merge | rebase | |
|---|---|---|
| History | Preserved, non-linear | Rewritten, linear |
| Use case | Branch integration | Cleanup before merge |
| Rule | Always fine | Never on a shared public branch |
git switch my-feature
git rebase main # Replay my-feature on top of main
Viewing history efficiently
git log --oneline --graph --all --decorate
git log --author="Greg" --since="2 weeks ago"
git log --grep="fix" --oneline
git log -- src/model.py # History of a file
git log --follow -- old_name.py # Follow renames
Comparing branches or commits
git diff main..my-feature
git log main..my-feature --oneline # Commits in my-feature not yet in main
git diff HEAD~3 HEAD -- file.py
Rewriting several commits (interactive rebase)
git rebase -i HEAD~5
In the editor: pick, squash, reword, drop, edit.
Collaboration / Remotes
Adding and managing a remote
git remote add origin git@github.com:user/repo.git
git remote -v
git remote set-url origin git@github.com:user/new-repo.git
Pushing a branch
git push -u origin my-feature # First time (sets up tracking)
git push # Subsequent pushes
git push origin --delete my-feature # Delete the remote branch
fetch vs pull: what’s the difference?
-
git fetch: fetches remote changes without integrating them. Safe at any time. -
git pull:fetch+merge(orrebase). Modifies the current branch.
git fetch origin
git log origin/main --oneline # Inspect before integrating
git merge origin/main
Tagging a version
git tag -a v1.0.0 -m "Version 1.0.0"
git push origin v1.0.0
git push origin --tags
git tag -d v1.0.0 # Delete locally
git push origin --delete v1.0.0 # Delete on the remote
Is there a convention for writing good commit messages?
Yes. The most widely adopted one is called Conventional Commits (conventionalcommits.org).
Format:
<type>(<scope>): <short description>
[optional body]
[optional footer]
Standard types:
| Type | Use |
|---|---|
feat | New feature |
fix | Bug fix |
docs | Documentation only |
style | Formatting, no logic change |
refactor | Neither feat nor fix — restructuring |
test | Adding or fixing tests |
chore | Maintenance, dependencies, CI |
perf | Performance improvement |
Examples:
feat(model): add KL regularization to VAE encoder
fix(nms): move reshape inside conditional block
docs(api): update OTFClient usage examples
chore(deps): bump torch from 2.1 to 2.3
Core rules (Tim Pope, 2008 — still the reference):
- Subject line: 50 characters max, no trailing period
- If body: blank line between subject and body
- Body: 72 characters per line, explains the why, not the what
- Use the imperative: Add feature, not Added feature
Why it’s useful: Conventional Commits allows you to automatically generate a changelog and drive semantic versioning (SemVer):
# With semantic-release or standard-version
feat → minor version bump (1.0.0 → 1.1.0)
fix → patch version bump (1.0.0 → 1.0.1)
feat! → major version bump (1.0.0 → 2.0.0) # breaking change
For a solo project or small team, the full convention is often overkill. The useful minimum is just the type prefix:
feat: add support for 512x512 tiles
fix: correct reshape outside conditional block
refactor: extract OTFClient into a separate module
docs: add LaTeX spec for the protocol
Even without tooling, it makes git log --oneline immediately readable.
Debugging & Inspection
Display the contents of a commit
git show abc1234
git show HEAD
git show HEAD~2
List tracked files
git ls-files
git ls-files --others --exclude-standard # Untracked files
Search in code and history
git grep "function_name" # In current code
git log -S "function_name" --oneline # Commits that changed this string
git log -G "regex" --oneline # Same with a regex
Contribution statistics
git shortlog -sn # Number of commits per author
git shortlog -sn --since="1 year"
Recovering a lost commit (reflog)
git reflog # History of all HEAD actions
git checkout abc1234
git branch recovery abc1234
Finding the commit that introduced a bug (bisect)
See the section Common problems → I don’t know which commit introduced this bug.
Checking and optimising the repository
git fsck --full # Check integrity
git count-objects -vH # Repository size
git gc --aggressive # Clean up and compress
Inspecting Git’s internal objects
git cat-file -t abc1234 # Type: blob, tree, commit, tag
git cat-file -p abc1234 # Object contents
git ls-tree HEAD # Tree of the current commit
References
- Official Git documentation: git-scm.com/doc
- Pro Git (Chacon & Straub, 2nd ed.) — free online: git-scm.com/book/en/v2
- Version Control with Git: Powerful Tools and Techniques for Collaborative Software Development, Prem Kumar Ponuthorai, O’Reilly, 2022
-
man git-<command>— built-in manual pages