Git's three zones
Git is not "a folder with copies". A change travels through three zones before it is recorded in the project's history:
- Working directory: your files exactly as you edit them. Here Git doesn't know about the changes yet.
- Staging area (index): the "boarding gate". You choose which changes go
into the next commit with
git add. - History (local repository): the immutable sequence of commits. Only
git commitmoves something here.
git status # what's in each zone?
git add file.js # working -> staging (you prepare the change)
git add . # stages EVERYTHING modified
git commit -m "feat: add login" # staging -> history
Key idea: only
git commitwrites to the history.git adddoes not create a save point; it only prepares what will go into the next commit.
A commit is a snapshot identified by a hash (SHA), with author, date, message, and a pointer to its parent commit. That chain of parents is the history.
Branches
A branch is a lightweight pointer to a commit. Creating a branch is almost free and
lets you work in parallel without touching main:
git branch # list the branches (the current one with *)
git switch -c feature/login # create and switch to a new branch
git switch main # go back to main
git log --oneline --graph # see the tree of commits
HEAD is a pointer to "where you are" (usually the current branch).
Integrating work: merge vs rebase
When your branch and main have advanced separately, you need to integrate. Two
strategies:
git merge
Creates a merge commit that joins the two histories. It does not rewrite anything: it preserves the real graph of how the work happened.
git switch main
git merge feature/login # creates a merge commit joining both branches
git rebase
Reapplies your commits on top of the tip of main, as if you had
written them from there. The result is a linear history, with no merge
commits.
git switch feature/login
git rebase main # move your commits on top of main (rewrites them)
When to use each?
- merge to integrate a branch into
main(especially when closing a PR): it preserves context and is safe on shared branches. - rebase to clean up your own branch before opening the PR (bring it up
to date with
main, group commits). Linear, easy-to-read history.
Golden rule: never
rebasecommits that you have already published and others have pulled. Rewriting shared history breaks your teammates' repo. Rebase in private, merge in public.
Resolving conflicts
A conflict happens when merge/rebase can't combine two changes on the same lines. Git marks the file like this:
<<<<<<< HEAD
const greeting = "Hello";
=======
const greeting = "Hi";
>>>>>>> feature/login
You edit the file leaving the final version (deleting the markers), and then:
git add file.js # mark the conflict as resolved
git commit # in merge: closes the merge
# or, if you were rebasing:
git rebase --continue
git rebase --abort # cancel and go back to the previous state
Undoing: revert vs reset
git revert <commit>: creates a new commit that inverts the changes of another. It does not delete history -> it's the safe way on shared branches.
git revert a1b2c3d # adds a commit that undoes a1b2c3d
git reset: moves the branch to another commit, discarding the later ones from the history. It rewrites history -> use it only on local changes.
git reset --soft HEAD~1 # undoes the last commit, leaves the changes in staging
git reset --mixed HEAD~1 # (default) undoes commit and staging; keeps files
git reset --hard HEAD~1 # undoes EVERYTHING, even your files (dangerous)
Summary: for something already published, revert. For local commits you haven't shared yet, reset.