A Git branch is a lightweight, movable named pointer to a commit, and it represents an independent line of development inside a repository. Among development tools for version control, branches are what let several developers build features, fix bugs and run experiments in parallel without disturbing each other, then combine the results through a merge or a rebase.
Unlike older systems that copied entire directories to create a branch, Git stores a branch as a file containing a 40-character commit hash (or 64 characters in SHA-256 repositories). Creating, switching and deleting branches therefore takes milliseconds regardless of repository size, which is why branch-heavy workflows became practical with Git.
How a Git branch points to commits
Every Git commit records a snapshot of the project and a reference to its parent commit, so commits form a directed acyclic graph. A branch is simply a reference under `refs/heads/` that names one commit in that graph, the branch tip. When a new commit is created while the branch is checked out, Git moves the branch pointer forward to the new commit automatically.
The special reference HEAD tells Git which branch is currently checked out. Normally HEAD contains a symbolic link such as ref: refs/heads/main. If a specific commit is checked out instead of a branch, HEAD points directly to that commit, a situation called detached HEAD; commits made in that state belong to no branch and can be lost once HEAD moves elsewhere unless a branch is created for them.
Several kinds of references are involved in everyday branching, and the table below distinguishes the ones developers meet most often.
| Reference | Stored under | Moves when |
|---|---|---|
| Local branch | refs/heads/ | A commit is made while it is checked out |
| Remote-tracking branch | refs/remotes/origin/ | git fetch updates it from the remote |
| HEAD | .git/HEAD | A different branch or commit is checked out |
| Tag | refs/tags/ | Never, unless deliberately recreated |
| Upstream setting | Branch configuration | Set with --set-upstream or on first push |
Essential branch commands
Day-to-day branch work relies on a small set of commands. Since Git 2.23, git switch handles changing branches and git restore handles file recovery, which splits the overloaded git checkout into two clearer commands.
| Task | Command | Effect |
|---|---|---|
| Create and switch | git switch -c feature/login | New branch at the current commit |
| List branches | git branch -vv | Shows tips and upstream status |
| Publish a branch | git push -u origin feature/login | Creates the remote branch and tracking |
| Merge into current | git merge feature/login | Fast-forward or merge commit |
| Replay onto another base | git rebase main | Rewrites commits on a new base |
| Delete safely | git branch -d feature/login | Refuses if work is unmerged |
| Force delete | git branch -D feature/login | Deletes even unmerged work |
A fast-forward merge happens when the target branch has no commits of its own since the branches diverged, so Git simply moves the pointer. Otherwise Git creates a merge commit with two parents, and if both sides changed the same lines, it stops with a merge conflict that a developer must resolve by hand.
Branching strategies used by teams
A branching strategy is a team agreement on which branches exist, how long they live and how changes flow between them. The choice affects release frequency, merge pain and continuous integration, and no single model fits every project.
| Strategy | Long-lived branches | Best suited to |
|---|---|---|
| Trunk-based development | Only main | Continuous delivery with strong automated tests |
| GitHub flow | main plus short feature branches | Web services deployed many times a day |
| Git flow | main, develop, release and hotfix branches | Versioned products with scheduled releases |
| Release branches | One branch per supported version | Software maintained in several versions |
Most teams now favor short-lived feature branches that last from a few hours to a few days and reach the main line through a pull request with code review. Branches that live for weeks drift away from main, accumulate conflicts and delay feedback, a problem often called merge hell. Feature flags let unfinished work merge early while staying hidden from users.
Git branches compared with related concepts
Several Git features look similar to branches but serve different purposes. Confusing them leads to lost work or awkward histories, so the distinctions are worth stating precisely.
- Tag: a fixed name for one commit, typically a release such as
v2.4.0; it does not advance with new commits. - Fork: a full copy of a repository on a hosting platform such as GitHub or GitLab, which then has its own branches.
- Stash: a temporary shelf for uncommitted changes, not a line of development.
- Worktree: an additional working directory created with
git worktree add, which lets two branches be checked out at once.
The key insight is that a branch costs almost nothing, while a fork duplicates the whole repository. Inside one organization, branches are the normal unit of parallel work; forks matter mainly when contributors lack write access, as in open-source projects.
Common mistakes and how to avoid them
Branch problems usually come from rewriting shared history or letting branches age. The table below lists recurring situations and the safest response to each.
| Problem | Cause | Remedy |
|---|---|---|
| Teammates see diverged history | Rebasing a pushed shared branch | Rebase only private branches |
| Commits seem to disappear | Work done in detached HEAD | Recover with git reflog, then create a branch |
| Huge conflicts at merge time | Long-lived feature branch | Merge or rebase from main frequently |
| Direct commits break the build | Unprotected main branch | Enable branch protection and required checks |
| Dozens of stale branches | No cleanup after merge | Delete branches automatically after merging |
When a forced update is unavoidable, git push --force-with-lease is safer than a plain force push, because it refuses to overwrite remote commits the local repository has not seen. The reflog keeps old branch positions for 90 days by default, which makes most accidental resets recoverable.
The idea in brief
A Git branch is like a bookmark on a separate draft of a document. The main draft stays clean while someone writes a new chapter in a copy, and when the chapter is ready, it is folded back into the main draft. Several people can each work on their own draft at the same time.
What makes Git special is that these drafts cost almost nothing to create. Git does not photocopy the whole document; it just remembers where each draft currently ends, so making a new one is instant.
A practical scenario
A developer on a five-person web team receives a ticket to add password reset by email. She runs git switch -c feature/password-reset from an up-to-date main, commits her work in four small steps over two days and pushes the branch to the shared repository. The hosting platform opens a pull request, the continuous integration pipeline runs about 600 tests, and a colleague reviews the changes.
Meanwhile, a critical bug appears in production. Another developer creates hotfix/session-timeout from main, fixes it in an hour and merges it. Before her own merge, the first developer rebases her private branch onto the updated main, resolves one small conflict and pushes with --force-with-lease. Both changes reach production the same week without either developer blocking the other, and both branches are deleted automatically after merging.