git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Johan Herland <johan@herland.net>
To: git@vger.kernel.org
Cc: johan@herland.net, gitster@pobox.com
Subject: [PATCH 7/7] refs.c: Add rules for resolving refs using remote refspecs
Date: Sun,  5 May 2013 01:55:49 +0200	[thread overview]
Message-ID: <1367711749-8812-8-git-send-email-johan@herland.net> (raw)
In-Reply-To: <1367711749-8812-1-git-send-email-johan@herland.net>

The "$remote/$branch" expression is often used as a shorthand with the
intention of referring to the remote-tracking branch corresponding to
the given $branch in the $remote repo.

Currently, Git resolves this by prepending "refs/remotes/" to the given
"$remote/$branch" expression, resulting in "refs/remotes/$remote/$branch",
which is equivalent to the above intention only when conventional
refspecs are being used (e.g. refs/heads/*:refs/remotes/origin/*).
Correspondingly, a full remote-tracking branch name, can only be
shortened to the "$remote/$branch" form if it textually matches the
"refs/remotes/$remote/$branch" format.

If unconventional refspecs (e.g. refs/heads/*:refs/remotes/origin/heads/*)
are being used, then the current expansion ("refs/remotes/$remote/$branch")
fails to match the remote-tracking branches from the configured remotes.
Ditto for the corresponding shortening.

Instead of doing pure textual expansion (from "$remote/$branch" to
"refs/remotes/$remote/$branch"), we should expand by finding all remotes
matching "$remote", look up their fetch refspecs, and map
"refs/heads/$branch" through them to find the appropriate remote-tracking
branch name corresponding to "$remote/$branch". This would yield the
correct remote-tracking branch in all cases, both for conventional and
unconventional refspecs.

Likewise, when shortening full refnames for remote-tracking branches into
their shorthand form ("$remote/$branch"), we should find the refspec with
the RHS that matches the full remote-tracking branch name, and map
through that refspec to produce the corresponding LHS (minus the leading
"refs/heads/" part), and from that construct the corresponding
"$remote/$branch" shorthand.

This patch adds a new expansion method - ref_expand_refspec() - and a
corresponding shortening method - ref_shorten_refspec() - that implements
the remote refspec traversal and expanding/shortening logic described
above. These expand/shorten methods complement the existing
ref_expand_txtly() and ref_shorten_txtly() methods that implement the
current textual expanding/shortening logic.

Implementing the proper expanding/shortening of "$remote/$branch" is now
a simple matter of adding another entry to the ref_expand_rules list,
using the new ref_expand_refspec() and ref_shorten_refspec() functions.

Note that the existing "refs/remotes/*" textual expansion/shortening rule
is kept to preserve backwards compatibility for refs under refs/remotes/*
that are not covered by any configured refspec.

Signed-off-by: Johan Herland <johan@herland.net>
---
 refs.c                                         | 98 +++++++++++++++++++++++++-
 t/t7900-working-with-namespaced-remote-refs.sh | 21 +++++-
 2 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/refs.c b/refs.c
index 98997c4..18d7188 100644
--- a/refs.c
+++ b/refs.c
@@ -4,6 +4,7 @@
 #include "tag.h"
 #include "dir.h"
 #include "string-list.h"
+#include "remote.h"
 
 /*
  * Make sure "ref" is something reasonable to have under ".git/refs/";
@@ -1758,12 +1759,102 @@ static char *ref_shorten_txtly(const struct ref_expand_rule *rule,
 	return xstrndup(refname + pre_len, match_len);
 }
 
+struct ref_expand_refspec_helper_data {
+	const struct ref_expand_rule *rule;
+	char *dst;
+	size_t dst_len;
+	const char *src;
+	size_t src_len;
+};
+
+static int ref_expand_refspec_helper(struct remote *remote, void *cb_data)
+{
+	struct ref_expand_refspec_helper_data *cb = cb_data;
+	struct refspec query;
+	char refspec_src[PATH_MAX];
+	size_t ref_start = strlen(remote->name) + 1;
+	if (prefixcmp(cb->src, remote->name) ||
+	    cb->src_len <= ref_start ||
+	    cb->src[ref_start - 1] != '/')
+		return 0;
+
+	mksnpath(refspec_src, sizeof(refspec_src), cb->rule->pattern,
+		 cb->src_len - ref_start, cb->src + ref_start);
+
+	memset(&query, 0, sizeof(struct refspec));
+	query.src = refspec_src;
+	if ((!remote_find_tracking(remote, &query)) &&
+	    strlen(query.dst) < cb->dst_len) {
+		strcpy(cb->dst, query.dst);
+		return 1;
+	}
+	return 0;
+}
+
+static void ref_expand_refspec(const struct ref_expand_rule *rule,
+			       char *dst, size_t dst_len,
+			       const char *shortname, size_t shortname_len)
+{
+	/*
+	 * Given shortname of the form "$remote/$ref", see if there is a
+	 * fetch refspec configured for $remote whose lhs matches
+	 * rule->pattern % $ref, and use the corresponding rhs of that
+	 * mapping as the expanded result of "$remote/$ref".
+	 */
+	const void *has_slash = memchr(shortname, '/', shortname_len);
+	struct ref_expand_refspec_helper_data cb = {
+		rule, dst, dst_len, shortname, shortname_len };
+	dst[0] = '\0';
+	if (has_slash)
+		for_each_remote(ref_expand_refspec_helper, &cb);
+}
+
+static int ref_shorten_refspec_helper(struct remote *remote, void *cb_data)
+{
+	struct ref_expand_refspec_helper_data *cb = cb_data;
+	struct refspec query;
+	char *lhs_ref;
+	int ret = 0;
+
+	memset(&query, 0, sizeof(struct refspec));
+	query.dst = (char *) cb->src;
+	if (!remote_find_tracking(remote, &query) &&
+	    (lhs_ref = ref_shorten_txtly(cb->rule, query.src))) {
+		/* refname matches rhs and rule->pattern matches lhs */
+		cb->dst_len = strlen(remote->name) + 1 + strlen(lhs_ref);
+		/* "$remote/$lhs_ref" should be shorter than src */
+		if (cb->dst_len < cb->src_len) {
+			cb->dst = xmalloc(cb->dst_len + 1);
+			snprintf(cb->dst, cb->dst_len + 1,
+				 "%s/%s", remote->name, lhs_ref);
+			ret = 1;
+		}
+		free(lhs_ref);
+	}
+	return ret;
+}
+
+static char *ref_shorten_refspec(const struct ref_expand_rule *rule,
+				 const char *refname)
+{
+	/*
+	 * See if there is a $remote with a fetch refspec that matches the
+	 * given refname on rhs, and will produce refs/heads/$ref on the
+	 * lhs. If so, construct "$remote/$ref" as the shorthand.
+	 */
+	struct ref_expand_refspec_helper_data cb = {
+		rule, NULL, 0, refname, strlen(refname) };
+	for_each_remote(ref_shorten_refspec_helper, &cb);
+	return cb.dst;
+}
+
 const struct ref_expand_rule ref_expand_rules_local[] = {
 	{ ref_expand_txtly, NULL, "%.*s" },
 	{ ref_expand_txtly, ref_shorten_txtly, "refs/%.*s" },
 	{ ref_expand_txtly, ref_shorten_txtly, "refs/tags/%.*s" },
 	{ ref_expand_txtly, ref_shorten_txtly, "refs/heads/%.*s" },
 	{ ref_expand_txtly, ref_shorten_txtly, "refs/remotes/%.*s" },
+	{ ref_expand_refspec, ref_shorten_refspec, "refs/heads/%.*s" },
 	{ ref_expand_txtly, ref_shorten_txtly, "refs/remotes/%.*s/HEAD" },
 	{ NULL, NULL, NULL }
 };
