Git development
 help / color / mirror / Atom feed
From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>,
	Johannes Sixt <j6t@kdbg.org>,
	Harald Nordgren <haraldnordgren@gmail.com>,
	Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v5 5/5] branch: add --all-remotes flag
Date: Mon, 11 May 2026 06:58:28 +0000	[thread overview]
Message-ID: <f2cee8c79b1ac3111410309c262283c645a1aae6.1778482708.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2285.v5.git.git.1778482708.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

Combined with --forked or --prune-merged, --all-remotes acts on
every configured remote, in addition to any explicit <remote>
arguments. Used alone, it errors out.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
 Documentation/git-branch.adoc |  9 ++++++--
 builtin/branch.c              | 41 +++++++++++++++++++++++++----------
 t/t3200-branch.sh             | 40 ++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 14 deletions(-)

diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
index 9d4944d17e..5c5b91d9b6 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -24,8 +24,8 @@ git branch (-m|-M) [<old-branch>] <new-branch>
 git branch (-c|-C) [<old-branch>] <new-branch>
 git branch (-d|-D) [-r] <branch-name>...
 git branch --edit-description [<branch-name>]
-git branch --forked <remote>...
-git branch [-f] --prune-merged <remote>...
+git branch --forked (<remote>... | --all-remotes)
+git branch [-f] --prune-merged (<remote>... | --all-remotes)
 
 DESCRIPTION
 -----------
@@ -226,6 +226,11 @@ With `--force` (or `-f`), delete them regardless. The currently
 checked-out branch in any worktree is always preserved, as is
 any branch with `branch.<name>.pruneMerged` set to `false`.
 
+`--all-remotes`::
+	With `--forked` or `--prune-merged`, act on every
+	configured remote in addition to any explicit _<remote>_
+	arguments.
+
 `-v`::
 `-vv`::
 `--verbose`::
diff --git a/builtin/branch.c b/builtin/branch.c
index 07d867373f..37ea75ecc3 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -685,6 +685,13 @@ static void copy_or_rename_branch(const char *oldname, const char *newname, int
 	free_worktrees(worktrees);
 }
 
+static int collect_remote_name(struct remote *remote, void *cb_data)
+{
+	struct string_list *remote_names = cb_data;
+	string_list_insert(remote_names, remote->name);
+	return 0;
+}
+
 static void parse_forked_args(int argc, const char **argv,
 			      struct string_list *remote_names,
 			      struct string_list *tracking_refs)
@@ -774,7 +781,7 @@ static void collect_default_branch_refs(const struct string_list *remote_names,
 	}
 }
 
