Commit d4efa91b5a0b6d8d90ff1f847518ff59c17682e3

Stefan Sperling 2020-01-14T13:25:55

handle Git-style "lightweight" tags in got tag -l

diff --git a/got/got.c b/got/got.c
index 8dafea8..4b74c05 100644
--- a/got/got.c
+++ b/got/got.c
@@ -3764,6 +3764,7 @@ cmp_tags(void *arg, int *cmp, struct got_reference *ref1,
 	struct got_repository *repo = arg;
 	struct got_object_id *id1, *id2 = NULL;
 	struct got_tag_object *tag1 = NULL, *tag2 = NULL;
+	struct got_commit_object *commit1 = NULL, *commit2 = NULL;
 	time_t time1, time2;
 
 	*cmp = 0;
@@ -3772,18 +3773,31 @@ cmp_tags(void *arg, int *cmp, struct got_reference *ref1,
 	if (err)
 		return err;
 	err = got_object_open_as_tag(&tag1, repo, id1);
-	if (err)
-		goto done;
+	if (err) {
+		if (err->code != GOT_ERR_OBJ_TYPE)
+			goto done;
+		/* "lightweight" tag */
+		err = got_object_open_as_commit(&commit1, repo, id1);
+		if (err)
+			goto done;
+		time1 = got_object_commit_get_committer_time(commit1);
+	} else
+		time1 = got_object_tag_get_tagger_time(tag1);
 
 	err = got_ref_resolve(&id2, repo, ref2);
 	if (err)
 		goto done;
 	err = got_object_open_as_tag(&tag2, repo, id2);
-	if (err)
-		goto done;
-
-	time1 = got_object_tag_get_tagger_time(tag1);
-	time2 = got_object_tag_get_tagger_time(tag2);
+	if (err) {
+		if (err->code != GOT_ERR_OBJ_TYPE)
+			goto done;
+		/* "lightweight" tag */
+		err = got_object_open_as_commit(&commit2, repo, id2);
+		if (err)
+			goto done;
+		time2 = got_object_commit_get_committer_time(commit2);
+	} else
+		time2 = got_object_tag_get_tagger_time(tag2);
 
 	/* Put latest tags first. */
 	if (time1 < time2)
@@ -3799,6 +3813,10 @@ done:
 		got_object_tag_close(tag1);
 	if (tag2)
 		got_object_tag_close(tag2);
+	if (commit1)
+		got_object_commit_close(commit1);
+	if (commit2)
+		got_object_commit_close(commit2);
 	return err;
 }
 
@@ -3819,9 +3837,11 @@ list_tags(struct got_repository *repo, struct got_worktree *worktree)
 		const char *refname;
 		char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
 		char datebuf[26];
+		const char *tagger;
 		time_t tagger_time;
 		struct got_object_id *id;
 		struct got_tag_object *tag;
+		struct got_commit_object *commit = NULL;
 
 		refname = got_ref_get_name(re->ref);
 		if (strncmp(refname, "refs/tags/", 10) != 0)
@@ -3839,40 +3859,74 @@ list_tags(struct got_repository *repo, struct got_worktree *worktree)
 		if (err)
 			break;
 		err = got_object_open_as_tag(&tag, repo, id);
-		free(id);
-		if (err)
-			break;
-		printf("from: %s\n", got_object_tag_get_tagger(tag));
-		tagger_time = got_object_tag_get_tagger_time(tag);
+		if (err) {
+			if (err->code != GOT_ERR_OBJ_TYPE) {
+				free(id);
+				break;
+			}
+			/* "lightweight" tag */
+			err = got_object_open_as_commit(&commit, repo, id);
+			if (err) {
+				free(id);
+				break;
+			}
+			tagger = got_object_commit_get_committer(commit);
+			tagger_time =
+			    got_object_commit_get_committer_time(commit);
+			err = got_object_id_str(&id_str, id);
+			free(id);
+			if (err)
+				break;
+		} else {
+			free(id);
+			tagger = got_object_tag_get_tagger(tag);
+			tagger_time = got_object_tag_get_tagger_time(tag);
+			err = got_object_id_str(&id_str,
+			    got_object_tag_get_object_id(tag));
+			if (err)
+				break;
+		}
+		printf("from: %s\n", tagger);
 		datestr = get_datestr(&tagger_time, datebuf);
 		if (datestr)
 			printf("date: %s UTC\n", datestr);
-		err = got_object_id_str(&id_str,
-		    got_object_tag_get_object_id(tag));
-		if (err)
-			break;
-		switch (got_object_tag_get_object_type(tag)) {
-		case GOT_OBJ_TYPE_BLOB:
-			printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB, id_str);
-			break;
-		case GOT_OBJ_TYPE_TREE:
-			printf("object: %s %s\n", GOT_OBJ_LABEL_TREE, id_str);
-			break;
-		case GOT_OBJ_TYPE_COMMIT:
+		if (commit)
 			printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str);
