How Git thinks
A commit is a full snapshot of your project, linked to the commit before it.
Where your changes live
git restore <file>
git restore --staged
git fetch / git pull
Try it: the Three Areas lab lets you move files through each stage.
The everyday commands
| Start or copy a repository | git init · git clone <url> |
| See what changed | git status · git diff |
| Stage only the hunks you mean | git add -p |
| Record a snapshot | git commit |
| Read the history | git log --oneline --graph --all |
| Create and switch to a branch | git switch -c feature/login |
| Get the team's latest work | git fetch · git pull --rebase |
| Share your branch | git push -u origin feature/login |
A branch is just a pointer
main and feature/login are labels that point at commits.
HEAD is where you are: the branch you have checked out
(shown as HEAD→).
Committing moves the current label forward. Nothing is copied.
Merge or rebase?
git merge
- Joins two histories with a merge commit
- Keeps the true record; never rewrites commits
- Use for shared branches and PR merges
git rebase
- Replays your commits on top of the latest main
- Linear history, but creates new commit hashes
- Use to update your own branch before a PR
Four common strategies
| Strategy | How it works | Best for | Watch out for |
|---|---|---|---|
| Git Flow | main + develop, plus feature, release and hotfix branches | Versioned releases, several supported versions | Heavy process; long-lived branches drift |
| GitHub Flow | main is always deployable; short feature branches merged by pull request | Web apps and services with continuous deployment | Needs solid CI and fast reviews |
| Trunk-based | Everyone integrates into main at least daily; feature flags hide unfinished work | High-velocity teams and monorepos | Requires feature flags and strong tests |
| Release branches | Cut release/x.y from main; fixes land on main and are cherry-picked back | Products where customers run older versions | Back-porting discipline |
Walk through each one step by step on the interactive flow map.
Choosing a strategy
You deploy many times a day and run one version in production
You ship versioned releases and support older versions
Many teams share one large repo or monorepo
Name branches so they track
| Prefix | Purpose | Example |
|---|---|---|
| feature/ | New functionality | feature/PAY-142-upi-refunds |
| fix/ | Bug fix in the normal flow | fix/PAY-187-null-currency |
| hotfix/ | Urgent production patch | hotfix/1.4.1-login-timeout |
| release/ | Stabilise a version | release/1.5.0 |
| chore/ | Tooling, dependencies, docs | chore/bump-node-22 |
Rules: lowercase and hyphens · include the ticket key · one purpose per branch · delete after merge
Conventional Commits
<type>(<scope>): <subject> <body: what changed and why> <footer: BREAKING CHANGE / Refs>
- Subject in the imperative: “add”, not “added”
- About 50 characters, no trailing period
- Body explains why; the diff already shows what
- Enforce it with commitlint in a commit-msg hook
| feat | a new feature |
| fix | a bug fix |
| docs | documentation only |
| style | formatting, no logic change |
| refactor | restructure, same behaviour |
| perf | performance improvement |
| test | adding or fixing tests |
| build | build system, dependencies |
| ci | pipeline configuration |
| chore | other maintenance |
| revert | undoes an earlier commit |
Messages your team can use
WIP
update
changes after review
final fix (really)
feat(payments): add UPI refund endpoint
perf(search): cache tokenizer per index
refactor(api): extract rate-limit middleware
docs(readme): document local Docker setup
fix(auth): refresh token before expiry check Tokens expiring mid-request caused 401s on slow networks. Refresh when less than 60 s remain instead of after expiry. Refs: AUTH-311
Clean up before you share
$ git rebase -i origin/main
pick 3f2a1c feat(cart): add coupon field fixup 9b7d20 fix typo pick c41e8a test(cart): cover coupon rules squash 77ab03 more tests reword 1d09f4 wip
Five noisy commits become three that each make sense on their own.
git commit --fixup <sha>then rebase -i --autosquash folds it in
git add -pstage hunks to keep commits atomic
pull.rebase = trueno “Merge branch main” noise
push --force-with-leasesafe after a rebase; never plain --force
squash or rebase mergekeeps main linear and readable
When the repository gets heavy
| Problem | Tool | Command |
|---|---|---|
| Large binaries: models, datasets, design files, videos | Git LFS | git lfs track "*.psd" "*.onnx" |
| Clone takes ages because of years of history | Partial clone | git clone --filter=blob:none <url> |
| You only work in one part of a monorepo | Sparse checkout | git sparse-checkout set services/api |
| CI only needs the latest code | Shallow clone | git clone --depth 1 <url> |
| Build outputs and generated files committed | .gitignore | dist/ build/ *.log → artifact store |
Monorepo or many repos?
Monorepo
Atomic changes across services · one CI setup · shared tooling and code
Needs sparse checkout, CODEOWNERS and path-filtered CI to stay fast
Polyrepo
Small, fast repos · independent release cycles · clear ownership
Cross-repo changes need coordination; shared library versions drift
Keeping Git fast
git maintenance start
Background prefetch, commit-graph and repacking, so fetch and log stay quick
core.fsmonitor = true
A file-system watcher makes git status fast on huge working trees
feature.manyFiles = true
Smaller index and untracked cache for repositories with many files
scalar clone <url>
Ships with Git: partial clone, sparse checkout and maintenance in one step
Everyday accidents
I committed to main instead of my branch
git branch feature/x git reset --hard origin/main git switch feature/x
Only if main isn't pushed yet
My last commit is missing a file
git add missed.ts git commit --amend --no-edit
Already pushed? Make a new commit instead
I lost commits after a reset or rebase
git reflog
git branch rescue HEAD@{2}
Reflog keeps every move of HEAD for about 90 days
Team-level incidents
An API key was pushed to the repo
git filter-repo --path .env \ --invert-paths git push --force-with-lease
Rotate the key first. Add gitleaks to pre-commit.
A 3-week-old branch conflicts everywhere
git fetch origin git rebase origin/main
Sync daily, split into smaller PRs, enable rerere.
v1.5 broke something. Which commit?
git bisect start git bisect bad v1.5.0 git bisect good v1.4.0 git bisect run npm test
Binary search finds it in a few steps.
Six habits that prevent conflicts
Small PRs, merged fast
A few hundred lines, reviewed within a day
Sync every day
Rebase on main each morning; small conflicts stay small
Clear ownership
CODEOWNERS and module boundaries limit overlapping edits
Auto-format everything
Prettier, Black or gofmt in pre-commit: no whitespace fights
Defuse hot files
Split giant configs; generate changelogs in CI, not by hand
Reuse resolutions
rerere replays a conflict fix you already made once
From ticket to release
PAY-142The why
feature/PAY-142Linked by name
Refs: PAY-142Typed messages
Closes PAY-142Review + CI
v1.5.0Tag + changelog
Required reviews and status checks; no direct or force pushes
Semantic versions, so any build maps to an exact commit
release-please or semantic-release read your commit types
Small branches.
Clear messages.
Merge often.
A clean history isn't a tool you install. It's a habit the whole team keeps.
git config --global pull.rebase true
No more accidental merge commits
Add commitlint to one repo and see how it feels
Require a review and green CI before merge
Move changes from your editor to the team
Type real Git commands, watch the graph
Same starting point, two different histories
git merge
On main. Merging joins both histories with a new merge commit; no existing commit changes.
git rebase
On feature/login. Rebasing replays your commits on top of main. Watch the hashes change.
Walk through each workflow
Which one fits your team?
Pick the situation that sounds like you. The map switches to the recommended strategy.
You deploy many times a day and run one version in production
You ship versioned releases and support older versions
Many teams share one large repo or monorepo
Test yourself
Check your own messages and branch names
Commit message checker
Type a subject line and see how it scores against the conventions.
Branch name checker
Prefix, lowercase-and-hyphens, ticket key or version.