Introduction
In Git, a branch is just a movable reference to a commit. Creating a branch is creating a 41-byte file. Compared to centralized systems where branches are full directory copies, Git's model makes branching cheap enough to use casually.
What a branch actually is
cat .git/refs/heads/main
# a1b2c3d4e5f6...
git rev-parse main
That hash is the tip of the branch. The "branch" itself is the chain of commits reachable from the tip via parent pointers.
Creating branches
git branch feature/login # create, do not switch
git switch -c feature/login # create and switch
git switch -c hotfix v1.2.3 # from a tag
git branch fixup HEAD~3 # from a relative ref
Moving the tip
Every commit on the current branch advances its tip by one. Commands that move tips include commit, merge, rebase, reset, and cherry-pick.
git commit -m "Work" # main moves forward
git reset --hard HEAD~2 # main moves backward; commits become unreachable
Branches and reachability
A commit is "alive" as long as some ref reaches it. Delete every branch and tag pointing at a commit and it becomes dangling; Git's garbage collector eventually removes it.
git fsck --unreachable
git gc
Tracking upstream
A local branch can be linked to a remote-tracking branch:
git branch --set-upstream-to=origin/main main
git branch -u origin/main
git status # shows ahead/behind based on the link
Branch maintenance
git branch --merged main # branches fully merged
git branch --no-merged main # branches with unmerged work
git branch -d feature/old # delete merged
git branch -D feature/discarded # force delete
Comparing branches
git log main..feature/login --oneline
git diff main...feature/login
git range-diff main..@{u} main..HEAD
range-diff is great for comparing two versions of a rebased branch.
Branch policies on hosts
Most hosting platforms let you protect branches by name pattern: require pull requests, signed commits, passing CI, or specific reviewers before a push is accepted. The Git side of this is implemented via pre-receive hooks; the platform side is configuration. From your local clone, branch protection is invisible until your push is rejected, at which point the server explains why. To inspect protection from the command line, use the host's CLI (gh, glab) or its API.
gh api repos/owner/repo/branches/main/protection
Common mistakes
Imagining branches as containers of commits. They are not; commits exist independently and many branches can reach the same commit. Deleting a "feature" branch and assuming the commits are gone; they are gone only after they become unreachable and the reflog expires (90 days by default). Using long single-word branch names that collide with file names; -- separators avoid the ambiguity. Finally, working directly on main in a shared repo; always branch for non-trivial work, even if you plan to fast-forward later.