Git Fundamentals
Git is a distributed version control system. Every clone is a full copy of the history. Everyone commits locally first; sharing is a separate step. Understanding Git’s mental model is worth more than memorising commands.
What problem Git solves
Two problems, really:
- History — “what did this file look like last Tuesday? why was this line added? who changed it?”
- Collaboration — “five people are editing the same code; how do we avoid stomping on each other?”
Git answers both by tracking every change as an immutable snapshot, each with a parent, forming a graph of history.
The mental model
A commit is a snapshot, not a diff
A common misconception: “a commit stores the changes I made.” Actually, a commit stores a complete snapshot of every tracked file at that moment, along with:
- A parent commit (what came before)
- The author, committer, timestamp
- A message
- A unique 40-character SHA-1 hash
When you look at “the diff for this commit,” Git is computing snapshot_N - snapshot_N-1 on the fly.
Three places your work lives
Working directory → Staging area (index) → Local repository (.git)
(your files) (what you (committed history)
`git add`ed)
- Working directory — the files on disk. Editable.
- Staging area — a list of changes you’re preparing to commit.
git addmoves things here. - Local repository — committed history.
git committurns what’s staged into a new commit.
Then there’s a fourth place: remote (GitHub, GitLab, etc.), which git push and git pull sync with.
Branches are just pointers
A branch is a movable label that points to a commit. That’s it. main, develop, feature/x — all the same kind of thing. Creating a branch is O(1); switching is fast. This is why Git encourages many short-lived branches.
feature/x ←── your current work
↓
A ── B ── C ── D
↑
main
When you git commit, the branch pointer moves forward to the new commit.
Commands you actually use
Setup
git init # start a repo here
git clone <url> # copy a remote repo locally
git config --global user.name "..." # first-time setup
git config --global user.email "..."Daily loop
git status # what's changed?
git diff # show the changes
git add <file> # stage a file for commit
git add -A # stage everything
git commit -m "fix: thing" # create a commit
git log # see history
git log --oneline --graph # compact graph viewBranching
git branch # list branches
git checkout -b feature/x # create and switch to new branch
git switch feature/x # (newer, clearer) switch to existing
git merge feature/x # merge feature/x into current branch
git branch -d feature/x # delete local branchRemote
git remote -v # show configured remotes
git push # send commits to remote
git push -u origin feature/x # first push of a new branch
git pull # fetch + merge from remote
git fetch # just fetch, don't mergeThe critical distinction: merge vs rebase
You’ve been working on feature/x for two days. Meanwhile, main has moved forward. Before merging back, you need to integrate the new main changes. Two options:
Merge (git merge main)
Creates a merge commit that has two parents — yours and main. History becomes a graph.
A ── B ── C ── D ── M (main with merge commit M)
\ /
E ── F ── (feature/x)
- Preserves actual history
- Easy, safe, reversible
- Can make
git lognoisy on busy repos
Rebase (git rebase main)
Rewrites your commits on top of the new main, as if you’d started from there.
A ── B ── C ── D ── E' ── F' (clean linear history)
- Clean, linear history
- But: you rewrote commits. If they were already pushed, other collaborators see different history → conflicts.
- Rule: never rebase commits that have been pushed and shared.
Most teams adopt one style: merge (easier) or rebase (cleaner). Either is fine; mixing them sloppily is the problem.
Undoing things
| What you want | Command |
|---|---|
Unstage a file you added | git restore --staged <file> |
| Discard uncommitted changes in a file | git restore <file> (⚠️ destructive) |
| Change the last commit message | git commit --amend |
| Add a forgotten file to the last commit | git add <file>; git commit --amend --no-edit |
| Move HEAD back but keep changes | git reset HEAD~1 |
| Nuke last commit and changes | git reset --hard HEAD~1 (⚠️) |
| Revert a pushed commit (safely) | git revert <sha> — creates an “undo” commit |
Rule: reset --hard and push --force are the two commands that lose work. Understand them before using them.
.gitignore
A file listing patterns for files Git should not track:
# node
node_modules/
# Python
__pycache__/
*.pyc
.venv/
# Secrets — never commit
.env
*.pem
credentials.json
# OS / editor
.DS_Store
.idea/
.vscode/
Never commit secrets. If you do, rotate them — git rm doesn’t actually remove them from history. Tools: git-secrets, gitleaks, trufflehog.
Git in IT / ops
You’ll use Git for:
- IaC repos — Terraform, Ansible, Kubernetes manifests
- Config backups — router/switch configs pushed to a repo as backup and audit trail
- Documentation — this vault, runbooks, dotfiles
- Scripts — your personal toolkit
The pattern you’ll see everywhere is GitOps: git is the source of truth, tooling watches it and applies changes.
Beyond the basics
Topics to learn after you’re fluent with the above:
- Rewriting history:
rebase -i,cherry-pick,reflog - Stashing:
git stashfor “I need to switch context but not commit” - Tags: marking releases
- Submodules / subtrees: including other repos
- Hooks: scripts that run on commit/push (pre-commit framework is widely used)