From your first commit to confident branching, rebasing, and undoing mistakes without fear. Git is the foundation every other tool in this toolkit sits on — GitOps, CI/CD, and IaC all assume you're fluent here. Work top to bottom, or jump to a part.
The journey:
| You need | Why |
|---|---|
| Git installed | Every command here is git |
| A terminal | All CLI |
| (Optional) a GitHub/GitLab account | For the remotes and PR sections |
Confirm and set your identity before your first commit — Git stamps it on everything:
git --version
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
Git isn't a pile of file copies — it's a graph of commits, each a full snapshot with a parent. The magic is that most work happens across three areas:
| Area | What it is | Moved by |
|---|---|---|
| Working tree | Your actual files on disk | you, editing |
| Staging area (index) | What will go into the next commit | git add |
Repository (.git) |
The committed history | git commit |
The loop is always: edit → add (stage) → commit (snapshot). Staging is what lets you commit some of your changes and not others — a feature, not a chore.
git init myproject && cd myproject # create a repo
echo "# My Project" > README.md
git status # see untracked/modified files
git add README.md # stage it
git commit -m "Add README" # snapshot it
git log --oneline # your history, one line per commit
git status is the command you'll run most — it always tells you which of the three trees your changes are in and what to do next. Add a .gitignore early so build output, node_modules/, and secrets never get committed.
A branch is just a movable pointer to a commit — cheap to create, cheap to throw away. Do all real work on branches, never directly on main.
git switch -c feature/login # create + switch to a branch (new syntax)
# ...edit, add, commit...
git switch main # back to main
git merge feature/login # bring the work in
git branch -d feature/login # delete the merged branch
If main didn't move while you worked, Git does a fast-forward (just advances the pointer). If it did, Git creates a merge commit joining both lines of history.
A remote is a copy of the repo somewhere else (GitHub, GitLab, a server). origin is the conventional name for your main one.
git remote add origin [email protected]:you/myproject.git
git push -u origin main # push + set upstream (only need -u once)
git clone [email protected]:you/other.git # copy an existing repo
git fetch # download remote changes, don't merge
git pull # fetch + merge (or --rebase) in one step
fetch vs pull: fetch updates your view of the remote without touching your work — safe any time. pull also merges it into your branch. When in doubt, fetch and look before you merge.
Both integrate one branch into another; they differ in the history they produce.
git switch feature
git rebase main # replay your commits on top of the latest main
The golden rule: never rebase commits you've already pushed and others may have based work on. Rebase your local, un-pushed branch to tidy it before opening a PR; merge to integrate shared branches.
Interactive rebase is the power tool for cleaning up a messy branch:
git rebase -i main # squash, reorder, reword, or drop commits before review
Almost nothing in Git is truly lost — know which tool matches the situation:
| You want to… | Command | Note |
|---|---|---|
| Unstage a file | git restore --staged <f> |
keeps your edits |
| Discard working-tree edits | git restore <f> |
destructive — edits gone |
| Fix the last commit message/contents | git commit --amend |
rewrites that commit |
| Undo a pushed commit safely | git revert <sha> |
new commit that inverts it |
| Move a branch pointer back | git reset --soft/--mixed/--hard <sha> |
--hard discards changes |
| Stash work-in-progress | git stash / git stash pop |
shelve, switch, come back |
| Recover a "lost" commit | git reflog |
Git's undo history for HEAD |
revert vs reset: use revert for anything already shared (it's non-destructive and safe on shared history); use reset only on local, un-pushed commits. When you think you've lost work, git reflog almost always finds it.
git log --oneline --graph --all # the whole branch topology, visually
git show <sha> # what a specific commit changed
git diff main..feature # difference between two branches
git blame <file> # who last touched each line (and in which commit)
git bisect start # binary-search history to find a bug's origin
git bisect is underused gold: mark a known-good and known-bad commit and Git checks out the midpoint repeatedly until it pinpoints the commit that introduced a regression.
This is how teams actually ship:
git switch -c fix/timeout main # branch from up-to-date main
# ...work, commit in small logical chunks...
git push -u origin fix/timeout # push the branch
# open a Pull/Merge Request in the UI → review → CI runs → merge
Conventions that keep it sane:
main before merge to resolve conflicts on your side, not in the merge.main — require reviews and green CI; no direct pushes..gitignore and a secret scanner; a leaked key in history stays there until you rewrite it.main is always deployable. Feature work lives on branches behind review.git config --global pull.rebase true) to avoid noisy merge commits on every sync.git tag -a v1.2.0 -m "...") so CI/CD and rollbacks have stable anchors.main. Work on branches; keep main reviewable and deployable.git reset --hard as a reflex. It discards uncommitted work permanently. Prefer restore/stash, and remember reflog.pull blind into local changes. Fetch and look first when unsure; a surprise merge/conflict mid-work is avoidable..gitignore until it's too late. Add it before the first commit so build output and deps never enter history.git log --oneline. Stage only part of your changes with git add -p and commit them separately.main, then merge. Did you get a fast-forward or a merge commit? Why?--amend the last commit, and git revert a commit. Then "lose" a commit with reset --hard and recover it via git reflog.git rebase -i main to squash them into one well-written commit.Self-check:
revert vs reset — which for shared history, which for local, and why?git revert is your production rollback.You now have the full loop: stage → commit → branch → integrate → undo → collaborate. Everything else in modern DevOps assumes it.
Learned the concepts? Test yourself with Git interview questions.
Go to the question bank →