Skip to content

Commit d47d061

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.
1 parent 852829b commit d47d061

File tree

8 files changed

+139
-17
lines changed

8 files changed

+139
-17
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_addf(buf, "%s", 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: 7 additions & 3 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,18 @@ 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

1007+
add_branch_prefix(start_name, branch_name, &new_branch_name);
1008+
10061009
if (recurse_submodules) {
1007-
create_branches_recursively(the_repository, branch_name,
1010+
create_branches_recursively(the_repository, new_branch_name.buf,
10081011
start_name, NULL, force,
10091012
reflog, quiet, track, 0);
10101013
ret = 0;
10111014
goto out;
10121015
}
1013-
create_branch(the_repository, branch_name, start_name, force, 0,
1014-
reflog, quiet, track, 0);
1016+
create_branch(the_repository, new_branch_name.buf, start_name, force, 0,
1017+
reflog, quiet, track, 0);
1018+
strbuf_release(&new_branch_name);
10151019
} else
10161020
usage_with_options(builtin_branch_usage, options);
10171021

builtin/checkout.c

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,31 +1170,43 @@ 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 };
11781202
struct object_id rev;
1179-
int flag, writeout_error = 0;
1203+
int writeout_error = 0;
11801204
int do_merge = 1;
11811205

11821206
trace2_cmd_mode("branch");
11831207

11841208
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-
}
1209+
get_current_branch_info(&old_branch_info);
11981210

11991211
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
12001212
if (new_branch_info->name)
@@ -1772,6 +1784,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17721784
int parseopt_flags = 0;
17731785
struct branch_info new_branch_info = { 0 };
17741786
int ret;
1787+
struct strbuf full_branch_name = { 0 };
17751788

17761789
opts->overwrite_ignore = 1;
17771790
opts->prefix = prefix;
@@ -1962,7 +1975,15 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
19621975
}
19631976

19641977
if (opts->new_branch) {
1978+
struct branch_info current_branch = { 0 };
19651979
struct strbuf buf = STRBUF_INIT;
1980+
strbuf_init(&full_branch_name, 0);
1981+
1982+
get_current_branch_info(&current_branch);
1983+
add_branch_prefix(current_branch.name, opts->new_branch,
1984+
&full_branch_name);
1985+
branch_info_release(&current_branch);
1986+
opts->new_branch = full_branch_name.buf;
19661987

19671988
if (opts->new_branch_force)
19681989
opts->branch_exists = validate_branchname(opts->new_branch, &buf);
@@ -1981,6 +2002,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
19812002
clear_pathspec(&opts->pathspec);
19822003
free(opts->pathspec_from_file);
19832004
free(options);
2005+
if (full_branch_name.buf)
2006+
strbuf_release(&full_branch_name);
19842007

19852008
return ret;
19862009
}

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)