git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC PATCH 2/2] push: support pushing to a remote group

From
Usman Akinyemi <usmanakinyemi202@gmail.com>
Date
Mar 5, 2026, 22:32 UTC
Message-ID
<20260305223248.170785-3-usmanakinyemi202@gmail.com>
In-Reply-To
<20260305223248.170785-1-usmanakinyemi202@gmail.com>

`git fetch` accepts a remote group name (configured via `remotes.<name>` in config) and fetches from each member remote. `git push` has no equivalent — it only accepts a single remote name.

Teach `git push` to resolve its repository argument through `add_remote_or_group()`, which was made public in the previous patch, so that a user can push to all remotes in a group with:

    git push <group>

When the argument resolves to a single remote the behaviour is identical to before. When it resolves to a group, each member remote is pushed in sequence.

The group push path rebuilds the refspec list (`rs`) from scratch for each member remote so that per-remote push mappings configured via `remote.<name>.push` are resolved correctly against each specific remote. Without this, refspec entries would accumulate across iterations and each subsequent remote would receive a growing list of duplicated entries.

Mirror detection (`remote->mirror`) is also evaluated per remote using a copy of the flags, so that a mirror remote in the group cannot set TRANSPORT_PUSH_FORCE on subsequent non-mirror remotes in the same group.

A known interaction: push.default = simple will die when the current branch has no upstream configured, because setup_default_push_refspecs() requires an upstream for that mode. Users pushing to a group should set push.default = current or supply explicit refspecs. This is consistent with how fetch handles default refspec resolution per remote.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
---
 builtin/push.c | 89 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 70 insertions(+), 19 deletions(-)
