Home / Alt manpages / git-check-ref-format(1)

  • git-check-ref-format(1)
  • User command
  • linux

Reject Bad Git Branch and Tag Names with check-ref-format

Use git check-ref-format to catch a malformed branch or tag name before a script creates, fetches or pushes anything with it. You will use git check-ref-format to test names, and it reports success with exit status 0. It only validates input: it does not create a branch, tag or other repository object.

  • Git 2.43.0 is installed with the git-man package on the machine used for these examples.
  • A shell is all you need, plus a Git repository for the branch-specific example.
  • About 10 minutes, and no elevated privileges.

1. Check a complete reference name

A full refname normally includes a namespace such as refs/heads/ for a branch or refs/tags/ for a tag. Pass the name as one quoted argument. Quoting matters when input comes from a variable, because it stops the shell treating wildcard characters as filename patterns before Git sees them.

git check-ref-format 'refs/heads/release/2026'

There is no output when the name is valid, and the command exits successfully. In a script, check the status directly:

if git check-ref-format --no-allow-onelevel "$refname"; then
    printf 'valid ref: %s\n' "$refname"
else
    printf 'invalid ref: %s\n' "$refname" >&2
    exit 1
fi

The default is --no-allow-onelevel. A name such as release is rejected because it has no slash-separated category. Keep that default when accepting complete refs. If you deliberately accept a one-level name, say so explicitly:

git check-ref-format --allow-onelevel 'release'
printf 'status=%s\n' "$?"

Checkpoint

A valid complete ref produces no diagnostic and status 0. A rejected ref produces a non-zero status. Do not read silence alone as success in a longer pipeline; test the status.

2. Validate a branch shorthand correctly

Use --branch when the value is a branch name supplied by a user or another command, rather than a complete refs/heads/... name.

branch_name='feature/report-export'
if git check-ref-format --branch "$branch_name"; then
    printf 'branch name accepted: %s\n' "$branch_name"
else
    printf 'branch name rejected: %s\n' "$branch_name" >&2
    exit 1
fi

This check is stricter than simply prefixing the value with refs/heads/. A branch name cannot begin with a dash, even though a dash can occur at the beginning of a ref component in the complete-ref check. The two forms are different interfaces, so validate the form your command will actually receive.

Inside a repository, --branch also expands the previous-checkout shorthand @{-n}. For example, @{-1} refers to the last thing checked out by git switch or git checkout.

Tip

That previous checkout can be a commit rather than a branch when detached HEAD was involved, so do not assume the printed value is always a branch name.

3. Normalise only when you want canonical input

--normalize removes leading slashes and collapses repeated slashes between components. It prints the resulting refname if that result is valid. That helps when a program intentionally accepts a path-like value and wants one canonical spelling.

git check-ref-format --normalize '//refs//heads///feature/report-export'

Expected output is:

refs/heads/feature/report-export

Normalisation is not a general repair tool. It does not make a name containing .., a trailing dot, a component ending in .lock, or forbidden punctuation valid. It also changes the value, so use the printed result rather than the original input if you continue:

normalised=$(git check-ref-format --normalize "$candidate") || {
    printf 'invalid ref: %s\n' "$candidate" >&2
    exit 1
}
printf 'canonical ref: %s\n' "$normalised"

The older spelling --print is deprecated. Prefer --normalize in new scripts.

4. Learn the rules behind common failures

Git permits slashes for hierarchy, but rejects a slash-separated component that begins with a dot or ends with .lock. The full list of rejections:

  • Punctuation: ~, ^, :, ?, *, [ and backslashes.
  • Whitespace and control characters: control characters and spaces.
  • Dots and slashes: consecutive dots, a trailing dot, and leading, trailing or repeated slashes.
  • Reserved sequences: @{, and the single character @.
  • No slash at all: a full ref must contain at least one slash unless you enable --allow-onelevel.

These restrictions are more than naming style. Git uses double dots for revision ranges, tildes and carets for revision expressions, colons for source and destination refs, and @{ for reflog notation. Rejecting them keeps a value from being mistaken for an expression in a later command.

for candidate in \
    'refs/heads/good-name' \
    'refs/heads/bad..name' \
    'refs/heads/.hidden' \
    'refs/heads/temporary.lock'; do
    if git check-ref-format "$candidate"; then
        printf 'accepted: %s\n' "$candidate"
    else
        printf 'rejected: %s\n' "$candidate"
    fi
done

Checkpoint

Expect one accepted line for refs/heads/good-name and rejected lines for the other three. The loop is read-only and does not need sudo.

5. Allow a wildcard only for a refspec pattern

Ordinary ref validation rejects *. A remote refspec pattern is a different input type, so validate it with --refspec-pattern. Git permits one wildcard, not several.

git check-ref-format --refspec-pattern 'refs/heads/release/*'

Warning

Do not add this option merely to make a rejected branch name pass. A wildcard pattern can select multiple remote refs and deserves separate handling. Keep the pattern quoted and validate it before passing it to fetch or push logic.

6. Verify the result in the operation that follows

This command validates syntax, not existence. A successful check does not prove that a branch exists, that a tag points at an object, or that a remote will accept a push. After validation, use the operation-specific Git command and check its status. For example, to test whether a local branch name already exists without changing anything:

branch_name='feature/report-export'
git check-ref-format --branch "$branch_name" >/dev/null || exit 1
if git show-ref --verify --quiet "refs/heads/$branch_name"; then
    printf 'branch already exists: %s\n' "$branch_name"
else
    printf 'branch name is valid and currently unused: %s\n' "$branch_name"
fi

Safety boundary

Neither example creates or deletes anything. Commands such as git branch -D, git push --delete and ref updates change repository state and need a separate confirmation and recovery plan. Validation is not that confirmation.

Done means

  • Complete refs. You use them with the default one-level rejection unless your interface explicitly allows otherwise.
  • Branch shorthand. You use --branch for it and account for its stricter rules.
  • Canonical input. You capture the output of --normalize when it is required.
  • Wildcards. You reserve --refspec-pattern for genuine refspec patterns.
  • Status and quoting. You check exit status, quote untrusted values, and run a separate existence or remote-operation check.