Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Documentation/config/branch.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ This option defaults to `never`.
value of this variable will be used as the default.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eric Sunshine wrote on the Git mailing list (how to reply to this email):

On Fri, Mar 6, 2026 at 8:15 AM VALERI Yoann via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> This patch adds a new configuration parameter for the branch creation
> feature: 'branch.namePrefix'. It corresponds to the '--name-prefix'
> option of 'git branch' made as configuration parameter, and behaves
> exactly like it.
>
> Signed-off-by: VALERI Yoann <yoann.valeri@cea.fr>
> ---
> diff --git a/Documentation/config/branch.adoc b/Documentation/config/branch.adoc
> @@ -35,6 +35,11 @@ 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.

This probably ought to mention --[no]-name-prefix to let the user know
that the configuration value can be overridden.

> diff --git a/branch.c b/branch.c
> @@ -368,18 +368,22 @@ 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;
>
> -       if (!name_prefix)
> -               return;
> +       if (!name_prefix) {
> +               if (repo_config_get_string(the_repository, "branch.namePrefix",
> +                                                                  &config_prefix))
> +                       return;
>
> -       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);
> +
> +    free(config_prefix);
>  }

This "diff" is very difficult to read because it's rewriting much of
the logic which was first introduced in patch [1/3]. When you reroll,
it would be a good idea to get the overall logic straight in patch
[1/3] so that subsequent patches only make small changes to it to
improve it (if necessary).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"VALERI Yoann via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +`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. This value can be overriden by using the '--[no-]name-prefix'
> +	option of `git branch`.

Even after reading the cover letter, I am not convinced why this is
a good idea in the first place.

Also, I am not sure if "current branch" really captures what you are
trying to achieve.  When "git branch foo" creates a new branch "foo"
out of _the_ _current_ branch, i.e.

    $ git checkout base
    $ git branch foo

with the nameprefix configuration, it may appear that it is doing
what you want by deriving the name of the new branch taking both
"base" and "foo" into account, but what would you want to happen if
you did this instead:

    $ git checkout base
    $ git branch foo

or while you may be on an unrelated "master" branch, create and
switch to the new branch:

    $ git checkout -b foo base

whose name takes both "base" and "foo" into account.

What I am getting at is that "the current branch" is missing the
point and it may be "the branch the new branch was forked off of"
that is what you really are trying to find that "base" in.

Having said all that, I am fairly negative on this new feature,
simply because it is too much magic and will be confusing,
especially when invoked with a configuration variable.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"VALERI Yoann via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +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 &&

Another thing I noticed.

Wouldn't you rather want to have a mechanism where the user does not
have to prefix the end-user supplied value ("with-prefix" in this
case) without leading dash?  From the look of what you are trying to
configure with the variable, the desire seems to be to allow the
user to make many branches that share the same prefix, so
conceptually, among blob-master, blob-maint, blob-feature branches
that would result in "git branch $name" where name is taken from the
vocabulary (master, maint, feature), the responsibility to supply
the word-separating hyphen belongs to either the prefix side (i.e.,
"blob-") or the mechanism that does prefising (i.e., the prefix
configuration @{current} comes up with a prefix string "blob", and
with the end-user supplied value of "with-prefix", the mechanism
that pastes these two strings makes the result "blob-with-prefix",
supplying the hyphen between "blob" and "with-prefix".

I said that I am negative in my previous message, simply because I
didn't get the intended use case at all, but even if there were a
good use case explained in future messages to make me change my
mind, I doubt that a design that requires the hyphen in front of
"with-prefix" the end-user is supposed to supply is a good thing to
have.

Thanks.

See linkgit:git-for-each-ref[1] field names for valid values.

`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. 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
Expand Down
11 changes: 10 additions & 1 deletion Documentation/git-branch.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ git branch [--color[=<when>] | --no-color] [--show-current]
[(-r|--remotes) | (-a|--all)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eric Sunshine wrote on the Git mailing list (how to reply to this email):

On Fri, Mar 6, 2026 at 8:15 AM VALERI Yoann via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> This patch adds a '--name-prefix' option to add a prefix to a newly
> created branch. It can use a regular string or a token as prefix. The
> only token currently handled is '@{current}', which is substituted for
> the current branch's name.
>
> Signed-off-by: VALERI Yoann <yoann.valeri@cea.fr>
> ---
> diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
> @@ -64,6 +65,10 @@ Note that this will create the new branch, but it will not switch the
> +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.

s/This can/& be/

> diff --git a/branch.c b/branch.c
> @@ -365,6 +365,23 @@ 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;

What is `value`? It doesn't seem to be used at all in this function.

> +       if (!name_prefix)
> +               return;
> +
> +       if (name_prefix[0] != '@') {
> +               strbuf_addstr(buf, name_prefix);
> +               return;
> +       }
> +
> +       if (strcmp(name_prefix, "@{current}") == 0)
> +               strbuf_addstr(buf, current_branch);
> +}

I would expect this function to produce some sort of diagnostic
warning when the user has given it a "@{token}" it doesn't recognize.

> diff --git a/branch.h b/branch.h
> @@ -148,6 +148,18 @@ int install_branch_config(int flag, const char *local, const char *origin, const
> +/*
> + * Store in 'buf' a prefix to the name of a branch to create by using the given
> + * 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.
> + */
> +void add_branch_prefix(const char *name_prefix, const char *current_branch,
> +                                          struct strbuf *buf);

It feels unnecessarily burdensome to force the caller to compute and
pass in `current_branch`. Intuitively, one would expect
add_branch_prefix() to compute the current branch itself if it
discovers that "@{current}" has been requested.

Moveover, this approach will not scale well when support for
additional "@{tokens}" is added down the road since it burdens *all*
callers with providing the values for *all* possible tokens.

> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
> @@ -1716,4 +1716,22 @@ test_expect_success 'errors if given a bad branch name' '
> +test_expect_success 'create branch with --name-prefix' '
> +       git config branch.autosetupmerge false &&

Let's use `test_config` which will ensure that this setting is
reverted at the end of the test.


> +       git branch branch-with-prefix &&
> +       git branch --name-prefix "blob" -- -with-prefix &&
> +       test_must_fail git branch --name-prefix "blob" -- -with-prefix &&
> +       git branch --name-prefix "@{current}" -- -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 &&
> +       git checkout main &&
> +       git branch -D branch-with-prefix main-with-prefix blob-with-prefix &&
> +       git branch -D blob-with-prefix-with-prefix
> +'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"VALERI Yoann via GitGitGadget" <gitgitgadget@gmail.com> writes:

> diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
> index c0afddc424..00967fa758 100644
> --- a/Documentation/git-branch.adoc
> +++ b/Documentation/git-branch.adoc
> @@ -17,7 +17,8 @@ git branch [--color[=<when>] | --no-color] [--show-current]
>  	   [(-r|--remotes) | (-a|--all)]
>  	   [--list] [<pattern>...]
>  git branch [--track[=(direct|inherit)] | --no-track] [-f]
> -	   [--recurse-submodules] <branch-name> [<start-point>]
> +	   [--recurse-submodules] [--name-prefix=<token>]
> +           <branch-name> [<start-point>]

The indentation of the last line seems a bit off; the previous line
uses leading HT both in the original and in the updated version, but
the last line uses SP indent.

> @@ -64,6 +65,10 @@ Note that this will create the new branch, but it will not switch the
>  working tree to it; use `git switch <new-branch>` to switch to the
>  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.

"can either" -> "can either be".
"managed" -> "supported".

> @@ -319,6 +324,10 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
>  	and the object it points at.  _<format>_ is the same as
>  	that of linkgit:git-for-each-ref[1].
>  
> +`--name-prefix <token>`::
> +	A string that will be used as prefix to the name of the new branch to
> +        create. Can be '@{current}' to use the current branch's name.
> +
>  _<branch-name>_::
>  	The name of the branch to create or delete.
>  	The new branch name must pass all checks defined by
> diff --git a/branch.c b/branch.c
> index 243db7d0fc..c24d7ce823 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -365,6 +365,23 @@ 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)

Overly deep indentation here.  Our tab-width is always 8, and you
would align the first "const char" on these two lines.

> +{
> +	int value = 0;

Unused variable?

> +	if (!name_prefix)
> +		return;
> +
> +	if (name_prefix[0] != '@') {
> +		strbuf_addstr(buf, name_prefix);
> +		return;
> +	}
> +
> +	if (strcmp(name_prefix, "@{current}") == 0)
> +		strbuf_addstr(buf, current_branch);
> +}

What happens when we need to support more than the @{current}?  Will
this function grow more parameters and the callers need to prepare
more parameters, even if only one of them may be picked by this
function?  That does not smell like a sound way to make things
maintainable.

> diff --git a/builtin/branch.c b/builtin/branch.c
> index c577b5d20f..58631913c7 100644
> --- a/builtin/branch.c
> +++ b/builtin/branch.c
> ...
> +		add_branch_prefix(name_prefix, start_name, &new_branch_name);


Here, `start_name` is passed as `current_branch` to `add_branch_prefix`.
However, `start_name` is the `<start-point>` (e.g., another branch,
a tag, or a commit). If the user runs:

    $ git branch --name-prefix=@{current} new-branch other-branch

the prefix will be `other-branch` instead of the *current* branch
name as advertised in the documentation.  It _may_ be how the
feature was intended to work, but then the name "current" and the
way the documentation describes the token are both misleading.

> +			create_branch(the_repository, new_branch_name.buf, start_name,
> +						  force, 0, reflog, quiet, track, 0);

Overly deep indentation.

[--list] [<pattern>...]
git branch [--track[=(direct|inherit)] | --no-track] [-f]
[--recurse-submodules] <branch-name> [<start-point>]
[--recurse-submodules] [--name-prefix=<token>]
<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>
Expand Down Expand Up @@ -64,6 +65,10 @@ Note that this will create the new branch, but it will not switch the
working tree to it; use `git switch <new-branch>` to switch to the
new branch.

With a `--name-prefix` option, you can add a prefix to the branch to create.
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`
configuration entries) so that `git pull` will appropriately merge from
Expand Down Expand Up @@ -319,6 +324,10 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
and the object it points at. _<format>_ is the same as
that of linkgit:git-for-each-ref[1].