-static void collect_forked_set(int argc, const char **argv,
+static void collect_forked_set(int argc, const char **argv, int all_remotes,
 			       struct string_list *protected_default_refs,
 			       struct string_list *out)
 {
@@ -787,6 +794,8 @@ static void collect_forked_set(int argc, const char **argv,
 	};
 
 	parse_forked_args(argc, argv, &remote_names, &tracking_refs);
+	if (all_remotes)
+		for_each_remote(collect_remote_name, &remote_names);
 
 	refs_for_each_branch_ref(get_main_ref_store(the_repository),
 				 collect_forked_branch, &cb);
@@ -800,15 +809,15 @@ static void collect_forked_set(int argc, const char **argv,
 	string_list_clear(&tracking_refs, 0);
 }
 
-static int list_forked_branches(int argc, const char **argv)
+static int list_forked_branches(int argc, const char **argv, int all_remotes)
 {
 	struct string_list out = STRING_LIST_INIT_DUP;
 	struct string_list_item *item;
 
-	if (!argc)
-		die(_("--forked requires at least one <remote>"));
+	if (!argc && !all_remotes)
+		die(_("--forked requires at least one <remote> or --all-remotes"));
 
-	collect_forked_set(argc, argv, NULL, &out);
+	collect_forked_set(argc, argv, all_remotes, NULL, &out);
 	for_each_string_list_item(item, &out)
 		puts(item->string);
 
@@ -816,8 +825,8 @@ static int list_forked_branches(int argc, const char **argv)
 	return 0;
 }
 
-static int prune_merged_branches(int argc, const char **argv, int force,
-				 int quiet)
+static int prune_merged_branches(int argc, const char **argv,
+				 int all_remotes, int force, int quiet)
 {
 	struct string_list candidates = STRING_LIST_INIT_DUP;
 	struct string_list protected_default_refs = STRING_LIST_INIT_DUP;
@@ -826,10 +835,11 @@ static int prune_merged_branches(int argc, const char **argv, int force,
 	int n_not_merged = 0;
 	int ret = 0;
 
-	if (!argc)
-		die(_("--prune-merged requires at least one <remote>"));
+	if (!argc && !all_remotes)
+		die(_("--prune-merged requires at least one <remote> or --all-remotes"));
 
-	collect_forked_set(argc, argv, &protected_default_refs, &candidates);
+	collect_forked_set(argc, argv, all_remotes, &protected_default_refs,
+			   &candidates);
 
 	for_each_string_list_item(item, &candidates) {
 		const char *short_name = item->string;
@@ -952,6 +962,7 @@ int cmd_branch(int argc,
 	    unset_upstream = 0, show_current = 0, edit_description = 0;
 	int forked = 0;
 	int prune_merged = 0;
+	int all_remotes = 0;
 	const char *new_upstream = NULL;
 	int noncreate_actions = 0;
 	/* possible options */
@@ -1009,6 +1020,9 @@ int cmd_branch(int argc,
 			N_("list local branches forked from the given <remote>s")),
 		OPT_BOOL(0, "prune-merged", &prune_merged,
 			N_("delete local branches forked from the given <remote>s that are merged into their upstream")),
+		OPT_BOOL_F(0, "all-remotes", &all_remotes,
+			N_("with --forked or --prune-merged, act on every configured remote"),
+			PARSE_OPT_NONEG),
 		OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
 		OPT_MERGED(&filter, N_("print only branches that are merged")),
 		OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
@@ -1052,6 +1066,9 @@ int cmd_branch(int argc,
 	argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
 			     0);
 
+	if (all_remotes && !forked && !prune_merged)
+		die(_("--all-remotes requires --forked or --prune-merged"));
+
 	if (!delete && !rename && !copy && !edit_description && !new_upstream &&
 	    !show_current && !unset_upstream && !forked && !prune_merged &&
 	    argc == 0)
@@ -1105,10 +1122,10 @@ int cmd_branch(int argc,
 				      quiet, 0, NULL);
 		goto out;
 	} else if (forked) {
-		ret = list_forked_branches(argc, argv);
+		ret = list_forked_branches(argc, argv, all_remotes);
 		goto out;
 	} else if (prune_merged) {
-		ret = prune_merged_branches(argc, argv, force, quiet);
+		ret = prune_merged_branches(argc, argv, all_remotes, force, quiet);
 		goto out;
 	} else if (show_current) {
 		print_current_branch_name();
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index f9aca90f4d..3809bfe0ad 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1771,6 +1771,27 @@ test_expect_success '--forked requires at least one <remote>' '
 	test_grep "at least one <remote>" err
 '
 
+test_expect_success '--forked --all-remotes covers every configured remote' '
+	git -C forked branch --forked --all-remotes >actual &&
+	cat >expect <<-\EOF &&
+	local-foreign
+	local-one
+	local-two
+	main
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success '--forked --all-remotes still validates explicit <remote>' '
+	test_must_fail git -C forked branch --forked nope --all-remotes 2>err &&
+	test_grep "neither a configured remote nor a remote-tracking branch" err
+'
+
+test_expect_success '--all-remotes alone is rejected' '
+	test_must_fail git -C forked branch --all-remotes 2>err &&
+	test_grep "requires --forked or --prune-merged" err
+'
+
 test_expect_success '--prune-merged: setup' '
 	test_create_repo pm-upstream &&
 	test_commit -C pm-upstream base &&
@@ -1924,4 +1945,23 @@ test_expect_success 'branch -d still deletes a pruneMerged=false branch' '
 	test_must_fail git -C pm-optout-d rev-parse --verify refs/heads/one
 '
 
+test_expect_success '--prune-merged --all-remotes covers every configured remote' '
+	test_when_finished "rm -rf pm-allremotes" &&
+	git clone pm-upstream pm-allremotes &&
+	test_create_repo pm-other &&
+	test_commit -C pm-other other-base &&
+	git -C pm-other branch foreign other-base &&
+	git -C pm-allremotes remote add other ../pm-other &&
+	git -C pm-allremotes fetch other &&
+	git -C pm-allremotes branch one --track origin/one &&
+	git -C pm-allremotes branch foreign --track other/foreign &&
+
+	git -C pm-allremotes update-ref -d refs/remotes/origin/one &&
+	git -C pm-allremotes update-ref -d refs/remotes/other/foreign &&
+	git -C pm-allremotes branch --force --prune-merged --all-remotes &&
+
+	test_must_fail git -C pm-allremotes rev-parse --verify refs/heads/one &&
+	test_must_fail git -C pm-allremotes rev-parse --verify refs/heads/foreign
+'
+
 test_done
-- 
gitgitgadget

  parent reply	other threads:[~2026-05-11  6:58 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-01 21:35 [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren via GitGitGadget
2026-05-03 22:39 ` Junio C Hamano
2026-05-04 18:28   ` [PATCH] checkout: add --autostash option for branch switching Harald Nordgren
2026-05-10  1:01     ` Junio C Hamano
2026-05-05  7:14   ` [PATCH] fetch: add fetch.pruneLocalBranches config Johannes Sixt
2026-05-04 18:27 ` [PATCH v2 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-04 23:25     ` Kristoffer Haugsbakk
2026-05-04 18:27   ` [PATCH v2 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-04 18:27   ` [PATCH v2 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-05  7:22   ` [PATCH v3 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-05  7:22     ` [PATCH v3 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-05 19:23     ` [PATCH v4 0/6] fetch: add fetch.pruneBranches config Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 1/6] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 2/6] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 3/6] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 4/6] fetch: add --prune-merged Harald Nordgren via GitGitGadget
2026-05-05 20:48         ` Johannes Sixt
2026-05-05 22:07           ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-11  2:59             ` Junio C Hamano
2026-05-11  6:56               ` Harald Nordgren
2026-05-05 19:23       ` [PATCH v4 5/6] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-05 19:23       ` [PATCH v4 6/6] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-07 20:14       ` [PATCH v4 0/6] fetch: add fetch.pruneBranches config Harald Nordgren
2026-05-11  6:58       ` [PATCH v5 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-11  8:18           ` Junio C Hamano
2026-05-11  8:44             ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-11  6:58         ` [PATCH v5 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` [PATCH v5 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-11  6:58         ` Harald Nordgren via GitGitGadget [this message]
2026-05-11  9:44         ` [PATCH v6 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-11  9:44           ` [PATCH v6 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-11 23:20           ` [PATCH v6 0/5] branch: prune-merged Junio C Hamano
2026-05-12  7:35             ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-12  8:23           ` [PATCH v7 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-12 13:53               ` Junio C Hamano
2026-05-12 17:00                 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-12  8:23             ` [PATCH v7 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-12  8:23             ` [PATCH v7 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-12 17:07             ` [PATCH v8 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-12 17:07               ` [PATCH v8 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget
2026-05-13 13:46               ` [PATCH v8 0/5] branch: prune-merged Junio C Hamano
2026-05-13 18:57                 ` [PATCH] fetch: add fetch.pruneLocalBranches config Harald Nordgren
2026-05-13 19:34               ` [PATCH v9 0/5] branch: prune-merged Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 1/5] branch: add --forked <remote> Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 2/5] branch: let delete_branches warn instead of error on bulk refusal Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 3/5] branch: add --prune-merged <remote> Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 4/5] branch: add branch.<name>.pruneMerged opt-out Harald Nordgren via GitGitGadget
2026-05-13 19:34                 ` [PATCH v9 5/5] branch: add --all-remotes flag Harald Nordgren via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f2cee8c79b1ac3111410309c262283c645a1aae6.1778482708.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=haraldnordgren@gmail.com \
    --cc=j6t@kdbg.org \
    --cc=kristofferhaugsbakk@fastmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox