"A branch is not a copy of your files. It's a sticky note on the timeline that says: keep building from here."
What a Branch Actually Is
Open .git/refs/heads/main in a text editor. You'll see exactly one line: a 40-character commit hash followed by a newline. That's a branch. The entire data structure is a pointer to a commit.
When you run git branch feature-name, Git creates one new file at .git/refs/heads/feature-name and writes the current commit hash into it. No file duplication. No copying of the working directory. Ten branches cost ten files of approximately 41 bytes each.
This matters because it changes how you should think about branching. Creating a branch in Git is not a heavyweight operation. It's creating a label. The cost is negligible; the benefit — isolating work in progress from stable code — is substantial. Branch freely.
HEAD — Where You Are Right Now
.git/HEAD is a file. Its contents when you're on a normal branch:
ref: refs/heads/main
HEAD is Git's answer to "which branch are you on?" When you make a commit, Git advances the branch HEAD points to. When you switch branches, Git rewrites this file and updates your working directory to match the target branch's latest commit.
Detached HEAD is what happens when HEAD contains a commit hash directly instead of a branch name — which occurs when you check out a specific commit by hash (git checkout a3f7b21). You can look around and even make commits in this state, but those commits have no branch tracking them. If you switch to a named branch without first creating one from your position, those commits become unreachable. The fix: git switch -c rescue-branch before leaving.
Creating and Switching Branches
git switch -c feature-name # create and switch (Git 2.23+) git switch main # switch to an existing branch git branch # list all branches; * marks current git branch -d feature-name # delete a fully-merged branch
The older git checkout -b feature-name and git checkout main work identically if you've built muscle memory around them. git switch is available in our 2.28+ requirement and is the clearer command for this purpose.
The workflow: branch off main, build the feature, merge when ready. Main stays at its last known-good commit throughout. If the experiment fails, delete the branch — main is untouched.
Fast-Forward Merge — Why No New Commit
Scenario: main is at commit A. You create feature and add commits B and C on it. Main has not changed since you branched — it's still at A.
Before: main → A
feature → A → B → C
After: main → A → B → C
feature → A → B → C (same commits)
Git sees that feature is directly ahead of main with no divergence. There is nothing to reconcile. Git simply moves the main pointer forward to C. No new commit is created. The history stays linear — the branch structure disappears from the log entirely.
To preserve the "this was a separate branch" record even when fast-forward is possible, use git merge --no-ff feature. This forces a merge commit, making the branch boundary visible in git log --graph. Teams working on shared repositories often require this for feature branches; it's a team convention, not a technical requirement.
Three-Way Merge — Why a New Commit Is Required
Scenario: main is at A. You branch to feature and make commit B. Then commit C lands on main — separately, perhaps a bug fix. Now both branches have diverged from their common ancestor A.
A ← B (feature)
↑
A ← C (main)
Git can't fast-forward because main has moved since the branch point. Instead Git uses three reference points: A (the common ancestor), B (feature tip), and C (main tip).
Git computes: what did feature change relative to A? What did main change relative to A? If the answers are in different places in different files — or non-overlapping regions of the same file — Git applies both changes automatically and creates a new merge commit D with two parents (B and C).
If the changes are in the same lines of the same file, Git cannot determine which version is correct. That's a conflict.
Merge Conflicts — What They Are and a Real Scenario
A conflict is Git saying: "Both branches changed the same lines. I don't know which version is correct. You decide."
A scenario grounded in the RR Skillverse codebase. During the tools pages dark theme update — converting five pages from white to the site's dark palette — two changes were in progress on separate branches simultaneously:
- Branch A was updating the
.tool-cardCSS block, changingbackgroundfrom#ffffffto#0d1117. - Branch B was adjusting responsive padding for the same card, updating
padding: 16pxtopadding: 20px 24px.
Both touched the same rule in main.css. After the merge attempt, Git marks the conflict in the file:
<<<<<<< HEAD
.tool-card {
background: #ffffff;
padding: 16px;
=======
.tool-card {
background: #0d1117;
padding: 20px 24px;
>>>>>>> feature/dark-theme
Git is not broken. It found two different versions of the same lines and flagged them for a human decision. The resolution is to combine the intent of both branches: keep the dark background and the new padding, in one coherent block. Then delete all three marker lines (<<<<<<<, =======, >>>>>>>), git add the file, and git commit.
The conflict markers are not part of the file's content. Every single marker line must be removed before the file is syntactically valid again. Leaving even one in place is a bug.
🎯 Quick Check
Q1: You make 4 commits on a feature branch. Main hasn't changed. What happens when you merge, and what does the history look like after?
Show Answer
Fast-forward merge. The main pointer moves forward 4 commits along the existing chain. No new commit is created. git log shows a perfectly linear history — the 4 feature commits appear as if they were always committed directly on main.
Q2: Why does a three-way merge need the common ancestor commit, not just the two branch tips?
Show Answer
Without the ancestor, Git can't determine the origin of a difference. If the same line appears differently on both branches, Git needs to know what that line looked like before both branches diverged. The ancestor provides that baseline. Without it, Git can't distinguish "Branch A changed this" from "Branch B changed this" from "both branches independently made the same change."
Q3: After a merge leaves conflict markers in a file, what are your two options?
Show Answer
1. Resolve: edit the file to remove all conflict markers and write the correct combined result, then git add <file> and git commit to complete the merge. 2. Abort: git merge --abort — restores every file to its pre-merge state. Nothing is lost; the branch is intact and you can try again.
Key Takeaways
- A branch is a ~41-byte file pointing to a commit — not a copy of the codebase; creating one is essentially free
- HEAD tells Git which branch is current; switching branches updates HEAD and swaps the working directory to match
- Fast-forward: one branch is directly ahead of the other, Git just moves the pointer — history stays linear, no new commit
- Three-way merge: both branches have new commits since diverging, Git creates a merge commit with two parents
- A conflict marker is Git's annotation, not a file error — remove all three marker lines and write the correct combined result