public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] branch: move multiple branches in a single --force
@ 2025-06-10  9:07 Andrea Stacchiotti via GitGitGadget
  2025-06-10 21:25 ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Andrea Stacchiotti via GitGitGadget @ 2025-06-10  9:07 UTC (permalink / raw)
  To: git; +Cc: Andrea Stacchiotti, Andrea Stacchiotti

From: Andrea Stacchiotti <andreastacchiotti@gmail.com>

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 <arg2> 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 <andreastacchiotti@gmail.com>
---
    branch: Move multiple branches in a single --force

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1992%2F12345ieee%2Fmaster-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1992/12345ieee/master-v1
Pull-Request: https://github.com/git/git/pull/1992

 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 c0afddc424d..817b4a5d3f6 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -17,7 +17,7 @@ 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] <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>
diff --git a/builtin/branch.c b/builtin/branch.c
index c150131bd9f..8ba04568c15 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -31,7 +31,7 @@
 
 static const char * const builtin_branch_usage[] = {
 	N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"),
-	N_("git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-point>]"),
+	N_("git branch [<options>] [-f] [--recurse-submodules] <branch-name>... [<start-point>]"),
 	N_("git branch [<options>] [-l] [<pattern>...]"),
 	N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
 	N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
@@ -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 f3e720dc10d..7cd31ca7820 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

base-commit: 4c0e625c091d4c648cec7319bafaed3cc81658e5
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-10  9:07 [PATCH] branch: move multiple branches in a single --force Andrea Stacchiotti via GitGitGadget
@ 2025-06-10 21:25 ` Junio C Hamano
  2025-06-10 22:17   ` Andrea Stacchiotti
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2025-06-10 21:25 UTC (permalink / raw)
  To: Andrea Stacchiotti via GitGitGadget; +Cc: git, Andrea Stacchiotti

"Andrea Stacchiotti via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> From: Andrea Stacchiotti <andreastacchiotti@gmail.com>
>
> 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 <arg2> respectively.

If you are renaming (or "moving") a branch that is not checked out
anywhere to a new name that is not in use, you do not even need to
force.  You can just do:

    git branch -m old new

You are not moving branches without "-m".

What you are doing is to point a branch A to point at a commit X
with

    git branch A X

Your proposed log message talks about "--force" too much; if you are
creating a branch, you need "--force" only when the name you want to
use is already taken.  Pointing the branch tip to a commit is not
inherently tied to "--force", but your description gives a false
impression that you are adding a special feature when "--force" is
used.  The proposed log message needs rewritten.

If there is not yet a branch A, you do not even need "--force" on
this command line.  Also take a special note that "X" does not have
to be a branch name.  It only has to resolve to a commit, so this is
also valid:

    git branch [--force] A X~4

I can understand that it may appear to be handy to be able to set
multiple branches at the same time with

    git branch A X~4 B X~3 C X~2		(* does not exist *)

with or without "--force".  If none of A, B, or C exist, they can be
created from these three comits X~4, X~3, and X~2.

Or you could propose a different syntax to create branches pointing
at the same commit

    git branch A B C origin/master		(* does not exist *)

But either syntax to create multiple branches feel somewhat
inadequate.  What should happen to their associated configuration
data like branch.A.remote and branch.B.merge?  Should they all point
at the same remote & a branch at the remote?  How would that make
having multiple of them useful?


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-10 21:25 ` Junio C Hamano
@ 2025-06-10 22:17   ` Andrea Stacchiotti
  2025-06-11  0:22     ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Andrea Stacchiotti @ 2025-06-10 22:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Andrea Stacchiotti via GitGitGadget, git

Thank you for your review, the concerns are fair, I'll reply point-to-point.

The general issue is that AFAIK the only way git has to say:
"point non-checked-out branch B to commit-ish X, leave B's config intact,
if B doesn't exist create it" is `git branch --force B X`.

This is likely an abuse of `branch -f`, it might be better giving it
another flag
altogether, like `-p/--point-to`.

`branch --force` is massively overloaded and most of its functionality is
tied to `--move` or `--copy`, so changing branch names, not repointing branches.

This patch aims to make repointing multiple branches to the same commit-ish
easier, currently it needs a shell loop.

I'd like this functionality for gitops repos, where each branch is an env
and I want to update dozens of envs to the last config state.

Il giorno mar 10 giu 2025 alle ore 23:25 Junio C Hamano
<gitster@pobox.com> ha scritto:
>
> "Andrea Stacchiotti via GitGitGadget" <gitgitgadget@gmail.com>
> writes:
>
> > From: Andrea Stacchiotti <andreastacchiotti@gmail.com>
> >
> > 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 <arg2> respectively.
>
> If you are renaming (or "moving") a branch that is not checked out
> anywhere to a new name that is not in use, you do not even need to
> force.  You can just do:
>
>     git branch -m old new
>
> You are not moving branches without "-m".
>
> What you are doing is to point a branch A to point at a commit X
> with
>
>     git branch A X

Indeed, this is about repointing, not changing branch names.

> Your proposed log message talks about "--force" too much; if you are
> creating a branch, you need "--force" only when the name you want to
> use is already taken.  Pointing the branch tip to a commit is not
> inherently tied to "--force", but your description gives a false
> impression that you are adding a special feature when "--force" is
> used.  The proposed log message needs rewritten.

Right, it's that I'm using "force create branch A onto X, delete A if
it existed"
as "repoint branch A to X, create it if needed", I likely have a skewed
view on it.
This usecase might be better served by a new flag.

> If there is not yet a branch A, you do not even need "--force" on
> this command line.  Also take a special note that "X" does not have
> to be a branch name.  It only has to resolve to a commit, so this is
> also valid:
>
>     git branch [--force] A X~4

Yeah, it was not clear from my message, nothing about the
change is about renaming a branch, my usecase is about
repointing to a generic commit-ish, like you say.

> I can understand that it may appear to be handy to be able to set
> multiple branches at the same time with
>
>     git branch A X~4 B X~3 C X~2                (* does not exist *)
>
> with or without "--force".  If none of A, B, or C exist, they can be
> created from these three commits X~4, X~3, and X~2.

I'm not opposed to implement this if it's preferred, but I liked
the cp-like syntax as it's less repeating and the average shell user
is already exposed to it via cp or mv.

> Or you could propose a different syntax to create branches pointing
> at the same commit
>
>     git branch A B C origin/master              (* does not exist *)
>
> But either syntax to create multiple branches feel somewhat
> inadequate.  What should happen to their associated configuration
> data like branch.A.remote and branch.B.merge?  Should they all point
> at the same remote & a branch at the remote?  How would that make
> having multiple of them useful?
>
This is closer to the intended syntax and use of the feature, I'm more
focused on branch repoints but I'd also use this when I create, say,
the staging and prod env of client newclient via
`git branch [-f] newclient-prod newclient-stag <commit-ish>`

The branches are useful because they are then supposed to evolve
independently and track the real world state of the two envs.

If <commit-ish> is a remote branch the implementation sets up
tracking, but this is really not the intended usecase, once
the branches are created their remote tracking
is set at push time to <default remote>/<branch name> as per
default push config.

Thanks for your review, I hope my intent is clearer.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-10 22:17   ` Andrea Stacchiotti
@ 2025-06-11  0:22     ` Junio C Hamano
  2025-06-11  8:34       ` Andrea Stacchiotti
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2025-06-11  0:22 UTC (permalink / raw)
  To: Andrea Stacchiotti; +Cc: Andrea Stacchiotti via GitGitGadget, git

Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:

> This patch aims to make repointing multiple branches to the same commit-ish
> easier, currently it needs a shell loop.

Or "update-ref --stdin"?

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-11  0:22     ` Junio C Hamano
@ 2025-06-11  8:34       ` Andrea Stacchiotti
  2025-06-11 15:26         ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Andrea Stacchiotti @ 2025-06-11  8:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Andrea Stacchiotti via GitGitGadget, git

Il giorno mer 11 giu 2025 alle ore 02:22 Junio C Hamano
<gitster@pobox.com> ha scritto:
>
> Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:
>
> > This patch aims to make repointing multiple branches to the same commit-ish
> > easier, currently it needs a shell loop.
>
> Or "update-ref --stdin"?

I learned something new, but I'd still like to keep advocating for a syntax
like `branch --some-flag A B C X` instead of feeding by hand
update-ref commands.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-11  8:34       ` Andrea Stacchiotti
@ 2025-06-11 15:26         ` Junio C Hamano
  2025-06-12  0:19           ` Andrea Stacchiotti
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2025-06-11 15:26 UTC (permalink / raw)
  To: Andrea Stacchiotti; +Cc: Andrea Stacchiotti via GitGitGadget, git

Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:

> Il giorno mer 11 giu 2025 alle ore 02:22 Junio C Hamano
> <gitster@pobox.com> ha scritto:
>>
>> Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:
>>
>> > This patch aims to make repointing multiple branches to the same commit-ish
>> > easier, currently it needs a shell loop.
>>
>> Or "update-ref --stdin"?
>
> I learned something new, but I'd still like to keep advocating for a syntax
> like `branch --some-flag A B C X` instead of feeding by hand
> update-ref commands.

I am personally not interested in such a mode, I do not know why you
think "--some-flag" is needed when the command can figure out from
the number of things on the command line being more than 2 just
fine.

But my comment was targetted against "it needs a shell loop" in the
justification in the proposed log message, which is not quite
correct.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-11 15:26         ` Junio C Hamano
@ 2025-06-12  0:19           ` Andrea Stacchiotti
  2025-06-12  9:55             ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Andrea Stacchiotti @ 2025-06-12  0:19 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Andrea Stacchiotti via GitGitGadget, git

So, if I may ask, is the proposed patch as written (branch -f A B C X)
acceptable and you just need me to rewrite the commit message
or are you not interested in it at all?

Il giorno mer 11 giu 2025 alle ore 17:26 Junio C Hamano
<gitster@pobox.com> ha scritto:
>
> Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:
>
> > Il giorno mer 11 giu 2025 alle ore 02:22 Junio C Hamano
> > <gitster@pobox.com> ha scritto:
> >>
> >> Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:
> >>
> >> > This patch aims to make repointing multiple branches to the same commit-ish
> >> > easier, currently it needs a shell loop.
> >>
> >> Or "update-ref --stdin"?
> >
> > I learned something new, but I'd still like to keep advocating for a syntax
> > like `branch --some-flag A B C X` instead of feeding by hand
> > update-ref commands.
>
> I am personally not interested in such a mode, I do not know why you
> think "--some-flag" is needed when the command can figure out from
> the number of things on the command line being more than 2 just
> fine.
>
> But my comment was targetted against "it needs a shell loop" in the
> justification in the proposed log message, which is not quite
> correct.
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-12  0:19           ` Andrea Stacchiotti
@ 2025-06-12  9:55             ` Junio C Hamano
  2025-06-12 15:48               ` Junio C Hamano
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2025-06-12  9:55 UTC (permalink / raw)
  To: Andrea Stacchiotti; +Cc: Andrea Stacchiotti via GitGitGadget, git

Andrea Stacchiotti <andreastacchiotti@gmail.com> writes:

> So, if I may ask, is the proposed patch as written (branch -f A B C X)
> acceptable and you just need me to rewrite the commit message
> or are you not interested in it at all?

As I said, I personally am not interested in such a mode, but you
may be able to interest other people in it, and with wider support
I may change my mind.  But I do not think the feature should not be
tied to "--force" option at all.  "git branch A B C X" should be
able to create three new branches A B C that all tracks X if none of
them exist without "--force".




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-12  9:55             ` Junio C Hamano
@ 2025-06-12 15:48               ` Junio C Hamano
  2025-06-12 16:51                 ` Andrea Stacchiotti
  0 siblings, 1 reply; 10+ messages in thread
From: Junio C Hamano @ 2025-06-12 15:48 UTC (permalink / raw)
  To: Andrea Stacchiotti; +Cc: Andrea Stacchiotti via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> writes:

> I may change my mind.  But I do not think the feature should not be
> tied to "--force" option at all.

Sorry for a double-negation failure.  What I think is that the
feature should be orthogonal to "--force".

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] branch: move multiple branches in a single --force
  2025-06-12 15:48               ` Junio C Hamano
@ 2025-06-12 16:51                 ` Andrea Stacchiotti
  0 siblings, 0 replies; 10+ messages in thread
