Skip to content

Commit 30a6d8e

Browse files
committed
branch: add 'branch.addCurrentBranchAsPrefix' config param
This patch adds a new configuration parameter for the branch creation feature: 'branch.addCurrentBranchAsPrefix'. When set to true, if one creates a new branch with either `git branch`, `git checkout -[bB]` or `git switch -[cC]`, we will now retrieve the current branch's name, and use it as prefix for the name of the newly created branch, alongside a hyphen separating the two. For instance, using this parameter, and attempting to create a branch 'test' while on the 'main' branch will instead create a branch 'main-test'. This parameters is useful for projects handling many branches, with features often needing backport on different branches, so as to reduce the time taken to create branches without having to come up with clever names. Signed-off-by: VALERI Yoann <yoann.valeri@cea.fr>
1 parent 852829b commit 30a6d8e

File tree

8 files changed

+142
-22
lines changed

8 files changed

+142
-22
lines changed

Documentation/config/branch.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ This option defaults to `never`.
3535
value of this variable will be used as the default.
3636
See linkgit:git-for-each-ref[1] field names for valid values.
3737

38+
`branch.addCurrentBranchAsPrefix`::
39+
When a new branch is created with `git branch`, `git switch` or `git
40+
checkout` use the name of the current branch as a prefix for the new
41+
branch's name, alongside the one provided by the user, with a hyphen in the
42+
middle. For instance, using this configuration variable, creating the branch
43+
`test` while on `main` will create the branch `main-test`. False by default.
44+
3845
`branch.<name>.remote`::
3946
When on branch _<name>_, it tells `git fetch` and `git push`
4047
which remote to fetch from or push to. The remote to push to

branch.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,20 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name)
365365
return 0;
366366
}
367367

368+
void add_branch_prefix(const char *current_branch,
369+
const char *target_branch, struct strbuf *buf)
370+
{
371+
int value = 0;
372+
373+
repo_config_get_bool(the_repository,
374+
"branch.addCurrentBranchAsPrefix", &value);
375+
376+
if (value)
377+
strbuf_addf(buf, "%s-%s", current_branch, target_branch);
378+
else
379+
strbuf_addstr(buf, target_branch);
380+
}
381+
368382
/*
369383
* Check if 'name' can be a valid name for a branch; die otherwise.
370384
* Return 1 if the named branch already exists; return 0 otherwise.

branch.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ int install_branch_config(int flag, const char *local, const char *origin, const
148148
*/
149149
int read_branch_desc(struct strbuf *, const char *branch_name);
150150

