The problem branches solve
Your project works. The demo is Friday. You want to try a completely different login screen, but if it goes wrong you must be able to get the working version back in seconds.
Copying the folder is what students do. Then the two copies drift apart and merging them by hand takes an evening.
A branch is Git's answer: a second line of history that can be thrown away or merged back with one command.
A branch is a pointer
This is the whole secret. A branch is not a copy of your files. It is a 41-byte file containing the hash of one commit. main is a pointer. login-redesign is another pointer. When you commit, the pointer you are on moves forward.
That is why creating a branch is instant, even in a project with ten thousand files.
HEAD is another pointer. It points at the branch you are currently on.
Create and switch
git branch
git switch -c login-redesign
git branch
git switch -c creates the branch and moves onto it. The star in git branch output marks where you are.
You may see git checkout -b login-redesign in older tutorials. It does the same thing. switch is newer and clearer, because checkout also does several unrelated jobs.
Now work normally:
echo "// new login screen" > login.js
git add login.js
git commit -m "Start new login screen"
Switch back and look:
git switch main
ls
login.js is gone from the folder. It is not lost. It lives on the other branch, and it comes back when you switch there. Git rewrites the files in your folder to match whichever branch you moved to.
Merging
Get onto the branch that should receive the changes, then merge.
git switch main
git merge login-redesign
Two things can happen.
Fast-forward. If main has no new commits since you branched, Git just slides the main pointer forward. No merge commit is created. The message says Fast-forward.
Three-way merge. If main moved on too, Git builds a new commit with two parents, joining both lines. The message says Merge branch 'login-redesign'.
If both branches changed the same lines of the same file, Git stops and asks you to decide. That is a merge conflict, and it gets a whole lesson.
Delete the branch after merging
git branch -d login-redesign
Lowercase -d refuses if the branch is not merged, which protects you. Uppercase -D deletes anyway and loses those commits. Use -D only when you are sure the experiment failed.
See the shape of your history
git log --oneline --graph --all --decorate
Output looks like this:
* 9f3c2a1 (HEAD -> main) Merge branch 'login-redesign'
|\
| * 7b21d40 (login-redesign) Style the login form
| * 4c88e19 Start new login screen
* | 2ad91f7 Fix attendance count bug
|/
* 1e40b3a Add students array
Read it bottom to top. One line split into two, then joined. Once you can read this graph, Git stops feeling mysterious.
A branch naming habit
feature/attendance-export
fix/login-crash
docs/readme-setup
Names that say what and why. In a four-person project this saves real time.
Commit or stash before you switch branches. If you have uncommitted changes that clash with the target branch, Git refuses and prints "Your local changes would be overwritten by checkout". Do not try to force past it. Either commit the work, or park it:
git stash
git switch main
git switch -
git stash pop
git switch - goes back to the previous branch.
When to branch
Honest answer for a student project: one branch per feature, one branch per person, or both. Keep main in a state that always runs. If someone asks for a demo, you should be able to show main without fear.
Practise merging in a throwaway folder before you need it for real. Make a repo, two branches, edit different files, merge. Then do it again editing the same file, and watch the conflict appear. Ten minutes of practice removes the panic when it happens the night before submission.
Next: putting all of this on GitHub, and fixing the rejected push.