From: Andrea Stacchiotti @ 2025-06-12 16:51 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Andrea Stacchiotti via GitGitGadget, git

Il giorno gio 12 giu 2025 alle ore 17:48 Junio C Hamano
<gitster@pobox.com> ha scritto:
>
> Junio C Hamano <gitster@pobox.com> writes:
>
> > I may change my mind.  But I do not think the feature should not be
> > tied to "--force" option at all.
>
> Sorry for a double-negation failure.  What I think is that the
> feature should be orthogonal to "--force".

No worries, I got it, it makes sense.
I can implement the revised request if I see some replies expressing interest.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-06-12 16:51 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-10  9:07 [PATCH] branch: move multiple branches in a single --force Andrea Stacchiotti via GitGitGadget
2025-06-10 21:25 ` Junio C Hamano
2025-06-10 22:17   ` Andrea Stacchiotti
2025-06-11  0:22     ` Junio C Hamano
2025-06-11  8:34       ` Andrea Stacchiotti
2025-06-11 15:26         ` Junio C Hamano
2025-06-12  0:19           ` Andrea Stacchiotti
2025-06-12  9:55             ` Junio C Hamano
2025-06-12 15:48               ` Junio C Hamano
2025-06-12 16:51                 ` Andrea Stacchiotti

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox