From 51404e978169644d91f6cf189b49098224374184 Mon Sep 17 00:00:00 2001 From: Andrea Stacchiotti Date: Mon, 9 Jun 2025 21:33:08 +0200 Subject: [PATCH] branch: move multiple branches in a single --force Using either the 1-arg or 2-args form of --force it is possible to only move one branch at a time, to HEAD and respectively. Allow moving multiple branches to a single target by giving 'git branch --force b1 b2 b3 ... dest' cp-like semantics, all the branches are moved/created to 'dest'. The convention extends the 2-args form in the same way 'cp a b c ... dest' would do. There could be another potential interpretation of `--force a b c`: moving all 3 to HEAD, but the 2-args form already changed the semantics to cp-like instead of appending an implicit HEAD, so this seems the least surprising way to support multiple moves. No such change is done to the move/copy paths, as such paths would error out anyway by trying to create multiple branches of the same name. Signed-off-by: Andrea Stacchiotti --- Documentation/git-branch.adoc | 2 +- builtin/branch.c | 26 ++++++++++++++------------ t/t3200-branch.sh | 7 +++++++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc index c0afddc424d610..817b4a5d3f6254 100644 --- a/Documentation/git-branch.adoc +++ b/Documentation/git-branch.adoc @@ -17,7 +17,7 @@ git branch [--color[=] | --no-color] [--show-current] [(-r|--remotes) | (-a|--all)] [--list] [...] git branch [--track[=(direct|inherit)] | --no-track] [-f] - [--recurse-submodules] [] + [--recurse-submodules] ... [] git branch (--set-upstream-to=|-u ) [] git branch --unset-upstream [] git branch (-m|-M) [] diff --git a/builtin/branch.c b/builtin/branch.c index c150131bd9f122..8ba04568c15548 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -31,7 +31,7 @@ static const char * const builtin_branch_usage[] = { N_("git branch [] [-r | -a] [--merged] [--no-merged]"), - N_("git branch [] [-f] [--recurse-submodules] []"), + N_("git branch [] [-f] [--recurse-submodules] ... []"), N_("git branch [] [-l] [...]"), N_("git branch [] [-r] (-d | -D) ..."), N_("git branch [] (-m | -M) [] "), @@ -992,9 +992,9 @@ int cmd_branch(int argc, strbuf_addf(&buf, "branch.%s.merge", branch->name); git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_release(&buf); - } else if (!noncreate_actions && argc > 0 && argc <= 2) { - const char *branch_name = argv[0]; - const char *start_name = argc == 2 ? argv[1] : head; + } else if (!noncreate_actions && argc > 0) { + const char *start_name = argc == 1 ? head : argv[argc - 1]; + int iters = argc == 1 ? 1 : argc - 1; if (filter.kind != FILTER_REFS_BRANCHES) die(_("the -a, and -r, options to 'git branch' do not take a branch name.\n" @@ -1003,15 +1003,17 @@ 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, - start_name, NULL, force, - reflog, quiet, track, 0); - ret = 0; - goto out; + for (int i = 0; i < iters; i++) { + const char *branch_name = argv[i]; + + if (recurse_submodules) + create_branches_recursively(the_repository, branch_name, + start_name, NULL, force, + reflog, quiet, track, 0); + else + create_branch(the_repository, branch_name, start_name, force, 0, + reflog, quiet, track, 0); } - create_branch(the_repository, branch_name, start_name, force, 0, - reflog, quiet, track, 0); } else usage_with_options(builtin_branch_usage, options); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index f3e720dc10da46..7cd31ca782089f 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -60,6 +60,13 @@ test_expect_success 'git branch --force abc should succeed when abc exists' ' test_cmp expect actual ' +test_expect_success 'git branch --force br1 br2 abc should create 2 new branches' ' + git branch --force br1 br2 abc && + test_ref_exists refs/heads/br1 && + test_ref_exists refs/heads/br2 && + git branch -d br1 br2 +' + test_expect_success 'git branch a/b/c should create a branch' ' git branch a/b/c && test_ref_exists refs/heads/a/b/c