@@ -3028,13 +3119,14 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
 
 			/*
 			 * the short name is ambiguous, if it resolves
-			 * (with this previous rule) to a valid ref
-			 * read_ref() returns 0 on success
+			 * (with this previous rule) to a valid
+			 * (but different) ref
 			 */
 			if (q->expand) {
 				q->expand(q, resolved, sizeof(resolved),
 					  short_name, short_name_len);
-				if (ref_exists(resolved))
+				if (strcmp(refname, resolved) &&
+				    ref_exists(resolved))
 					break;
 			}
 		}
diff --git a/t/t7900-working-with-namespaced-remote-refs.sh b/t/t7900-working-with-namespaced-remote-refs.sh
index cc25e76..302083e 100755
--- a/t/t7900-working-with-namespaced-remote-refs.sh
+++ b/t/t7900-working-with-namespaced-remote-refs.sh
@@ -89,7 +89,7 @@ test_expect_success 'enter client repo' '
 	cd client
 '
 
-test_expect_failure 'short-hand notation expands correctly for remote-tracking branches' '
+test_expect_success 'short-hand notation expands correctly for remote-tracking branches' '
 	echo refs/remotes/origin/heads/master > expect &&
 	git rev-parse --symbolic-full-name refs/remotes/origin/heads/master > actual &&
 	test_cmp expect actual &&