-			break;
-		case GOT_OBJ_TYPE_TAG:
-			printf("object: %s %s\n", GOT_OBJ_LABEL_TAG, id_str);
-			break;
-		default:
-			break;
+		else {
+			switch (got_object_tag_get_object_type(tag)) {
+			case GOT_OBJ_TYPE_BLOB:
+				printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB,
+				    id_str);
+				break;
+			case GOT_OBJ_TYPE_TREE:
+				printf("object: %s %s\n", GOT_OBJ_LABEL_TREE,
+				    id_str);
+				break;
+			case GOT_OBJ_TYPE_COMMIT:
+				printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT,
+				    id_str);
+				break;
+			case GOT_OBJ_TYPE_TAG:
+				printf("object: %s %s\n", GOT_OBJ_LABEL_TAG,
+				    id_str);
+				break;
+			default:
+				break;
+			}
 		}
 		free(id_str);
-		tagmsg0 = strdup(got_object_tag_get_message(tag));
-		got_object_tag_close(tag);
-		if (tagmsg0 == NULL) {
-			err = got_error_from_errno("strdup");
-			break;
+		if (commit) {
+			err = got_object_commit_get_logmsg(&tagmsg0, commit);
+			if (err)
+				break;
+			got_object_commit_close(commit);
+		} else {
+			tagmsg0 = strdup(got_object_tag_get_message(tag));
+			got_object_tag_close(tag);
+			if (tagmsg0 == NULL) {
+				err = got_error_from_errno("strdup");
+				break;
+			}
 		}
 
 		tagmsg = tagmsg0;
diff --git a/regress/cmdline/tag.sh b/regress/cmdline/tag.sh
index ff9e9e5..070aa6e 100755
--- a/regress/cmdline/tag.sh
+++ b/regress/cmdline/tag.sh
@@ -170,5 +170,53 @@ function test_tag_list {
 	test_done "$testroot" "$ret"
 }
 
+function test_tag_list_lightweight {
+	local testroot=`test_init tag_list_lightweight`
+	local commit_id=`git_show_head $testroot/repo`
+	local tag=1.0.0
+	local tag2=2.0.0
+
+	# create "lightweight" tag with Git
+	(cd $testroot/repo && git tag $tag)
+	(cd $testroot/repo && git tag $tag2)
+
+	tag_id=`got ref -r $testroot/repo -l \
+		| grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2`
+	local tagger_time=`git_show_author_time $testroot/repo $tag`
+	d1=`env TZ=UTC date -r $tagger_time +"%a %b %e %X %Y UTC"`
+	tag_id2=`got ref -r $testroot/repo -l \
+		| grep "^refs/tags/$tag2" | tr -d ' ' | cut -d: -f2`
+	local tagger_time2=`git_show_author_time $testroot/repo $tag2`
+	d2=`env TZ=UTC date -r $tagger_time2 +"%a %b %e %X %Y UTC"`
+
+	got tag -r $testroot/repo -l > $testroot/stdout
+
+	echo "-----------------------------------------------" \
+		> $testroot/stdout.expected
+	echo "tag $tag2 $tag_id2" >> $testroot/stdout.expected
+	echo "from: $GOT_AUTHOR" >> $testroot/stdout.expected
+	echo "date: $d1" >> $testroot/stdout.expected
+	echo "object: commit $commit_id" >> $testroot/stdout.expected
+	echo " " >> $testroot/stdout.expected
+	echo " adding the test tree" >> $testroot/stdout.expected
+	echo " " >> $testroot/stdout.expected
+	echo "-----------------------------------------------" \
+		>> $testroot/stdout.expected
+	echo "tag $tag $tag_id" >> $testroot/stdout.expected
+	echo "from: $GOT_AUTHOR" >> $testroot/stdout.expected
+	echo "date: $d2" >> $testroot/stdout.expected
+	echo "object: commit $commit_id" >> $testroot/stdout.expected
+	echo " " >> $testroot/stdout.expected
+	echo " adding the test tree" >> $testroot/stdout.expected
+	echo " " >> $testroot/stdout.expected
+	cmp -s $testroot/stdout $testroot/stdout.expected
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_tag_create
 run_test test_tag_list
+run_test test_tag_list_lightweight