A Git branch is a pointer to a commit, saved as a small file that holds one hash. Making one copies no project files, so it costs almost nothing.
Developers use them to keep features and fixes away from the stable line, then merge the finished work back in.
Plenty of tutorials say new repositories start on main. Git’s own manual for version 2.56.0 (git-scm.com, September 2026) says the default initial name is master, and the switch to main is scheduled for Git 3.0.
What Is a Git Branch?

Git treats a branch as a name attached to a single commit. Each time you commit, the name moves forward on its own.
Nearly every version control system has branching, but Git builds it from far less. Atlassian’s tutorial points out that Git stores a branch as a reference to a commit rather than copying files between directories, which is where it parts ways with Subversion.
Pro Git calls creating and switching branches nearly instantaneous, for the same reason. It’s a small file, not a second copy of the project.
Why two definitions circulate
Search the topic and you’ll see two descriptions side by side. GeeksforGeeks talks about a separate workspace, and CodeSignal says a new branch is a copy of the current one, files and commits included. Pro Git, Atlassian and GitKraken describe a pointer to one specific commit.
The workspace version matches what you see, since your files change when you switch. The pointer version matches what Git stores, and it’s the only one that predicts how branches behave.
GitKraken’s guide says it plainly: a branch is not a collection of commits.
The commit is the snapshot. The branch just gives you a name to find it by.
How Does Git Store a Branch?

Under .git/refs/heads, Git keeps one file per branch, named after it, containing a single commit hash. Pro Git’s internals chapter treats that file as the entire branch, a simple pointer to the head of a line of work.
With the default SHA-1 object format the file holds a 40-character commit hash. The git-init manual lists sha256 as an optional object format too.
Once git gc packs references, the same data may live as a line in .git/packed-refs instead of a loose file, according to the git-check-ref-format manual.
The hash is everything. Atlassian notes that a branch’s history gets worked out from the parent links between commits, so the branch holds no list of its own commits.
The numbers worth remembering are few. The SHA-1 hash inside a branch file is 40 characters (Pro Git), and branch files sit in .git/refs/heads (git-check-ref-format manual). The earliest release in the git-switch manual’s version history is Git 2.23, August 2019. And master stays Git’s default initial branch name until Git 3.0 changes it to main (git-init manual, Git 2.56.0).
HEAD and the checked-out branch
HEAD lives in .git/HEAD and usually holds a symbolic reference to whatever branch you’re on. Pro Git shows it reading ref: refs/heads/master, and after git checkout test it reads ref: refs/heads/test.
So the chain goes HEAD, then a branch file, then a commit. When you run git commit, Git creates the commit object with its parent set to whatever HEAD’s reference points to, and the branch pointer moves forward without any help from you.
You can read the chain yourself with git symbolic-ref HEAD. Pro Git’s example prints refs/heads/master.
Detached HEAD
Detached HEAD just means HEAD holds a commit hash instead of a branch reference.
The git-checkout manual says this happens when you check out something that isn’t the tip of a local branch, a tag or an arbitrary commit for example. A remote-tracking branch does the same thing, since HEAD never points at one. For this case the git-switch manual offers git switch –detach, and describes it as meant for inspection and discardable experiments.
Commits made in this state belong to no branch. Per the git-checkout manual, nothing refers to them, so Git’s routine garbage collection eventually deletes them unless you create a reference first.
Any detached HEAD work worth keeping needs a name. The git-switch manual’s answer is git switch -c good-surprises, which creates the branch at the current commit.
How Do You Create, Switch and Rename a Git Branch?

