Version Control and Git
Commands Cheat Sheet
Git Commands Cheat Sheet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# init local repo and push to GitHub
git init
git branch -M main
git add .
git commit -m "Initial commit"
git remote add origin <url>
git push -u origin main
# clone existing GitHub repo
git clone <url>
# status / history
git status
git log --all --graph --decorate --oneline
git graph
git show <commit> # show one commit
git reflog # recover lost commits / branch moves
# inspect changes
git diff # unstaged changes
git diff --staged # staged changes
git diff HEAD # all changes vs current commit
# staging / commit
git add file.cpp
git add .
git add -p # interactively stage chunks
git restore --staged file.cpp # unstage, keep working directory changes
git commit -m "Message"
git commit -am "Message" # add + commit tracked files only
git commit --amend # replace last commit
# discard / restore changes
git restore file.cpp # discard working directory changes in file
git restore . # discard all tracked working directory changes
git clean -fd # delete untracked files/directories; dangerous
# branch / switch
git branch # view local branches
git branch -vv # view branches with upstream info
git branch feature # create branch, do not switch
git switch feature # switch to branch
git switch -c feature # create and switch
git branch -d feature # delete merged branch
git branch -D feature # force delete branch
# detached HEAD
git switch --detach <commit> # detach HEAD at commit
git switch -c new-branch <commit> # create new branch at commit and switch
# checkout legacy
git checkout main # switch to branch
git checkout -b feature # create and switch
git checkout <commit> # detached HEAD
git checkout -- file.cpp # restore file
# objects
git cat-file -t <hash> # type: blob, tree, commit, tag
git cat-file -p <hash> # pretty-print content
# remotes
git remote -v # view remotes
git remote add origin <url> # add remote named origin
git fetch origin # fetch updates from remote, don't touch files
git pull --ff-only # pull only if fast-forward is possible
git push # push current branch to upstream
git push -u origin main # push main and set upstream
git push -u origin HEAD # push current branch and set upstream
# merge / rebase
git merge main # merge main into current branch
git rebase main # replay current branch on top of main
# stash
git stash push -u -m "wip" # stash changes, including untracked files
git stash list # view stashes
git stash pop # apply and remove from stash list
git stash apply # apply but keep in stash list
git stash show -p stash@{0} # inspect stash content
# reset
git reset --soft <commit> # move branch, keep changes staged
git reset --mixed <commit> # move branch, keep changes unstaged
git reset --hard <commit> # move branch and discard changes; dangerous
git reset --hard origin/main # force local branch/files to match remote
# ignore debugging
git status --ignored # view ignored files
git check-ignore -v path/to/file # debug why a file is ignored
git add -f path/to/file # force add an ignored file
Quick Mental Models
git add = put changes into staging area
git commit = snapshot staging area
git branch = create/view/delete branch pointers
git switch = move HEAD to another branch
git fetch = download remote refs, don't touch working files
git pull = fetch + merge/rebase
git push = upload local commits
git stash = temporarily save current edits
git reset --hard = force branch + index + files to match a commit
Dangerous Commands
1
2
3
4
git reset --hard
git clean -fd
git branch -D
git push --force
Use safer force push:
1
git push --force-with-lease
Things I Might Forget
git commitcommits the staging area, not necessarily all modified files.git branch featurecreates a branch but does not switch to it.git switch featureswitches to a branch.git switch <commit>fails; usegit switch --detach <commit>.git checkout <commit>enters detached HEAD.- Detached HEAD still lets me edit files; the problem only happens if I commit without a branch.
git remote add origin URLdoes not change the current branch.git pushpushes committed changes, not uncommitted working directory changes.git pushusually pushes the current branch to its upstream, not all branches.git fetchis safe: it downloads remote updates but does not change working files.git pull --ff-onlyrefuses non-fast-forward merges.git reset --hard origin/mainforces local branch and files to match the remote.git stash push -ualso saves untracked files.- If
.gitignorebehaves weirdly, usegit check-ignore -v.
Why Version Control?
A version control system keeps project history so we can view old snapshots, know who changed what, work on branches, collaborate, and roll back when something breaks.
Key idea: do not learn Git as magic commands. Think of Git as a data structure:
1
Git = object database + commit graph + references
Git’s Data Model
Git stores history as snapshots, not mainly as line-by-line diffs.
1
history = series of snapshots
Core objects:
1
2
3
blob = file content
tree = directory mapping names to object hashes
commit = root tree hash + parent commit hash(es) + metadata
A blob stores bytes, not the file name. The file name lives in a tree.
1
2
3
4
5
6
7
8
9
10
11
root/
|-- foo/
| `-- bar.txt
`-- baz.txt
tree_root:
foo -> hash(tree_foo)
baz.txt -> hash(blob_baz)
tree_foo:
bar.txt -> hash(blob_bar)
A commit points to the root tree and its parent commit(s):
1
2
3
4
5
6
commit:
tree = hash(root tree)
parent(s) = hash(parent commits)
author
message
timestamp
Commit Graph
Git history is a DAG: Directed Acyclic Graph. Each commit points back to its parent(s).
Straight history:
1
A <- B <- C <- D
Branch:
1
2
A <- B <- C <- D
E <- F
Merge:
1
2
3
A <- B <- C <- D <- M
\ /
E <- F
Merge commit M has two parents:
1
2
parent 1 = D
parent 2 = F
Immutability and Content Addressing
Commits are immutable. Editing history creates new commits and moves references.
1
git commit --amend
1
2
Before: A <- B <- C
After: A <- B <- C'
Git stores objects by hash:
1
hash(content) -> content
If one byte changes, the hash changes. Since commits contain tree hashes and trees contain blob hashes:
1
blob changes -> tree changes -> commit changes
This is why commits are immutable.
Repository, References, and HEAD
A repository is mostly:
1
2
3
.git/
|-- objects/
`-- refs/
References are human-readable names pointing to commit hashes.
1
2
3
main -> abc123
feature -> def456
HEAD -> main
A branch is a mutable pointer to a commit. Commits are immutable; references move.
1
2
Before commit: A <- B main -> B
After commit: A <- B <- C main -> C
HEAD means where I currently am.
Normal HEAD:
1
HEAD -> main -> commit C
Detached HEAD:
1
HEAD -> commit
Detached HEAD is not read-only. We can edit files, but a new commit is not held by a branch unless we create one:
1
2
git switch --detach <commit>
git switch -c my-work
Staging Area
Git has three important areas:
1
2
3
HEAD = current committed snapshot
staging area = snapshot prepared for the next commit
working directory = real files currently being edited
Flow:
1
working directory --git add--> staging area --git commit--> commit
git commit commits the staging area, not necessarily the whole working directory.
1
2
3
git add main.cpp
git commit -m "Fix parser bug"
git add -p # interactively stage chunks
The staging area helps create clean commits, especially when one file contains both real fixes and temporary debug prints.
Basic Commands
1
2
3
4
5
6
7
git init # create .git/
git status # branch, modified, staged, untracked files
git add file.cpp # stage file
git add . # stage many changes
git commit -m "Message" # commit staged snapshot
git log # history
git log --all --graph --decorate --oneline
Default branch can be main or master, depending on config.
1
2
git branch -M main
git config --global init.defaultBranch main
Useful alias:
1
2
git config --global alias.graph "log --all --graph --decorate --oneline"
git graph
Inspecting Objects
git cat-file inspects Git’s object database.
1
2
git cat-file -t <object-id> # type: blob, tree, commit, tag
git cat-file -p <object-id> # pretty-print object content
Examples:
1
2
3
4
git cat-file -t HEAD
git cat-file -p HEAD
git cat-file -p <tree-hash>
git cat-file -p <blob-hash>
Commit output looks like:
1
2
3
4
5
6
tree abc123
parent def456
author ...
committer ...
Commit message
Tree output looks like:
1
2
100644 blob <hash> README.md
040000 tree <hash> src
Common modes:
1
2
3
100644 = normal file
100755 = executable file
040000 = directory/tree
Branch, Switch, Checkout
git branch manages branch pointers.
1
2
3
4
git branch # view branches
git branch feature # create branch, do not switch
git branch -d feature # delete branch
git branch -M main # rename current branch
git switch switches branches.
1
2
3
git switch main
git switch -c feature # create and switch
git switch --detach <commit> # detach HEAD at commit
git switch <commit> fails because switch expects a branch by default.
git checkout is the older command that does many things:
1
2
3
4
5
git checkout main
git checkout -b feature
git checkout <commit> # detached HEAD
git checkout -- file.cpp # restore file
git checkout <commit> -- file.cpp
Modern mapping:
1
2
3
4
5
git checkout main -> git switch main
git checkout -b feature -> git switch -c feature
git checkout -- file.cpp -> git restore file.cpp
git checkout <commit> -- file -> git restore --source <commit> file
git checkout <commit> -> git switch --detach <commit>
Prefer switch and restore when possible because they are clearer.
Remotes, Push, and Upstream
A remote is another repository, usually GitHub.
1
2
git remote add origin <url>
git remote -v
origin is just a nickname for a remote URL. Adding it does not change the current branch or modify main.
1
2
origin https://github.com/user/repo.git (fetch)
origin https://github.com/user/repo.git (push)
git push sends committed changes to a remote. It does not push uncommitted working directory edits.
1
2
3
git add .
git commit -m "..."
git push
Usually git push pushes the current branch to its upstream, not all branches.
1
2
3
git push origin main # push specific branch
git push -u origin fix-crawler # push new branch and set upstream
git push --all origin # push all branches; do not use casually
An upstream is the remote branch tracked by a local branch.
1
local main tracks origin/main
1
2
git push -u origin main # -u = --set-upstream
git branch -vv # show upstreams
After upstream is set, Git knows what git push and git pull mean for that branch.
Fetch, Pull, and Reset
git fetch downloads new commits/references and updates remote-tracking branches, but does not change local files.
1
git fetch origin
1
2
3
4
5
6
7
8
9
Before:
local main: A <- B
origin/main: A <- B
Remote has C.
After fetch:
local main: A <- B
origin/main: A <- B <- C
git pull is roughly:
1
2
git fetch
git merge
git pull --ff-only only updates if a fast-forward is possible.
1
git pull --ff-only
Fast-forward:
1
2
3
4
local main: A <- B
origin/main: A <- B <- C
After: main: A <- B <- C
Diverged history:
1
2
local main: A <- B <- D
origin/main: A <- B <- C
In this case, --ff-only fails instead of creating an unexpected merge commit.
git reset --hard forces the current branch, staging area, and working directory to match a commit.
1
git reset --hard origin/main
1
2
3
current branch -> origin/main
staging area -> origin/main
working dir -> origin/main
Danger: unstashed/uncommitted work is lost. Unique local commits can become unreachable from the branch.
Stash
git stash temporarily saves current changes and makes the working directory clean.
1
2
3
4
git stash push -u -m "wip" # -u includes untracked files
git stash list
git stash pop # apply and remove from stash list
git stash apply # apply but keep in stash list
Mental model:
1
stash = temporary hidden commit in a drawer
Jump to Latest Main but Keep Current Edits
Goal:
1
2
jump to the latest main
but keep my current file changes
Force local main to match GitHub:
1
2
3
4
5
git stash push -u -m "wip before updating main"
git fetch origin
git switch main
git reset --hard origin/main
git stash pop
Safer version that does not delete local commits on main:
1
2
3
4
git stash push -u -m "wip before updating main"
git switch main
git pull --ff-only
git stash pop
Use pull --ff-only for safety. Use reset --hard origin/main only when local main should exactly match GitHub.
.gitignore
.gitignore ignores intentionally untracked files.
*.csv
__pycache__/
.env
Debug ignored files:
1
2
3
4
git check-ignore -v path/to/file
git status --ignored
git config --get core.excludesfile
git add -f path/to/file
Ignore rules can come from:
1
2
3
4
.gitignore
folder/.gitignore
.git/info/exclude
global gitignore
Usually run git check-ignore -v before force-adding an ignored file.
Common Workflows
Create a new repo and push to GitHub:
1
2
3
4
5
6
git init
git branch -M main
git add .
git commit -m "Initial commit"
git remote add origin <url>
git push -u origin main
Create a new branch and work there:
1
2
3
4
5
git switch -c fix-crawler
# edit files
git add .
git commit -m "Fix crawler"
git push -u origin fix-crawler
Switch branch but keep modifications:
1
git switch main
If Git refuses because changes would be overwritten:
1
2
3
git stash push -u
git switch main
git stash pop
Discard uncommitted changes:
1
2
3
4
5
git restore file.cpp # one file
git restore . # all tracked changes
git clean -fd # untracked files too; deletes files
git restore --staged file.cpp
git reset file.cpp # older style unstage
Mental Models
1
2
3
4
5
6
7
8
9
10
11
12
13
14
repo = objects + references
commit = root tree hash + parent hashes + metadata
branch = mutable pointer to commit
HEAD = where I currently am
normal HEAD = HEAD -> branch -> commit
detached HEAD = HEAD -> commit
commit creation = working directory -> staging area -> commit
origin = nickname for remote repository URL
origin/main = remote-tracking branch
upstream = remote branch tracked by local branch
fetch = download remote updates, do not touch my files
pull = fetch + merge/rebase into current branch
reset --hard H = make current branch + index + working directory exactly H
stash = temporarily save current edits somewhere else