151+
/*
152+
* Fetch the configuration parameter 'branch.addCurrentBranchAsPrefix' and
153+
* fill the buffer 'buf' with '<current_branch>-<target_branch>' if true,
154+
* otherwise just '<current_branch>'.
155+
*/
156+
void add_branch_prefix(const char *current_branch,
157+
const char *target_branch, struct strbuf *buf);
158+
151159
/*
152160
* Check if a branch is checked out in the main worktree or any linked
153161
* worktree and die (with a message describing its checkout location) if

builtin/branch.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,7 @@ int cmd_branch(int argc,
995995
} else if (!noncreate_actions && argc > 0 && argc <= 2) {
996996
const char *branch_name = argv[0];
997997
const char *start_name = argc == 2 ? argv[1] : head;
998+
struct strbuf new_branch_name = STRBUF_INIT;
998999

9991000
if (filter.kind != FILTER_REFS_BRANCHES)
10001001
die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n"
@@ -1003,15 +1004,17 @@ int cmd_branch(int argc,
10031004
if (track == BRANCH_TRACK_OVERRIDE)
10041005
die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead"));
10051006

1006-
if (recurse_submodules) {
1007-
create_branches_recursively(the_repository, branch_name,
1007+
add_branch_prefix(start_name, branch_name, &new_branch_name);
1008+
1009+
if (recurse_submodules)
1010+
create_branches_recursively(the_repository, new_branch_name.buf,
10081011
start_name, NULL, force,
10091012
reflog, quiet, track, 0);
1010-
ret = 0;
1011-
goto out;
1012-
}
1013-
create_branch(the_repository, branch_name, start_name, force, 0,
1014-
reflog, quiet, track, 0);
1013+
else
1014+
create_branch(the_repository, new_branch_name.buf, start_name,
1015+
force, 0, reflog, quiet, track, 0);
1016+
1017+
strbuf_release(&new_branch_name);
10151018
} else
10161019
usage_with_options(builtin_branch_usage, options);
10171020

builtin/checkout.c

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,31 +1170,42 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
11701170
release_revisions(&revs);
11711171
}
11721172

1173+
static void get_current_branch_info(struct branch_info *branch_info)
1174+
{
1175+
struct object_id rev;
1176+
int flag;
1177+
1178+
branch_info->path = refs_resolve_refdup(get_main_ref_store(the_repository),
1179+
"HEAD", 0, &rev, &flag);
1180+
1181+
if (branch_info->path)
1182+
branch_info->commit = lookup_commit_reference_gently(the_repository,
1183+
&rev, 1);
1184+
1185+
if (!(flag & REF_ISSYMREF))
1186+
FREE_AND_NULL(branch_info->path);
1187+
1188+
if (branch_info->path) {
1189+
const char *const prefix = "refs/heads/";
1190+
const char *p;
1191+
1192+
if (skip_prefix(branch_info->path, prefix, &p))
1193+
branch_info->name = xstrdup(p);
1194+
}
1195+
}
1196+
11731197
static int switch_branches(const struct checkout_opts *opts,
11741198
struct branch_info *new_branch_info)
11751199
{
11761200
int ret = 0;
11771201
struct branch_info old_branch_info = { 0 };
1178-
struct object_id rev;
1179-
int flag, writeout_error = 0;
1202+
int writeout_error = 0;
11801203
int do_merge = 1;
11811204

11821205
trace2_cmd_mode("branch");
11831206

11841207
memset(&old_branch_info, 0, sizeof(old_branch_info));
1185-
old_branch_info.path = refs_resolve_refdup(get_main_ref_store(the_repository),
1186-
"HEAD", 0, &rev, &flag);
1187-
if (old_branch_info.path)
1188-
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
1189-
if (!(flag & REF_ISSYMREF))
1190-
FREE_AND_NULL(old_branch_info.path);
1191-
1192-
if (old_branch_info.path) {
1193-
const char *const prefix = "refs/heads/";
1194-
const char *p;
1195-
if (skip_prefix(old_branch_info.path, prefix, &p))
1196-
old_branch_info.name = xstrdup(p);
1197-
}
1208+
get_current_branch_info(&old_branch_info);
11981209

11991210
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
12001211
if (new_branch_info->name)
@@ -1772,6 +1783,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17721783
int parseopt_flags = 0;
17731784
struct branch_info new_branch_info = { 0 };
17741785
int ret;
1786+
struct strbuf full_branch_name = { 0 };
17751787

17761788
opts->overwrite_ignore = 1;
17771789
opts->prefix = prefix;
@@ -1962,7 +1974,15 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
19621974
}
19631975

19641976
if (opts->new_branch) {
1977+
struct branch_info current_branch = { 0 };
19651978
struct strbuf buf = STRBUF_INIT;
1979+
strbuf_init(&full_branch_name, 0);
1980+
1981+
get_current_branch_info(&current_branch);
1982+
add_branch_prefix(current_branch.name, opts->new_branch,
1983+
&full_branch_name);
1984+
branch_info_release(&current_branch);
1985+
opts->new_branch = full_branch_name.buf;
19661986

19671987
if (opts->new_branch_force)
19681988
opts->branch_exists = validate_branchname(opts->new_branch, &buf);
@@ -1981,6 +2001,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
19812001
clear_pathspec(&opts->pathspec);
19822002
free(opts->pathspec_from_file);
19832003
free(options);
2004+
if (full_branch_name.buf)
2005+
strbuf_release(&full_branch_name);
19842006

19852007
return ret;
19862008
}

t/t2018-checkout-branch.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,30 @@ test_expect_success 'checkout -b rejects an extra path argument' '
285285
test_grep "Cannot update paths and switch to branch" err
286286
'
287287

288+
test_expect_success 'checkout -b with prefix is valid' '
289+
git checkout -b main &&
290+
git checkout -b checkoutb-with-prefix &&
291+
git checkout main &&
292+
test_config branch.addCurrentBranchAsPrefix false &&
293+
test_must_fail git checkout -b checkoutb-with-prefix &&
294+
test_config branch.addCurrentBranchAsPrefix true &&
295+
git checkout -b checkoutb-with-prefix &&
296+
git checkout -b checkoutb-with-prefix &&
297+
test_ref_exists refs/heads/checkoutb-with-prefix &&
298+
test_ref_exists refs/heads/main-checkoutb-with-prefix &&
299+
test_ref_exists refs/heads/main-checkoutb-with-prefix-checkoutb-with-prefix
300+
'
301+
302+
test_expect_success 'checkout -B with prefix is valid' '
303+
git checkout main &&
304+
git checkout -B checkoutB-with-prefix &&
305+
git checkout main &&
306+
test_config branch.addCurrentBranchAsPrefix false &&
307+
git checkout -B checkoutB-with-prefix &&
308+
test_config branch.addCurrentBranchAsPrefix true &&
309+
git checkout -B checkoutB-with-prefix &&
310+
test_ref_exists refs/heads/checkoutB-with-prefix &&
311+
test_ref_exists refs/heads/checkoutB-with-prefix-checkoutB-with-prefix
312+
'
313+
288314
test_done

t/t2060-switch.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,30 @@ test_expect_success 'switch back when temporarily detached and checked out elsew
177177
git -C wt2 switch --ignore-other-worktrees shared
178178
'
179179

180+
test_expect_success 'switch -c with prefix is valid' '
181+
git switch main &&
182+
git switch -c switchc-with-prefix &&
183+
git checkout main &&
184+
test_config branch.addCurrentBranchAsPrefix false &&
185+
test_must_fail git switch -c switchc-with-prefix &&
186+
test_config branch.addCurrentBranchAsPrefix true &&
187+
git switch -c switchc-with-prefix &&
188+
git switch -c switchc-with-prefix &&
189+
test_ref_exists refs/heads/switchc-with-prefix &&
190+
test_ref_exists refs/heads/main-switchc-with-prefix &&
191+
test_ref_exists refs/heads/main-switchc-with-prefix-switchc-with-prefix
192+
'
193+
194+
test_expect_success 'switch -C with prefix is valid' '
195+
git switch main &&
196+
git switch -C switchC-with-prefix &&
197+
git checkout main &&
198+
test_config branch.addCurrentBranchAsPrefix false &&
199+
git switch -C switchC-with-prefix &&
200+
test_config branch.addCurrentBranchAsPrefix true &&
201+
git switch -C switchC-with-prefix &&
202+
test_ref_exists refs/heads/switchC-with-prefix &&
203+
test_ref_exists refs/heads/switchC-with-prefix-switchC-with-prefix
204+
'
205+
180206
test_done

t/t3200-branch.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,4 +1716,18 @@ test_expect_success 'errors if given a bad branch name' '
17161716
test_cmp expect actual
17171717
'
17181718

1719+
test_expect_success 'create branch with prefix' '
1720+
git config branch.autosetupmerge false &&
1721+
git branch branch-with-prefix &&
1722+
test_config branch.addCurrentBranchAsPrefix false &&
1723+
test_must_fail git branch branch-with-prefix &&
1724+
test_config branch.addCurrentBranchAsPrefix true &&
1725+
git branch branch-with-prefix &&
1726+
git checkout branch-with-prefix &&
1727+
git branch branch-with-prefix &&
1728+
test_ref_exists refs/heads/branch-with-prefix &&
1729+
test_ref_exists refs/heads/main-branch-with-prefix &&
1730+
test_ref_exists refs/heads/branch-with-prefix-branch-with-prefix
1731+
'
1732+
17191733
test_done

0 commit comments

Comments
 (0)