diff --git a/builtin/push.c b/builtin/push.c
index 5b6cebbb85..a98fb4c934 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -551,12 +551,13 @@ int cmd_push(int argc,
 	int flags = 0;
 	int tags = 0;
 	int push_cert = -1;
-	int rc;
+	int rc = 0;
 	const char *repo = NULL;	/* default repository */
 	struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
+	struct string_list remote_group = STRING_LIST_INIT_DUP; /* represent remote or remote group */
 	struct string_list *push_options;
 	const struct string_list_item *item;
-	struct remote *remote;
+	struct remote *remote = NULL;
 
 	struct option options[] = {
 		OPT__VERBOSITY(&verbosity),
@@ -625,25 +626,35 @@ int cmd_push(int argc,
 	if (argc > 0)
 		repo = argv[0];
 
-	remote = pushremote_get(repo);
-	if (!remote) {
-		if (repo)
-			die(_("bad repository '%s'"), repo);
-		die(_("No configured push destination.\n"
-		    "Either specify the URL from the command-line or configure a remote repository using\n"
-		    "\n"
-		    "    git remote add <name> <url>\n"
-		    "\n"
-		    "and then push using the remote name\n"
-		    "\n"
-		    "    git push <name>\n"));
+	if (repo) {
+		if (!add_remote_or_group(repo, &remote_group))
+			die(_("no such remote or remote group: %s"), repo);
+	} else {
+		remote = pushremote_get(NULL);
+		if (!remote)
+			die(_("No configured push destination.\n"
+			    "Either specify the URL from the command-line or configure a remote repository using\n"
+			    "\n"
+			    "    git remote add <name> <url>\n"
+			    "\n"
+			    "and then push using the remote name\n"
+			    "\n"
+			    "    git push <name>\n"));
 	}
 
-	if (argc > 0)
-		set_refspecs(argv + 1, argc - 1, remote);
+	/*
+	 * set_refspecs and mirror detection must not use `remote`
+	 * when it may be NULL (group path). For the single-remote case,
+	 * handle them here. For the group case they are handled
+	 * per-remote inside the loop below.
+	 */
+	if (remote) {
+		if (argc > 0)
+			set_refspecs(argv + 1, argc - 1, remote);
 
-	if (remote->mirror)
-		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+		if (remote->mirror)
+			flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+	}
 
 	if (flags & TRANSPORT_PUSH_ALL) {
 		if (argc >= 2)
@@ -661,10 +672,50 @@ int cmd_push(int argc,
 		if (strchr(item->string, '\n'))
 			die(_("push options must not have new line characters"));
 
-	rc = do_push(flags, push_options, remote);
+	if (remote) {
+		rc = do_push(flags, push_options, remote);
+	} else {
+		int base_flags = flags;
+		for (int i = 0; i < remote_group.nr; i++) {
+			int iter_flags = base_flags;
+			struct remote *r = pushremote_get(remote_group.items[i].string);
+			if (!r)
+				die(_("no such remote or remote group: %s"),
+				    remote_group.items[i].string);
+
+			/*
+			 * Rebuild rs from scratch for each remote so that
+			 * push mappings (remote.NAME.push config) are resolved
+			 * against this specific remote. Without this, mappings
+			 * from a previous iteration would accumulate in rs and
+			 * each remote would be pushed an ever-growing refspec list.
+			 */
+			refspec_clear(&rs);
+			rs = (struct refspec) REFSPEC_INIT_PUSH;
+
+			if (tags)
+				refspec_append(&rs, "refs/tags/*");
+			if (argc > 0)
+				set_refspecs(argv + 1, argc - 1, r);
+
+			/*
+			 * Compute mirror flag from a fresh base each iteration
+			 * so that a mirror remote does not bleed TRANSPORT_PUSH_FORCE
+			 * into subsequent non-mirror remotes in the same group.
+			 */
+			if (r->mirror)
+				iter_flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+
+			rc |= do_push(iter_flags, push_options, r);
+		}
+	}
+
+
 	string_list_clear(&push_options_cmdline, 0);
 	string_list_clear(&push_options_config, 0);
+	string_list_clear(&remote_group, 0);
 	clear_cas_option(&cas);
+
 	if (rc == -1)
 		usage_with_options(push_usage, options);
 	else
-- 
2.53.0
Previous: Usman AkinyemiNext: Junio C Hamano
Message 5 of 39 in “push: add support for pushing to remote groups”
  1. 0/2 push: add support for pushing to remote groupsUsman Akinyemi, Mar 5, 2026
  2. 1/2 remote: move remote group resolution to remote.cUsman Akinyemi, Mar 5, 2026
  3. Junio C HamanoMar 6, 2026
  4. Usman AkinyemiMar 9, 2026
  5. 2/2 push: support pushing to a remote groupUsman Akinyemi, Mar 5, 2026
  6. Junio C HamanoMar 7, 2026
  7. Usman AkinyemiMar 9, 2026
  8. Junio C HamanoMar 9, 2026
  9. 0/2 push: add support for pushing to remote groupsUsman Akinyemi, Mar 18, 2026
  10. 1/2 remote: move remote group resolution to remote.cUsman Akinyemi, Mar 18, 2026
  11. 2/2 push: support pushing to a remote groupUsman Akinyemi, Mar 18, 2026
  12. Junio C HamanoMar 18, 2026
  13. Junio C HamanoMar 18, 2026
  14. Junio C HamanoMar 18, 2026
  15. Junio C HamanoMar 19, 2026
  16. Usman AkinyemiMar 25, 2026
  17. Junio C HamanoMar 18, 2026
  18. Usman AkinyemiMar 18, 2026
  19. 0/2 push: add support for pushing to remote groupsUsman Akinyemi, Mar 25, 2026
  20. 1/2 remote: move remote group resolution to remote.cUsman Akinyemi, Mar 25, 2026
  21. 2/2 push: support pushing to a remote groupUsman Akinyemi, Mar 25, 2026
  22. Junio C HamanoMar 25, 2026
  23. Usman AkinyemiMar 31, 2026
  24. Usman AkinyemiMar 31, 2026
  25. Junio C HamanoApr 1, 2026
  26. Junio C HamanoMar 27, 2026
  27. 0/2 push: add support for pushing to remote groupsUsman Akinyemi, Apr 27, 2026
  28. 1/2 remote: move remote group resolution to remote.cUsman Akinyemi, Apr 27, 2026
  29. 2/2 push: support pushing to a remote groupUsman Akinyemi, Apr 27, 2026
  30. Junio C HamanoApr 28, 2026
  31. 0/3 push: add support for pushing to remote groupsUsman Akinyemi, May 3, 2026
  32. 1/3 remote: fix sign-compare warnings in push_cas_optionUsman Akinyemi, May 3, 2026
  33. 2/3 remote: move remote group resolution to remote.cUsman Akinyemi, May 3, 2026
  34. 3/3 push: support pushing to a remote groupUsman Akinyemi, May 3, 2026
  35. Kristoffer HaugsbakkMay 12, 2026
  36. 0/3 push: add support for pushing to remote groupsUsman Akinyemi, May 18, 2026
  37. 1/3 remote: fix sign-compare warnings in push_cas_optionUsman Akinyemi, May 18, 2026
  38. 2/3 remote: move remote group resolution to remote.cUsman Akinyemi, May 18, 2026
  39. 3/3 push: support pushing to a remote groupUsman Akinyemi, May 18, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.