@@ -101,7 +101,7 @@ test_expect_failure 'short-hand notation expands correctly for remote-tracking b
 	test_cmp expect actual
 '
 
-test_expect_failure 'remote-tracking branches are shortened correctly' '
+test_expect_success 'remote-tracking branches are shortened correctly' '
 	echo origin/master > expect &&
 	git rev-parse --abbrev-ref refs/remotes/origin/heads/master > actual &&
 	test_cmp expect actual &&
@@ -113,4 +113,21 @@ test_expect_failure 'remote-tracking branches are shortened correctly' '
 	test_cmp expect actual
 '
 
+cat > expect.origin_master << EOF
+$server_master_b
+$server_master_a
+EOF
+
+cat > expect.origin_other << EOF
+$server_other_b
+$server_master_a
+EOF
+
+test_expect_success 'rev-list machinery should work with $remote/$branch' '
+	git rev-list origin/master > actual.origin_master &&
+	test_cmp expect.origin_master actual.origin_master &&
+	git rev-list origin/other > actual.origin_other &&
+	test_cmp expect.origin_other actual.origin_other
+'
+
 test_done
-- 
1.8.1.3.704.g33f7d4f

  parent reply	other threads:[~2013-05-04 23:56 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-04 23:55 [PATCH 0/7] Make "$remote/$branch" work with unconventional refspecs Johan Herland
2013-05-04 23:55 ` [PATCH 1/7] shorten_unambiguous_ref(): Allow shortening refs/remotes/origin/HEAD to origin Johan Herland
2013-05-05 11:56   ` Bert Wesarg
2013-05-06 17:52   ` Junio C Hamano
2013-05-07 18:49     ` Johan Herland
2013-05-07 18:54       ` [PATCHv2 1/3] t1514: Add tests of shortening refnames in strict/loose mode Johan Herland
2013-05-07 18:54         ` [PATCHv2 2/3] t1514: Demonstrate failure to correctly shorten "refs/remotes/origin/HEAD" Johan Herland
2013-05-07 18:54         ` [PATCHv2 3/3] shorten_unambiguous_ref(): Fix shortening refs/remotes/origin/HEAD to origin Johan Herland
2013-05-07 21:03       ` [PATCH 1/7] shorten_unambiguous_ref(): Allow " Junio C Hamano
2013-05-07 21:31       ` Junio C Hamano
2013-05-07 22:03         ` Johan Herland
2013-05-07 22:06           ` Junio C Hamano
2013-05-07 22:37             ` Johan Herland
2013-05-04 23:55 ` [PATCH 2/7] t7900: Start testing usability of namespaced remote refs Johan Herland
2013-05-07  1:29   ` Junio C Hamano
2013-05-07 21:52     ` Johan Herland
2013-05-07 22:20       ` Junio C Hamano
2013-05-04 23:55 ` [PATCH 3/7] t7900: Demonstrate failure to expand "$remote/$branch" according to refspecs Johan Herland
2013-05-07  1:30   ` Junio C Hamano
2013-05-04 23:55 ` [PATCH 4/7] refs.c: Refactor rules for expanding shorthand names into full refnames Johan Herland
2013-05-07  1:36   ` Junio C Hamano
2013-05-04 23:55 ` [PATCH 5/7] refs.c: Refactor code for shortening full refnames into shorthand names Johan Herland
2013-05-07  1:44   ` Junio C Hamano
2013-05-04 23:55 ` [PATCH 6/7] refname_match(): Caller must declare if we're matching local or remote refs Johan Herland
2013-05-07  1:48   ` Junio C Hamano
2013-05-04 23:55 ` Johan Herland [this message]
2013-05-05  4:28 ` [PATCH 0/7] Make "$remote/$branch" work with unconventional refspecs Junio C Hamano
2013-05-05  9:59   ` Johan Herland
2013-05-05 19:02     ` Junio C Hamano
2013-05-05 22:26       ` Johan Herland
2013-05-05 22:36         ` Junio C Hamano
2013-05-06  1:02           ` Santi Béjar
2013-05-06  1:04             ` Santi Béjar
2013-05-06 17:11               ` Junio C Hamano
2013-05-06 19:17                 ` Santi Béjar
2013-05-06 17:06         ` Junio C Hamano
2013-05-06 17:20           ` Junio C Hamano
2013-05-06 23:42           ` Johan Herland
2013-05-07  2:11             ` Junio C Hamano

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=1367711749-8812-8-git-send-email-johan@herland.net \
    --to=johan@herland.net \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.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;
as well as URLs for NNTP newsgroup(s).