`--name-prefix <token>`::
A string that will be used as prefix to the name of the new branch to
create. Can be '@{current}' to use the current branch's name.

_<branch-name>_::
The name of the branch to create or delete.
The new branch name must pass all checks defined by
Expand Down
57 changes: 57 additions & 0 deletions branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,63 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
return 0;
}

static char *get_current_branch_name(void)
{
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 0;

if (name_prefix[0] != '@') {
strbuf_addstr(buf, name_prefix);
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;
}

return 0;
}

/*
* Check if 'name' can be a valid name for a branch; die otherwise.
* Return 1 if the named branch already exists; return 0 otherwise.
Expand Down
12 changes: 12 additions & 0 deletions branch.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ int install_branch_config(int flag, const char *local, const char *origin, const
*/
int read_branch_desc(struct strbuf *, const char *branch_name);

/*
* Store in 'buf' a prefix to the name of a branch to create by using the given
* string 'name_prefix'. It can either be a simple string to a shorthand
* starting with '@'.
*
* 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.
*/
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
* worktree and die (with a message describing its checkout location) if
Expand Down
29 changes: 22 additions & 7 deletions builtin/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,8 @@ 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;
char *safekeep_name_prefix;
int ret;

struct option options[] = {
Expand Down Expand Up @@ -777,6 +779,7 @@ 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")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_STRING(0, "name-prefix", &name_prefix, N_("name"), N_("prefix for the branch to create")),
OPT_END(),
};

Expand Down Expand Up @@ -807,6 +810,9 @@ int cmd_branch(int argc,
else if (!skip_prefix(head, "refs/heads/", &head))
die(_("HEAD not found below refs/heads!"));

repo_config_get_string(the_repository, "branch.namePrefix", &name_prefix);
safekeep_name_prefix = name_prefix;

argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
0);

Expand Down Expand Up @@ -996,6 +1002,8 @@ int cmd_branch(int argc,
} else if (!noncreate_actions && argc > 0 && argc <= 2) {
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"
Expand All @@ -1004,21 +1012,28 @@ int cmd_branch(int argc,
if (track == BRANCH_TRACK_OVERRIDE)
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead"));

if (recurse_submodules) {
create_branches_recursively(the_repository, 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)
create_branches_recursively(the_repository, new_branch_name.buf,
start_name, NULL, force,
reflog, quiet, track, 0);
ret = 0;
goto out;
}
create_branch(the_repository, branch_name, start_name, force, 0,
reflog, quiet, track, 0);
else
create_branch(the_repository, new_branch_name.buf, start_name,
force, 0, reflog, quiet, track, 0);

strbuf_release(&new_branch_name);
} else
usage_with_options(builtin_branch_usage, options);

ret = 0;

out:
free(safekeep_name_prefix);
string_list_clear(&sorting_options, 0);
return ret;
}
39 changes: 39 additions & 0 deletions t/t3200-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1717,4 +1717,43 @@ test_expect_success 'errors if given a bad branch name' '
test_cmp expect actual
'

test_expect_success 'create branch with --name-prefix' '
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_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 branch-with-no-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/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
Loading