How to Use git branches and Naming Conventions Step by Step
Choose a git branch naming convention that works: prefixes, issue IDs, allowed characters, length, and how to enforce the rule with a hook or CI check.

Branch names are read in pull request lists, CI logs and release notes, far more often than they are typed. A convention costs nothing and saves the question "what is test2?". The rules below mix Git's hard limits with team habits that work. They connect to commit message practice and assume Git 2.47.
A convention that scales
feature/1234-export-csv
fix/login-redirect-loop
chore/bump-node-22
docs/deploy-readme
hotfix/2026-11-payment-timeout
release/1.8
The prefix gives the intent at a glance and allows filtering: git branch --list 'fix/*'. The issue number links the branch to the tracker in tools that parse it. Lowercase avoids case-insensitive file system trouble on macOS and Windows, where Fix/Login and fix/login look like the same directory in the refs store.
What Git allows
git check-ref-format --branch "fix/ok-name"; echo $?
git check-ref-format --branch "bad name"; echo $?
git check-ref-format --branch "feature..x"; echo $?fix/ok-name
0
fatal: 'bad name' is not a valid branch name
128
fatal: 'feature..x' is not a valid branch name
128
Git rejects spaces, control characters, ~ ^ : ? * [ \, the sequence .., a leading dash, a trailing slash or dot, and the suffix .lock. The check command is the authority and works in scripts. A branch whose name starts with - would be read as an option by many commands, so Git blocks it for good reason.
Names that cause trouble
git branch fix
git branch fix/loginfatal: cannot lock ref 'refs/heads/fix/login': 'refs/heads/fix' exists; cannot create 'refs/heads/fix/login'
Refs are stored as files, so a branch called fix and another called fix/login cannot coexist. Pick a prefix scheme early and never create a bare branch with a prefix name. Also avoid naming a branch the same as a tag, such as v1.2.0, because commands like git checkout v1.2.0 become ambiguous; see git tags.
Enforce it
#!/usr/bin/env bash
# .git/hooks/pre-push
branch=$(git symbolic-ref --short HEAD)
pattern='^(feature|fix|chore|docs|hotfix|release)/[a-z0-9._-]+$'
if [[ $branch != main && ! $branch =~ $pattern ]]; then
echo "branch name '$branch' does not match $pattern" >&2
exit 1
fiA local hook is a convenience, not a guarantee: clients can skip it. For a rule the team must follow, check in CI or in the hosting platform's branch rules. Share hooks through a tracked directory and git config core.hooksPath .githooks. Rename an existing branch that breaks the rule with the rename steps.
Why the name carries information
A branch name is read far more often than it is written. It appears in pull request lists, in CI logs, in the output of git branch -a and in the title of a merge commit. A name such as fix/login-redirect-loop tells a reviewer the kind of change and its subject before they open anything, while temp2 tells them nothing and gets deleted by someone who guesses it is abandoned. The prefix before the slash is the part that earns its keep, because tooling can act on it: CI can run a different pipeline for release/*, and a cleanup script can target spike/*.
Keep the vocabulary small. Four or five prefixes cover most teams: feature, fix, chore, docs and release. A longer list invites debate about whether a performance change is a feature or a fix, and the debate is not worth the distinction. Pick the nearest prefix and move on.
Ticket numbers and who owns the branch
Including an issue key, as in fix/SH-142-login-loop, links the branch to the tracker automatically in most hosting services and lets anyone find the discussion behind the change. Put the key right after the prefix so branches sort together by type and then by ticket. Avoid personal names as prefixes, such as pete/experiment, for work that others will review: ownership changes, and a branch named after a person that someone else finishes is confusing.
Personal prefixes are still reasonable for scratch work that will never merge. In that case make the intent explicit, and agree that branches under a person's prefix may be deleted by the owner without notice. The rule gives teams a clear place for experiments without cluttering the shared namespace.
Length, case and characters that cause friction
Long names are truncated by terminals, status bars and some CI dashboards, so put the distinguishing words early and keep the whole name under about fifty characters. Use lowercase with hyphens. File systems that ignore case, such as the default on macOS and Windows, treat Fix/Login and fix/login as the same directory under .git/refs/heads, and a pair of branches that differ only in case can break a clone on those systems. Slashes create nested refs, so a branch named fix cannot coexist with one named fix/login, because one is a file and the other needs a directory.
Avoid spaces, which Git rejects, and avoid characters with meaning in shells such as #, & or !, which force quoting in every script that touches the name. The underscore is fine but mixing them with hyphens makes names harder to type from memory, so choose one separator.
Retiring branches and keeping the list short
A convention helps most at cleanup time. Delete a branch as soon as its pull request merges; most hosts offer a setting that does it automatically. Locally, git branch --merged main lists branches already contained in main, which are safe to remove, and git fetch --prune drops bookmarks for branches the server has deleted. A repository with hundreds of old branches hides the few that matter.
Long-lived branches deserve their own rule. If the team keeps a release/1.x branch for patches, protect it on the server and say in the contributing guide who may merge to it. Short-lived work follows the prefixes above, and the two kinds should be easy to tell apart at a glance. Writing the convention down in a short section of the README, and enforcing the parts that matter with a hook or a CI check, keeps it from being a suggestion people forget by the second week.
A convention only works if checking it is cheap. A pre-push hook or a server-side rule that rejects names not matching a pattern such as ^(feature|fix|chore|docs|release)/[a-z0-9._-]+$ gives the error at the moment of the mistake, with a message that shows an example. Many hosts support branch-name rules in repository settings, so the check does not need custom code. Start by warning, then enforce once people have settled in, and always allow an escape hatch for release automation that creates its own names.
Finally, review the list now and then. A convention that no longer matches how the team works, for instance a prefix nobody uses, should be dropped from the document instead of kept as clutter.
Where this does not apply
Trunk-based teams with tiny branches that live for hours may not need prefixes at all; a short descriptive name is enough. A convention that nobody enforces drifts within a month, and a convention that is too elaborate (dates, initials, ticket and type) gets ignored. Match the style your tracker and CI already parse. See the git-check-ref-format manual and branching workflows in Pro Git.


