[PATCH v4 0/2] branch: add prefixes to new branch names
- From
- Yoann Valeri via GitGitGadget <gitgitgadget@gmail.com>
- Date
- Apr 9, 2026, 10:43 UTC
- Message-ID
- <pull.2202.v4.git.git.1775731390.gitgitgadget@gmail.com>
- In-Reply-To
- <pull.2202.v3.git.git.1772802872.gitgitgadget@gmail.com>
This PR adds a way to add prefixes to a new branch being created. The goal is mostly to ease the developer process of creating new branches by adding shortcuts that can be set either with a command-line option or with configuration parameter. This is useful especially when you have to do similar backports on multiple branches, removing a bit of the need for finding names or typing the names over and over again.
One instance of where this feature would be useful is for software stacks based on distribution versions (for instance the Ocean software stack for HPC centers https://ocean.eupex.eu/). Here, stacks for the different versions are organized in git branches, one per versions. Since multiple softwares will go on multiple version branches, we have to create the same package for each of those version branches, which result one work branch per version branch. For instance, if we want to update a package for AlmaLinux 8.10, we have to create another branch specifically for that package and that version. Therefore, if we have a lot of version branches to update, that means double the work branches, and specifying each time the version branch the work branch is based on becomes annoying and can be error prone. That is especially true if many people (around 40 for the Ocean stack) work on this stack.
So I propose this new feature to simplify this process. While it could be done with a script or an alias, both of those require either the developer to share the script or to create it, or to define an alias they may forget. Hence, an even easier way to ease the process is with this configuration parameter which is a one and done deal.
Changes since v1:
* Added a '--no-prefix' option to git branch
Changes since v2:
* Changed the PR structure, with 3 patches: * first patch adds the '--name-prefix' option * second adds the 'branch.namePrefix' configuration parameter * third adds the '--no-name-prefix' option * Those patches only target 'git branch' now
Changes since v3:
* Changed the PR structure, with 2 patches: * first patch adds the '--[no-]name-prefix' option * second adds the 'branch.namePrefix' configuration parameter * Fixed the different issues pointed out by reviewers, especially regarding option/configuration management
VALERI Yoann (2): branch: add '--name-prefix' option branch: add 'branch.namePrefix' config param
Documentation/config/branch.adoc | 6 ++++ Documentation/git-branch.adoc | 11 +++++- branch.c | 57 ++++++++++++++++++++++++++++++++ branch.h | 12 +++++++ builtin/branch.c | 29 ++++++++++++---- t/t3200-branch.sh | 39 ++++++++++++++++++++++ 6 files changed, 146 insertions(+), 8 deletions(-)
base-commit: 256554692df0685b45e60778b08802b720880c50 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2202%2Fvaleriyoann%2Fbranch-with-prefix-v4 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2202/valeriyoann/branch-with-prefix-v4 Pull-Request: https://github.com/git/git/pull/2202
Range-diff vs v3:
1: 6cbb950d8b ! 1: 322f94d121 branch: add '--name-prefix' option
@@ Documentation/git-branch.adoc: git branch [--color[=<when>] | --no-color] [--sho
git branch [--track[=(direct|inherit)] | --no-track] [-f]
- [--recurse-submodules] <branch-name> [<start-point>]
+ [--recurse-submodules] [--name-prefix=<token>]
-+ <branch-name> [<start-point>]
++ <branch-name> [<start-point>]
git branch (--set-upstream-to=<upstream>|-u <upstream>) [<branch-name>]
git branch --unset-upstream [<branch-name>]
git branch (-m|-M) [<old-branch>] <new-branch>
@@ Documentation/git-branch.adoc: Note that this will create the new branch, but it
new branch.
+With a `--name-prefix` option, you can add a prefix to the branch to create.
-+This can either a simple name, or a token. Currently, only '@{current}' is
-+managed as token, and will use the current branch name as prefix.
++This can either be a simple name, or a token. Currently, only '@{current}' is
++supported as token, and will use the current branch name as prefix.
+
When a local branch is started off a remote-tracking branch, Git sets up the
branch (specifically the `branch.<name>.remote` and `branch.<name>.merge`
@@ branch.c: int read_branch_desc(struct strbuf *buf, const char *branch_name)
return 0;
}
-+void add_branch_prefix(const char *name_prefix,
-+ const char *current_branch, struct strbuf *buf)
++static char *get_current_branch_name(void)
+{
-+ int value = 0;
++ const char *const prefix = "refs/heads/";
++ struct object_id rev;
++ const char *p;
++ char *output;
++ char *path;
++ int flag;
+
++ path = refs_resolve_refdup(get_main_ref_store(the_repository),
++ "HEAD", 0, &rev, &flag);
++ if (!path) {
++ warning(_("Failed to get the current branch's path"));
++ return NULL;
++ } else if (!(flag & REF_ISSYMREF)) {
++ FREE_AND_NULL(path);
++ warning(_("Failed to get the current branch's name"));
++ return NULL;
++ }
++
++ if (skip_prefix(path, prefix, &p)) {
++ output = xstrdup(p);
++ free(path);
++ return output;
++ }
++
++ warning(_("Failed to get the current branch's name"));
++ return NULL;
++}
++
++int add_branch_prefix(const char *name_prefix, struct strbuf *buf)
++{
+ if (!name_prefix)
-+ return;
++ return 0;
+
+ if (name_prefix[0] != '@') {
+ strbuf_addstr(buf, name_prefix);
-+ return;
++ return 0;
++ }
++
++ if (strcmp(name_prefix, "@{current}") == 0) {
++ char *current_branch_name = get_current_branch_name();
++
++ if (!current_branch_name)
++ return 1;
++
++ strbuf_addstr(buf, current_branch_name);
++ free(current_branch_name);
++ } else {
++ advise(_("Token '%s' unrecognized, only '@{current}' is managed currently"),
++ name_prefix);
++ return 1;
+ }
+
-+ if (strcmp(name_prefix, "@{current}") == 0)
-+ strbuf_addstr(buf, current_branch);
++ return 0;
+}
+
/*
@@ branch.h: int install_branch_config(int flag, const char *local, const char *ori
+ * string 'name_prefix'. It can either be a simple string to a shorthand
+ * starting with '@'.
+ *
-+ * Currently, only '@{current}' is managed, and will use 'current_branch' as
-+ * prefix.
++ * Currently, only '@{current}' is managed, and will retrieve the current branch
++ * to use as prefix.
++ *
++ * Return 1 if the function failed to set the branch prefix, 0 otherwise.
+ */
-+void add_branch_prefix(const char *name_prefix, const char *current_branch,
-+ struct strbuf *buf);
-+
++int add_branch_prefix(const char *name_prefix, struct strbuf *buf);
+
/*
* Check if a branch is checked out in the main worktree or any linked
@@ builtin/branch.c
@@ builtin/branch.c: int cmd_branch(int argc,
struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
++ char *name_prefix = NULL;
int ret;
-+ const char *name_prefix = NULL;
struct option options[] = {
- OPT_GROUP(N_("Generic options")),
@@ builtin/branch.c: int cmd_branch(int argc,
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_BOOL(0, "recurse-submodules", &recurse_submodules_explicit, N_("recurse through submodules")),
@@ builtin/branch.c: int cmd_branch(int argc,
const char *branch_name = argv[0];
const char *start_name = argc == 2 ? argv[1] : head;
+ struct strbuf new_branch_name = STRBUF_INIT;
++ int rc;
if (filter.kind != FILTER_REFS_BRANCHES)
die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n"
@@ builtin/branch.c: int cmd_branch(int argc,
- if (recurse_submodules) {
- create_branches_recursively(the_repository, branch_name,
-+ add_branch_prefix(name_prefix, start_name, &new_branch_name);
++ rc = add_branch_prefix(name_prefix, &new_branch_name);
++ if (rc)
++ die(_("Failed to add a branch prefix to '%s'"), branch_name);
++
+ strbuf_addstr(&new_branch_name, branch_name);
+
+ if (recurse_submodules)
@@ t/t3200-branch.sh: test_expect_success 'errors if given a bad branch name' '
'
+test_expect_success 'create branch with --name-prefix' '
-+ git config branch.autosetupmerge false &&
++ test_config branch.autosetupmerge false &&
+ git branch branch-with-prefix &&
++ test_ref_exists refs/heads/branch-with-prefix &&
+ git branch --name-prefix "blob" -- -with-prefix &&
++ test_ref_exists refs/heads/blob-with-prefix &&
+ test_must_fail git branch --name-prefix "blob" -- -with-prefix &&
+ git branch --name-prefix "@{current}" -- -with-prefix &&
++ test_ref_exists refs/heads/main-with-prefix &&
+ git switch blob-with-prefix &&
+ git branch --name-prefix "@{current}" -- -with-prefix &&
-+ test_must_fail git branch --name-prefix "@{current}" -- -with-prefix &&
-+ test_ref_exists refs/heads/branch-with-prefix &&
-+ test_ref_exists refs/heads/main-with-prefix &&
-+ test_ref_exists refs/heads/blob-with-prefix &&
+ test_ref_exists refs/heads/blob-with-prefix-with-prefix &&
++ test_must_fail git branch --name-prefix "@{current}" -- -with-prefix &&
++ git branch --name-prefix "blob" --no-name-prefix branch-with-no-prefix &&
++ test_ref_exists refs/heads/branch-with-no-prefix &&
+ git checkout main &&
++ test_config alias.bn "branch --name-prefix=blob" &&
++ git bn --no-name-prefix bn-with-no-prefix &&
++ test_ref_exists refs/heads/bn-with-no-prefix &&
+ git branch -D branch-with-prefix main-with-prefix blob-with-prefix &&
-+ git branch -D blob-with-prefix-with-prefix
++ git branch -D blob-with-prefix-with-prefix branch-with-no-prefix &&
++ git branch -D bn-with-no-prefix
+'
+
test_done
2: d51f71708c ! 2: 80d1ffde9d branch: add 'branch.namePrefix' config param
@@ Documentation/config/branch.adoc: This option defaults to `never`.
+`branch.namePrefix`::
+ When a new branch is created with `git branch`, use the provided value as
+ prefix for its name. Can be '@{current}' to use the current branch's name
-+ as prefix.
++ as prefix. This value can be overriden by using the '--[no-]name-prefix'
++ option of `git branch`.
+
`branch.<name>.remote`::
When on branch _<name>_, it tells `git fetch` and `git push`
which remote to fetch from or push to. The remote to push to
- ## branch.c ##
-@@ branch.c: int read_branch_desc(struct strbuf *buf, const char *branch_name)
- void add_branch_prefix(const char *name_prefix,
- const char *current_branch, struct strbuf *buf)
- {
-- int value = 0;
-+ char *config_prefix = NULL;
+ ## builtin/branch.c ##
+@@ builtin/branch.c: int cmd_branch(int argc,
+ struct ref_format format = REF_FORMAT_INIT;
+ struct repo_config_values *cfg = repo_config_values(the_repository);
+ char *name_prefix = NULL;
++ char *safekeep_name_prefix;
+ int ret;
-- if (!name_prefix)
-- return;
-+ if (!name_prefix) {
-+ if (repo_config_get_string(the_repository, "branch.namePrefix",
-+ &config_prefix))
-+ return;
+ struct option options[] = {
+@@ builtin/branch.c: int cmd_branch(int argc,
+ else if (!skip_prefix(head, "refs/heads/", &head))
+ die(_("HEAD not found below refs/heads!"));
-- if (name_prefix[0] != '@') {
-- strbuf_addstr(buf, name_prefix);
-- return;
-+ name_prefix = config_prefix;
- }
-
-- if (strcmp(name_prefix, "@{current}") == 0)
-+ if (name_prefix[0] != '@')
-+ strbuf_addstr(buf, name_prefix);
-+ else if (strcmp(name_prefix, "@{current}") == 0)
- strbuf_addstr(buf, current_branch);
++ repo_config_get_string(the_repository, "branch.namePrefix", &name_prefix);
++ safekeep_name_prefix = name_prefix;
+
-+ free(config_prefix);
- }
+ argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
+ 0);
+
+@@ builtin/branch.c: int cmd_branch(int argc,
+ ret = 0;
- /*
+ out:
++ free(safekeep_name_prefix);
+ string_list_clear(&sorting_options, 0);
+ return ret;
+ }
## t/t3200-branch.sh ##
@@ t/t3200-branch.sh: test_expect_success 'create branch with --name-prefix' '
- git branch -D blob-with-prefix-with-prefix
+ git branch -D bn-with-no-prefix
'
+test_expect_success 'create branch with config prefix' '
++ test_config branch.autosetupmerge false &&
+ test_config branch.namePrefix blob &&
+ git branch -- -with-prefix &&
++ test_ref_exists refs/heads/blob-with-prefix &&
+ test_must_fail git branch -- -with-prefix &&
+ test_config branch.namePrefix "@{current}" &&
+ git checkout main &&
+ git branch -- -with-prefix &&
-+ test_ref_exists refs/heads/blob-with-prefix &&
+ test_ref_exists refs/heads/main-with-prefix &&
-+ git branch -D blob-with-prefix main-with-prefix
++ git branch --no-name-prefix branch-with-no-prefix &&
++ test_ref_exists refs/heads/branch-with-no-prefix &&
++ git branch -D blob-with-prefix main-with-prefix branch-with-no-prefix
+'
+
test_done
3: 8f45374007 < -: ---------- branch: add '--no-name-prefix' option-- gitgitgadget