Subject: Re: [PATCH v9 5/5] branch: add --all-remotes flag

From: Phillip Wood <[email-masked]>
Date: Mon, 18 May 2026 16:27:10 +0100

Hi Harald

On 13/05/2026 20:34, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <[email-masked]>
> 
> +`--all-remotes`::
> +	With `--forked` or `--prune-merged`, act on every
> +	configured remote in addition to any explicit _<remote>_
> +	arguments.

Why do we except additional arguments if this option includes all the 
remotes?

Thanks

Phillip

>   `-v`::
>   `-vv`::
>   `--verbose`::
> diff --git a/builtin/branch.c b/builtin/branch.c
> index bc4f4a4a18..7d45bada45 100644
> --- a/builtin/branch.c
> +++ b/builtin/branch.c
> @@ -687,6 +687,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)
> @@ -776,7 +783,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)
>   {
> @@ -789,6 +796,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);
> @@ -802,15 +811,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);
>   
> @@ -818,7 +827,8 @@ static int list_forked_branches(int argc, const char **argv)
>   	return 0;
>   }
>   
> -static int prune_merged_branches(int argc, const char **argv, int quiet)
> +static int prune_merged_branches(int argc, const char **argv,
> +				 int all_remotes, int quiet)
>   {
>   	struct string_list candidates = STRING_LIST_INIT_DUP;
>   	struct string_list protected_default_refs = STRING_LIST_INIT_DUP;
> @@ -827,10 +837,11 @@ static int prune_merged_branches(int argc, const char **argv, int quiet)
>   	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;
> @@ -943,6 +954,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 */
> @@ -1000,6 +1012,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")),
> @@ -1043,6 +1058,10 @@ 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)
> @@ -1096,10 +1115,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, quiet);
> +		ret = prune_merged_branches(argc, argv, all_remotes, quiet);
>   		goto out;
>   	} else if (show_current) {
>   		print_current_branch_name();
> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
> index 885a275e36..a36e5ee80a 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 &&
> @@ -1881,4 +1902,27 @@ 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 pm-other" &&
> +	git clone pm-upstream pm-allremotes &&
> +	test_create_repo pm-other &&
> +	test_commit -C pm-other other-base &&
> +	git -C pm-other checkout -b stable &&
> +	test_commit -C pm-other foreign-commit &&
> +	git -C pm-other branch foreign HEAD &&
> +	git -C pm-other checkout main &&
> +
> +	git -C pm-allremotes remote add other ../pm-other &&
> +	git -C pm-allremotes fetch other &&
> +	git -C pm-allremotes branch one one-commit &&
> +	git -C pm-allremotes branch --set-upstream-to=origin/next one &&
> +	git -C pm-allremotes branch foreign other/foreign &&
> +	git -C pm-allremotes branch --set-upstream-to=other/stable foreign &&
> +
> +	git -C pm-allremotes branch --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