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

[RFC PATCH] object-name: add @{upstreamhead} shorthand

From
Bence Ferdinandy <bence@ferdinandy.com>
Date
Oct 20, 2024, 20:24 UTC
Message-ID
<20241020202507.2596990-1-bence@ferdinandy.com>

The HEAD of the remote is useful in many situations, but currently one would need to know the name of the remote to perform something like "git log origin/HEAD..", which makes writing remote agnostic aliases complicated. Introduce the new shorthand "@{upstreamhead}" which returns <remote>/HEAD for the same <remote> "@{upstream}" would yield.

Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
---
Notes:
    RFC v1: Testing and documentation is completely missing, I'll add those
            in a v2 if people think the patch has merit.
 object-name.c | 15 ++++++++++++++-
 remote.c      | 36 ++++++++++++++++++++++++++++++++++++
 remote.h      |  8 ++++++++
 3 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/object-name.c b/object-name.c
index c892fbe80a..f40a226a57 100644
--- a/object-name.c
+++ b/object-name.c
@@ -936,6 +936,12 @@ static inline int push_mark(const char *string, int len)
 	return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
 }
 
+static inline int upstream_head_mark(const char *string, int len)
+{
+	const char *suffix[] = { "@{upstreamhead}", "@{uh}" };
+	return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
 static enum get_oid_result get_oid_1(struct repository *r, const char *name, int len, struct object_id *oid, unsigned lookup_flags);
 static int interpret_nth_prior_checkout(struct repository *r, const char *name, int namelen, struct strbuf *buf);
 
@@ -985,7 +991,8 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
 					continue;
 				}
 				if (!upstream_mark(str + at, len - at) &&
-				    !push_mark(str + at, len - at)) {
+				    !push_mark(str + at, len - at) &&
+				    !upstream_head_mark(str + at, len - at)) {
 					reflog_len = (len-1) - (at+2);
 					len = at;
 				}
@@ -1729,6 +1736,12 @@ int repo_interpret_branch_name(struct repository *r,
 					    options);
 		if (len > 0)
 			return len;
+
+		len = interpret_branch_mark(r, name, namelen, at - name, buf,
+					    upstream_head_mark, branch_get_upstream_head,
+					    options);
+		if (len > 0)
+			return len;
 	}
 
 	return -1;
diff --git a/remote.c b/remote.c
index 10104d11e3..302f013a25 100644
--- a/remote.c
+++ b/remote.c
@@ -1980,6 +1980,42 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
 	return branch->merge[0]->dst;
 }
 
+const char *branch_get_upstream_head(struct branch *branch, struct strbuf *err)
+{
+	struct strbuf retval = STRBUF_INIT, refstring = STRBUF_INIT;
+	struct string_list l = STRING_LIST_INIT_DUP;
+
+	if (!branch)
+		return error_buf(err, _("HEAD does not point to a branch"));
+
+	if (!branch->merge || !branch->merge[0]) {
+		/*
+		 * no merge config; is it because the user didn't define any,
+		 * or because it is not a real branch, and get_branch
+		 * auto-vivified it?
+		 */
+		if (!refs_ref_exists(get_main_ref_store(the_repository), branch->refname))
+			return error_buf(err, _("no such branch: '%s'"),
+					 branch->name);
+		return error_buf(err,
+				 _("no upstream configured for branch '%s'"),
+				 branch->name);
+	}
+
+	if (!branch->merge[0]->dst)
+		return error_buf(err,
+				 _("upstream branch '%s' not stored as a remote-tracking branch"),
+				 branch->merge[0]->src);
+
+	string_list_split(&l, branch->merge[0]->dst, '/', -1);
+	strbuf_addf(&refstring, "refs/remotes/%s/HEAD", l.items[2].string);
+
+	if (refs_read_symbolic_ref(get_main_ref_store(the_repository), refstring.buf, &retval))
+			return error_buf(err, _("%s does not exist"), refstring.buf);
+
+	return retval.buf;
+}
+
 static const char *tracking_for_push_dest(struct remote *remote,
 					  const char *refname,
 					  struct strbuf *err)
diff --git a/remote.h b/remote.h
index a7e5c4e07c..a1d0f44297 100644
--- a/remote.h
+++ b/remote.h
@@ -360,6 +360,14 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err);
  */
 const char *branch_get_push(struct branch *branch, struct strbuf *err);
 
+/**
+ * Return the fully-qualified refname of the HEAD branch for the same remote
+ * that "branch@{upstream}" is on.
+ *
+ * The return value and `err` conventions match those of `branch_get_upstream`.
+ */
+const char *branch_get_upstream_head(struct branch *branch, struct strbuf *err);
+
 /* Flags to match_refs. */
 enum match_refs_flags {
 	MATCH_REFS_NONE		= 0,
-- 
2.47.0.94.gc947641c25
Next: Kristoffer Haugsbakk
Message 1 of 14 in “object-name: add @{upstreamhead} shorthand”
  1. object-name: add @{upstreamhead} shorthandBence Ferdinandy, Oct 20, 2024
  2. Kristoffer HaugsbakkOct 20, 2024
  3. Bence FerdinandyOct 20, 2024
  4. Jeff KingOct 21, 2024
  5. Bence FerdinandyOct 21, 2024
  6. Taylor BlauOct 21, 2024
  7. Jeff KingOct 23, 2024
  8. Bence FerdinandyOct 24, 2024
  9. Jeff KingOct 25, 2024
  10. Bence FerdinandyOct 27, 2024
  11. Taylor BlauOct 27, 2024
  12. Jeff KingOct 28, 2024
  13. Taylor BlauOct 21, 2024
  14. Bence FerdinandyOct 21, 2024

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.