git switch -c name creates a branch and moves you onto it in one step. git branch name creates it without switching, and git branch -m renames one.
- List what exists with git branch. The current branch carries an asterisk, and -a adds remote-tracking branches.
- Create and enter a branch with git switch -c feature-login. It starts at the current commit unless you name another start point.
- Commit as usual. The new branch pointer moves forward and the old one stays put.
- Check the result with git branch -v, which shows each tip’s abbreviated hash and commit subject, plus its relationship to the upstream branch when there is one.
The git-branch manual is explicit that git branch name only creates the pointer and leaves the working tree alone. For the everyday job of creating a new branch, the same manual’s notes recommend git switch with -c. I’d stick with that one.
git checkout -b does the same thing and still works. The git-branch manual’s own example calls it the combined form of branch plus switch.
git switch itself arrived in Git 2.23 (August 2019), per the git-switch manual’s version history.
Switching refuses to run when it would overwrite local changes. Git prints an error naming the file and leaves you where you were. The -m flag carries those changes across by stashing and reapplying them.
Renaming uses git branch -m old new, or git branch -m new for the current branch. A branch rename also moves the reflog, according to the git-branch manual, and -M forces it when the new name already exists.
Default branch name: main or master
As of the Git 2.56.0 manual (September 2026), Git’s default initial branch name is still master.
The git-init manual adds that the default will change to main when Git 3.0 is released. Until then, the init.defaultBranch setting or the –initial-branch option (-b) overrides it.
That clashes with tutorials calling main the default. CodeSignal’s lesson says Git initializes new repositories with main, which only holds true with local configuration, since stock Git still says master.
Pro Git settles the practical side. The master branch isn’t special and behaves like any other.
Branch naming rules
Git checks every new branch name against git check-ref-format, which sets out 10 rules for reference names.
Spaces and ASCII control characters are out, and so are two consecutive dots. The tilde, caret, colon, question mark, asterisk, open bracket and backslash are all banned. No path component can start with a dot or end in .lock, and a name can’t end in a dot, start or end with a slash, or contain consecutive slashes. The single character @ is rejected, along with any @{ sequence.
Slashes are fine for grouping, so feature/login is a valid name. Branch names get one extra restriction on top: the manual says a name can’t begin with a dash, even though reference components can.
What Happens When You Merge a Git Branch?

Merging pulls commits from another branch into the one you’re on, and how it goes depends on the histories involved. Sometimes it’s a fast-forward, sometimes a true merge, and sometimes Git stops and asks for help.
| Outcome | Condition | Result |
|---|---|---|
| Fast-forward | Current branch is an ancestor of the merged branch | Pointer moves forward, no merge commit |
| True merge | Histories have diverged | New merge commit with two parents |
| Conflict | Both sides changed the same area of a file | Merge stops, files get conflict markers |
In every case, what git merge does comes down to replaying the changes made on the other branch since the two histories diverged, as the git-merge manual describes it.
When nothing has diverged, Git only moves the pointer. The –no-ff option forces a merge commit anyway, and setting merge.ff to false makes that the default.
One documented exception. Merging an annotated tag always creates a merge commit, even when a fast-forward is possible. Use –ff-only when you just want to land on a tagged release.
A conflict appears only when both branches changed the same area of a file. Git can’t pick a side, so it stops with HEAD unchanged, points MERGE\_HEAD at the other branch tip and writes conflict markers into the file.
The markers are seven less-than characters, seven equals signs and seven greater-than characters around the two competing versions.
To resolve the conflicts, edit the marked files, run git add on them, and finish with git commit or git merge –continue. Run git merge –abort instead to go back to the pre-merge state.
Abort has a limit, though. The manual warns that git merge –abort can’t always rebuild uncommitted changes you had when the merge started.
Commit or stash first. According to the pre-merge checks section, git merge stops without doing anything when local uncommitted changes overlap with files it needs to update.
How Do Remote-Tracking Branches Relate to Local Branches?

A remote-tracking branch is a read-only local reference, written like origin/main, that records where a branch stood on the remote at your last contact with it. Git moves it for you during network operations, and you can’t commit to it.
Pro Git’s internals chapter explains why. You can check one out, but HEAD never points at it symbolically, so a commit can never update it.
A local branch is yours to commit on. The remote-tracking one is only a bookmark of the remote branch’s last known position, and the upstream branch is the remote branch your local one is paired with.
That pairing sits in .git/config as a remote entry and a merge entry, per the git-push manual. The upstream branch is also what lets git status report how many commits you’re ahead or behind.
Publishing a new branch
Nothing leaves your machine until you push it. Pro Git explains that you share a branch by pushing it explicitly, which is what keeps private branches private.
git push -u origin feature-login publishes the branch and records its upstream. Since Git 2.0 the default push.default value is simple, which pushes to a same-named branch on the remote.
Set push.autoSetupRemote to true and the first push of a new branch sets the upstream on its own.
The reverse direction is automatic in one case. git switch feature-login creates a local tracking branch when exactly one remote has a branch of that name, and checkout.defaultRemote settles ties when several do (git-switch manual).
Keeping remote-tracking branches current
Pro Git warns that the ahead and behind counts from git branch -vv only reflect your last fetch, because the command never contacts the server. Fetch first, or run git fetch –all before git branch -vv, and then trust the counts.
A fresh git fetch updates the remote-tracking branches without touching your working directory. git pull runs that fetch and then integrates the upstream branch. By default it only fast-forwards and fails if your local branch has diverged, unless you choose a strategy with –no-rebase, –rebase, or the pull.rebase and pull.ff settings.
Stale references are the annoying part. A branch deleted on the remote leaves its remote-tracking reference behind, and git fetch –prune removes those references. The fetch.prune setting makes that automatic, according to the git-fetch manual.
How Do You Delete a Git Branch, and Can You Recover It?

git branch -d deletes a local branch only when its work is already merged, and git branch -D deletes it regardless. Either way, Git removes the pointer and nothing else.
| Command | What it removes | Safety check |
|---|---|---|
| git branch -d name | Local branch | Refuses unless merged |
| git branch -D name | Local branch | None (shortcut for –delete –force) |
| git push origin –delete name | Branch on the remote | Blocked only by server config or hooks |
| git branch -d -r origin/name | Remote-tracking reference | Next fetch recreates it unless configured not to |
The -d check runs against the branch’s upstream, or against HEAD when no upstream is set, according to the git-branch manual. Merge status is judged from your local references, so fetch before deleting.
For routine branch deletion, use -d and let the merge check act as the safety net. Reach for -D only when you mean to throw the work away.
Deleting on the server removes only the pointer there. Pro Git adds that the server usually keeps the data until garbage collection runs, so an accidental deletion is often easy to undo.
GitHub’s flow guide recommends removing a branch after its pull request merges, and says it can be restored later. The same page notes that pull request and commit history survive, which makes deleting a branch on GitHub low risk once the merge is done.
Recovering a deleted local branch
Recovery runs through the reflog, because deleting a branch also deletes that branch’s own reflog.
- Run git reflog and find the last entry that shows you leaving the branch. The HEAD reflog records every branch switch.
- Copy the commit hash from that entry.
- Recreate the branch with git branch feature-login followed by the hash.
Reflog entries expire, so don’t wait around. The git-reflog manual sets the defaults at 90 days for reachable entries and 30 days for entries no longer reachable from the current tip.
An abandoned, unmerged branch falls under the shorter window. The reflog also only describes your own repository, so a teammate’s clone can’t supply the missing entry.
Which Branch Types and Workflows Do Teams Use?

Teams sort branches by lifespan and role. Short-lived topic branches carry single changes, and long-lived branches show up only when releases demand them.
| Workflow | Permanent branches | Branch lifespan |
|---|---|---|
| GitHub flow | Default branch only | Short, ends in a reviewed pull request |
| Git flow | master and develop | Feature branches last as long as the feature; release and hotfix branches are temporary |
| Trunk-based development | Trunk only | A couple of days at most |
Branch types by role
Git treats every branch the same. The labels below describe how a team uses them, a point Vincent Driessen’s original model makes explicitly.
Feature branches come off develop and merge back into develop. Release branches also start from develop but merge into both develop and master, and they’re named release-\. Hotfix branches start from master, merge into develop and master, and use the hotfix-\ pattern.
That structure is Git flow, published in January 2010. In a March 2020 note, Driessen advised teams that deliver continuously to adopt a simpler workflow such as GitHub flow instead.
He kept Git flow for explicitly versioned software, and for software that supports several versions in the wild at once.
GitHub’s documentation describes its own workflow in 6 steps: create a branch, make changes, open a pull request, address review comments, merge, and delete the branch. Its guide suggests short, descriptive branch names such as increase-test-timeout.
Long-lived versus short-lived branches
Sources disagree on how long a topic branch should stay open. Pro Git says it can sit there for minutes, days or months and merge when it’s ready. Trunk-based guidance says a couple of days at most, because past that it risks becoming a long-lived feature branch.
Read together, they don’t actually contradict each other. Pro Git describes what Git permits, and the trunk-based rule describes the habit that keeps integration cheap.
Under trunk-based development, the same guide ties the choice to team size. Up to 15 people can commit straight to the trunk, and teams of 16 or more do better with short-lived branches plus CI checks before changes land.
Pro Git adds that several long-running branches are optional, and mostly pay off on very large or complex projects.
Git Branch FAQ
Is a Git branch the same as a tag?
No. Both are named pointers to a commit, but a branch pointer moves forward with every commit, while a tag stays on the commit it was created for.
Pro Git’s internals chapter describes a tag as a reference that never moves, which is why tags mark releases.
Should I merge or rebase a branch?

Merge if you want to keep both histories and record the join in a merge commit. Rebase if you’d rather have your commits replayed on top of another tip as new commits.
Publishing rebased history that others already have needs a forced push. The git-push manual describes –force-with-lease as the safer form, because it only overwrites the remote when it still holds the value you expect. A blind –force skips those checks and can lose commits.
Can I create a branch from an older commit or a tag?
Yes. The start point can be a branch name, a commit ID or a tag, so git switch -c fixup HEAD~3 and git branch my2.6.14 v2.6.14 both work, as the Git manuals show.
How do I work on two branches at the same time?
Use git worktree, which checks out a branch in a second linked directory. Git marks such branches with a plus sign in git branch output.
git switch refuses a branch that another worktree already holds, unless you pass –ignore-other-worktrees.
How do I see what differs between two branches?
git log main..feature lists the commits on feature that main lacks. git branch –no-merged shows which branches still hold work that isn’t contained in your current HEAD.
Can you protect a branch from unreviewed changes?
Yes, on GitHub. Branch protection settings can block a merge until requirements are met, such as a set number of approving reviews or approval from a specific team, per GitHub Docs.
How Long Should a Git Branch Live?
Only until one change is finished, reviewed and merged. Work that outlasts that change belongs on a new branch.
In practice that means one purpose per branch, a sync from the default branch before review, and a merge followed by deletion the same day. Syncing comes before the merge because trunk-based guidance allows merges into the default branch only as the last step of a finished branch.
The catch is slicing. Every merge has to be releasable on its own, so large features get cut into smaller pieces, which takes some getting used to.
This position was checked against the Git 2.56.0 manuals in September 2026. Teams that support several released versions at once should revisit it, because Git flow keeps release branches alive for that case, and a documented Git workflow records the rules a team settles on.
- Notepad++ Android Options: Apps That Come Close - October 21, 2026
- MongoDB Cheat Sheet - October 20, 2026
- Adding AI Video Generation to a Software Product: Architecture, Cost Guardrails and QA - October 20, 2026




