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

[PATCH v3 09/14] remote.c: add branch_get_push

From
Jeff King <peff@peff.net>
Date
May 21, 2015, 04:45 UTC
Message-ID
<20150521044536.GI23409@peff.net>
In-Reply-To
<20150521044429.GA5857@peff.net>

In a triangular workflow, the place you pull from and the place you push to may be different. As we have branch_get_upstream for the former, this patch adds branch_get_push for the latter (and as the former implements @{upstream}, so will this implement @{push} in a future patch).

Note that the memory-handling for the return value bears some explanation. Some code paths require allocating a new string, and some let us return an existing string. We should provide a consistent interface to the caller, so it knows whether to free the result or not.

We could do so by xstrdup-ing any existing strings, and having the caller always free. But that makes us inconsistent with branch_get_upstream, so we would prefer to simply take ownership of the resulting string. We do so by storing it inside the "struct branch", just as we do with the upstream refname (in that case we compute it when the branch is created, but there's no reason not to just fill it in lazily in this case).

Signed-off-by: Jeff King <peff@peff.net>
---
 remote.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 remote.h | 10 ++++++++
 2 files changed, 95 insertions(+)
diff --git a/remote.c b/remote.c
index 1b7051a..be45a39 100644
--- a/remote.c
+++ b/remote.c
@@ -1737,6 +1737,91 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
 	return branch->merge[0]->dst;
 }
 
+static const char *tracking_for_push_dest(struct remote *remote,
+					  const char *refname,
+					  struct strbuf *err)
+{
+	char *ret;
+
+	ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
+	if (!ret)
+		return error_buf(err,
+				 _("push destination '%s' on remote '%s' has no local tracking branch"),
+				 refname, remote->name);
+	return ret;
+}
+
+static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
+{
+	struct remote *remote;
+
+	if (!branch)
+		return error_buf(err, _("HEAD does not point to a branch"));
+
+	remote = remote_get(pushremote_for_branch(branch, NULL));
+	if (!remote)
+		return error_buf(err,
+				 _("branch '%s' has no remote for pushing"),
+				 branch->name);
+
+	if (remote->push_refspec_nr) {
+		char *dst;
+		const char *ret;
+
+		dst = apply_refspecs(remote->push, remote->push_refspec_nr,
+				     branch->refname);
+		if (!dst)
+			return error_buf(err,
+					 _("push refspecs for '%s' do not include '%s'"),
+					 remote->name, branch->name);
+
+		ret = tracking_for_push_dest(remote, dst, err);
+		free(dst);
+		return ret;
+	}
+
+	if (remote->mirror)
+		return tracking_for_push_dest(remote, branch->refname, err);
+
+	switch (push_default) {
+	case PUSH_DEFAULT_NOTHING:
+		return error_buf(err, _("push has no destination (push.default is 'nothing')"));
+
+	case PUSH_DEFAULT_MATCHING:
+	case PUSH_DEFAULT_CURRENT:
+		return tracking_for_push_dest(remote, branch->refname, err);
+
+	case PUSH_DEFAULT_UPSTREAM:
+		return branch_get_upstream(branch, err);
+
+	case PUSH_DEFAULT_UNSPECIFIED:
+	case PUSH_DEFAULT_SIMPLE:
+		{
+			const char *up, *cur;
+
+			up = branch_get_upstream(branch, err);
+			if (!up)
+				return NULL;
+			cur = tracking_for_push_dest(remote, branch->refname, err);
+			if (!cur)
+				return NULL;
+			if (strcmp(cur, up))
+				return error_buf(err,
+						 _("cannot resolve 'simple' push to a single destination"));
+			return cur;
+		}
+	}
+
+	die("BUG: unhandled push situation");
+}
+
+const char *branch_get_push(struct branch *branch, struct strbuf *err)
+{
+	if (!branch->push_tracking_ref)
+		branch->push_tracking_ref = branch_get_push_1(branch, err);
+	return branch->push_tracking_ref;
+}
+
 static int ignore_symref_update(const char *refname)
 {
 	unsigned char sha1[20];
diff --git a/remote.h b/remote.h
index 03ca005..3326f2b 100644
--- a/remote.h
+++ b/remote.h
@@ -209,6 +209,8 @@ struct branch {
 	struct refspec **merge;
 	int merge_nr;
 	int merge_alloc;
+
+	const char *push_tracking_ref;
 };
 
 struct branch *branch_get(const char *name);
@@ -229,6 +231,14 @@ int branch_merge_matches(struct branch *, int n, const char *);
  */
 const char *branch_get_upstream(struct branch *branch, struct strbuf *err);
 
+/**
+ * Return the tracking branch that corresponds to the ref we would push to
+ * given a bare `git push` while `branch` is checked out.
+ *
+ * The return value and `err` conventions match those of `branch_get_upstream`.
+ */
+const char *branch_get_push(struct branch *branch, struct strbuf *err);
+
 /* Flags to match_refs. */
 enum match_refs_flags {
 	MATCH_REFS_NONE		= 0,
-- 
2.4.1.528.g00591e3
Previous: Jeff KingNext: Jeff King
Message 20 of 27 in “implement @{push} shorthand”
  1. 0/14 implement @{push} shorthandJeff King, May 21, 2015
  2. 01/14 remote.c: drop default_remote_name variableJeff King, May 21, 2015
  3. 02/14 remote.c: refactor setup of branch->merge listJeff King, May 21, 2015
  4. Junio C HamanoMay 21, 2015
  5. 03/14 remote.c: drop "remote" pointer from "struct branch"Jeff King, May 21, 2015
  6. 04/14 remote.c: hoist branch.*.remote lookup out of remote_get_1Jeff King, May 21, 2015
  7. 05/14 remote.c: provide per-branch pushremote nameJeff King, May 21, 2015
  8. 06/14 remote.c: hoist read_config into remote_get_1Jeff King, May 21, 2015
  9. 07/14 remote.c: introduce branch_get_upstream helperJeff King, May 21, 2015
  10. Junio C HamanoMay 21, 2015
  11. Jeff KingMay 21, 2015
  12. Jeff KingMay 21, 2015
  13. Junio C HamanoMay 21, 2015
  14. 08/14 remote.c: report specific errors from branch_get_upstreamJeff King, May 21, 2015
  15. Junio C HamanoMay 21, 2015
  16. Jeff KingMay 21, 2015
  17. Junio C HamanoMay 21, 2015
  18. Jeff KingMay 22, 2015
  19. Jeff KingMay 22, 2015
  20. 09/14 remote.c: add branch_get_pushJeff King, May 21, 2015
  21. 10/14 sha1_name: refactor upstream_markJeff King, May 21, 2015
  22. 11/14 sha1_name: refactor interpret_upstream_markJeff King, May 21, 2015
  23. 12/14 sha1_name: implement @{push} shorthandJeff King, May 21, 2015
  24. 13/14 for-each-ref: use skip_prefix instead of starts_withJeff King, May 21, 2015
  25. 14/14 for-each-ref: accept "%(push)" formatJeff King, May 21, 2015
  26. Jeff KingMay 21, 2015
  27. Junio C HamanoMay 21, 2015

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.