diff mbox series

[2/3] fast-export: fix bug with nested tags

Message ID 20210422010659.2498280-3-lukeshu@lukeshu.com (mailing list archive)
State New, archived
Headers show
Series fast-export, fast-import: let tags specify a different refname | expand

Commit Message

Luke Shumaker April 22, 2021, 1:06 a.m. UTC
From: Luke Shumaker <lukeshu@datawire.io>

The t9350-fast-export.sh 'handling nested tags' test takes an annotated
tag named 'muss' and creates a second annotated tag named 'nested' that
points to 'muss'.  As the test observes, fast-export indeed does spit out
a stream that creates a tag named 'nested' that points to another tag.

However, the test doesn't do a very thorough job of inspecting the
resulting tag.  It doesn't notice that the output 'nested' isn't quite the
same as the input 'nested'.

The 'nested' tags are different because the 'muss' tags that they point to
are different; fast-export accidentally creates the 'muss' tag object as
saying "tag nested" instead of "tag muss".

This is because of a quirk in how the fast-export walk sets the refname
for objects that aren't directly pointed to by an exported ref.  So, fix
the bug by getting the tagname from the tag object itself, rather than
from the refname.

Signed-off-by: Luke Shumaker <lukeshu@datawire.io>
---
 builtin/fast-export.c  | 28 +++++++++++++++++++++++++---
 t/t9350-fast-export.sh |  9 ++++++++-
 2 files changed, 33 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index e697f87172..2bf83fe52e 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -550,6 +550,21 @@  static const char *anonymize_refname(const char *refname)
 	return anon.buf;
 }
 
+static const char *anonymize_tagname(size_t tagname_len, const char *tagname)
+{
+	/*
+	 * Use anonymize_refname internally, so that the anonymization
+	 * is consistent between a tag's refname and its internal
+	 * tagname (if they were consistent to begin with, anyway).
+	 */
+	static struct strbuf as_refname = STRBUF_INIT;
+
+	strbuf_reset(&as_refname);
+	strbuf_addf(&as_refname, "refs/tags/%.*s", (int)tagname_len, tagname);
+
+	return anonymize_refname(as_refname.buf) + strlen("refs/tags/");
+}
+
 /*
  * We do not even bother to cache commit messages, as they are unlikely
  * to be repeated verbatim, and it is not that interesting when they are.
@@ -775,6 +790,7 @@  static void handle_tag(const char *refname, struct tag *tag)
 	const char *message;
 	size_t message_size = 0;
 	const char *tagname;
+	size_t tagname_len;
 	const char *tagger, *tagger_end;
 	struct object *tagged;
 	int tagged_mark;
@@ -804,6 +820,12 @@  static void handle_tag(const char *refname, struct tag *tag)
 		message_size = strlen(message);
 	}
 
+	tagname = memmem(buf, message ? message - buf : size, "\ntag ", 5);
+	if (!tagname)
+		die("malformed tag %s", oid_to_hex(&tag->object.oid));
+	tagname += 5;
+	tagname_len = (size_t)(strchrnul(tagname, '\n') - tagname);
+
 	tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8);
 	if (!tagger) {
 		if (fake_missing_tagger)
@@ -821,6 +843,8 @@  static void handle_tag(const char *refname, struct tag *tag)
 
 	if (anonymize) {
 		refname = anonymize_refname(refname);
+		tagname = anonymize_tagname(tagname_len, tagname);
+		tagname_len = strlen(tagname);
 		if (message) {
 			static struct hashmap tags;
 			message = anonymize_str(&tags, anonymize_tag,
@@ -890,9 +914,7 @@  static void handle_tag(const char *refname, struct tag *tag)
 		printf("reset %s\nfrom %s\n\n",
 		       refname, oid_to_hex(&null_oid));
 	}
-	tagname = refname;
-	skip_prefix(tagname, "refs/tags/", &tagname);
-	printf("tag %s\n", tagname);
+	printf("tag %.*s\n", (int)tagname_len, tagname);
 	if (mark_tags) {
 		mark_next_object(&tag->object);
 		printf("mark :%"PRIu32"\n", last_idnum);
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 409b48e244..0bcc1bd54e 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -572,10 +572,17 @@  test_expect_success 'handling tags of blobs' '
 
 test_expect_success 'handling nested tags' '
 	git tag -a -m "This is a nested tag" nested muss &&
+	NESTED=$(git rev-parse --verify nested) &&
 	git fast-export --mark-tags nested >output &&
 	grep "^from $ZERO_OID$" output &&
 	grep "^tag nested$" output >tag_lines &&
-	test_line_count = 2 tag_lines
+	test_line_count = 1 tag_lines &&
+	rm -rf new &&
+	mkdir new &&
+	git --git-dir=new/.git init &&
+	(cd new &&
+	 git fast-import &&
+	 test $NESTED = $(git rev-parse --verify refs/tags/nested)) <output
 '
 
 test_expect_success 'directory becomes symlink'        '