| State | Area | Meaning |
|---|---|---|
| Modified | Working directory | Changed, not yet marked |
| Staged | Staging area (index) | Selected for the next commit snapshot |
| Committed | Local repository (.git) |
Safely stored in the local database |
flowchart LR
W["Working directory<br/>(modified)"] -- "git add" --> I["Staging area<br/>(staged)"]
I -- "git commit" --> L[("Local repo<br/>(committed)")]
L -- "git push" --> R[("Remote<br/>origin")]
R -- "git pull / clone" --> W
I -- "git restore --staged" --> W
git statustells you which state each file is in.git addpicks exactly what goes into the commit — one commit should be one meaningful change.git commit -m "..."creates a snapshot in the local repo; nobody sees it untilgit push.git diffcompares the working directory with staging;git diff --staged(=--cached) compares staging with the last commit — what will be committed.- To unstage a file but keep the change:
git restore --staged FILE(old way:git reset HEAD FILE).git restore FILE(no--staged) discards the working-directory change.
Branching and merging: a branch is a separate line of work that leaves the main code alone; merging brings tested changes back to the main branch.
Traps:
- Thinking a commit is already on the server.
- git diff is empty although there are changes — because they are staged.
- Using git reset --hard just to unstage — it wipes uncommitted changes.
| Command | Meaning |
|---|---|
git status |
File states |
git add src/login.js |
Stage one file |
git commit -m "Add login form" |
Commit |
git diff --staged |
Show staged changes |
git restore --staged src/login.js |
Unstage, keep the change |
What a commit really is
A commit is a snapshot of the whole project plus metadata: author, date, message, and a pointer to its parent commit(s). It is identified by a SHA hash calculated from its content, so history cannot be changed silently. A branch is just a movable label pointing at one commit, and HEAD points at the branch (or commit) you have checked out. That is why creating a branch is instant.
A ← B ← C main
↖
D ← E feature/login (HEAD)
Branching strategies
| Strategy | Branches | Fits |
|---|---|---|
| GitHub Flow | main plus short feature branches merged by PR |
Web apps deployed continuously |
| Trunk-based | Everyone integrates into main at least daily, feature flags hide unfinished work |
High-frequency CI/CD |
| Git Flow | main, develop, feature/*, release/*, hotfix/* |
Products with scheduled versioned releases |
Git Flow adds overhead; most teams doing continuous delivery prefer GitHub Flow or trunk-based development.
Merge or rebase?
| Merge | Rebase | |
|---|---|---|
| History | Keeps the real shape with a merge commit | Linear, as if work happened in sequence |
| Rewrites commits? | No | Yes, new SHAs |
| Safe on shared branches? | Yes | Only on your own unpushed or private branch |
A fast-forward merge happens when main has not moved since you branched: Git just moves the label forward, with no merge commit.
Selecting changes precisely
git add -p walks through each changed block and asks whether to stage it, so one messy working session can become several clean commits. git stash push -m "wip login" saves a named stash and git stash list shows them all.
Tags for releases
An annotated tag (git tag -a v1.2.0 -m "Release 1.2.0") stores the tagger, date, and message, and is what release pipelines and changelogs usually rely on. Follow semantic versioning: MAJOR for breaking changes, MINOR for new features, PATCH for fixes.