Commit Diff


commit - 8479284323dc11b5f08a6d8896bd806722b34634
commit + 303e2782e0a61a1cf76da52a945ab8645a8a78ca
blob - 967828d4c76fd2aff9b23dcefda926b5a25846f9
blob + 7f75a48345e3d059e9439ae651ab97f98016f253
--- got/got.1
+++ got/got.1
@@ -153,7 +153,7 @@ Check out files from the specified
 .Ar commit
 on the selected branch.
 The expected argument is a commit ID SHA1 hash or an existing reference
-which will be resolved to a commit ID.
+or tag name which will be resolved to a commit ID.
 An abbreviated hash argument will be expanded to a full SHA1 hash
 automatically, provided the abbreviation is unique.
 If this option is not specified, the most recent commit on the selected
@@ -223,7 +223,7 @@ This option requires that all paths in the work tree a
 Update the work tree to the specified
 .Ar commit .
 The expected argument is a commit ID SHA1 hash or an existing reference
-which will be resolved to a commit ID.
+or tag name which will be resolved to a commit ID.
 An abbreviated hash argument will be expanded to a full SHA1 hash
 automatically, provided the abbreviation is unique.
 If this option is not specified, the most recent commit on the work tree's
@@ -299,7 +299,7 @@ are as follows:
 Start traversing history at the specified
 .Ar commit .
 The expected argument is a commit ID SHA1 hash or an existing reference
-which will be resolved to a commit ID.
+or tag name which will be resolved to a commit ID.
 An abbreviated hash argument will be expanded to a full SHA1 hash
 automatically, provided the abbreviation is unique.
 If this option is not specified, default to the work tree's current branch
@@ -374,7 +374,7 @@ are as follows:
 Start traversing history at the specified
 .Ar commit .
 The expected argument is a commit ID SHA1 hash or an existing reference
-which will be resolved to a commit ID.
+or tag name which will be resolved to a commit ID.
 An abbreviated hash argument will be expanded to a full SHA1 hash
 automatically, provided the abbreviation is unique.
 .It Fl r Ar repository-path
@@ -412,7 +412,7 @@ are as follows:
 List files and directories as they appear in the specified
 .Ar commit .
 The expected argument is a commit ID SHA1 hash or an existing reference
-which will be resolved to a commit ID.
+or tag name which will be resolved to a commit ID.
 An abbreviated hash argument will be expanded to a full SHA1 hash
 automatically, provided the abbreviation is unique.
 .It Fl r Ar repository-path
blob - c05635e7b6ff95fde0b9d9366149284a8ce8a5d2
blob + e30bb0b425d521c0d92f3f3300537958da8a6919
--- got/got.c
+++ got/got.c
@@ -825,7 +825,20 @@ resolve_commit_arg(struct got_object_id **commit_id,
 {
 	const struct got_error *err;
 	struct got_reference *ref;
+	struct got_tag_object *tag;
 
+	err = got_repo_object_match_tag(&tag, commit_id_arg,
+	    GOT_OBJ_TYPE_COMMIT, repo);
+	if (err == NULL) {
+		*commit_id = got_object_id_dup(
+		    got_object_tag_get_object_id(tag));
+		if (*commit_id == NULL)
+			err = got_error_from_errno("got_object_id_dup");
+		got_object_tag_close(tag);
+		return err;
+	} else if (err->code != GOT_ERR_NO_OBJ)
+		return err;
+
 	err = got_ref_open(&ref, repo, commit_id_arg, 0);
 	if (err == NULL) {
 		err = got_ref_resolve(commit_id, repo, ref);
blob - 6b8435a8f69b24786fa17e77464465e2339529e4
blob + 16cdca51a48b0e15d4516534fe4efec46fe384af
--- include/got_repository.h
+++ include/got_repository.h
@@ -16,6 +16,7 @@
 
 struct got_repository;
 struct got_pathlist_head;
+struct got_tag_object;
 
 /* Open and close repositories. */
 const struct got_error *got_repo_open(struct got_repository**, const char *);
@@ -63,6 +64,13 @@ const struct got_error *got_repo_init(const char *);
 const struct got_error *got_repo_match_object_id_prefix(struct got_object_id **,
     const char *, int, struct got_repository *);
 
+/*
+ * Attempt to find a tag object with a given name and target object type.
+ * Return GOT_ERR_NO_OBJ if no matching tag can be found.
+ */
+const struct got_error *got_repo_object_match_tag(struct got_tag_object **,
+    const char *, int, struct got_repository *);
+
 /* A callback function which is invoked when a path is imported. */
 typedef const struct got_error *(*got_repo_import_cb)(void *, const char *);
 
blob - 8f6a04258f4bc1a53b5c23649fecf82243266b08
blob + 60e8e27f0ead8fbd0ef767acaaa4a7618826a10d
--- lib/repository.c
+++ lib/repository.c
@@ -1111,7 +1111,51 @@ done:
 		*id = NULL;
 	} else if (*id == NULL)
 		err = got_error(GOT_ERR_NO_OBJ);
+
+	return err;
+}
+
+const struct got_error *
+got_repo_object_match_tag(struct got_tag_object **tag, const char *name,
+    int obj_type, struct got_repository *repo)
+{
+	const struct got_error *err;
+	struct got_reflist_head refs;
+	struct got_reflist_entry *re;
+	struct got_object_id *tag_id;
+
+	SIMPLEQ_INIT(&refs);
+	*tag = NULL;
+
+	err = got_ref_list(&refs, repo);
+	if (err)
+		return err;
+
+	SIMPLEQ_FOREACH(re, &refs, entry) {
+		const char *refname;
+		refname = got_ref_get_name(re->ref);
+		if (got_ref_is_symbolic(re->ref) ||
+		    strncmp("refs/tags/", refname, 10) != 0)
+			continue;
+		refname += 10;
+		if (strcmp(refname, name) != 0)
+			continue;
+		err = got_ref_resolve(&tag_id, repo, re->ref);
+		if (err)
+			break;
+		err = got_object_open_as_tag(tag, repo, tag_id);
+		free(tag_id);
+		if (err)
+			break;
+		if (got_object_tag_get_object_type(*tag) == obj_type)
+			break;
+		got_object_tag_close(*tag);
+		*tag = NULL;
+	}
 
+	got_ref_list_free(&refs);
+	if (err == NULL && *tag == NULL)
+		err = got_error(GOT_ERR_NO_OBJ);
 	return err;
 }
 
blob - e1dbec4d3615974a53043664c84ecffb8591e6d1
blob + ccd7980f4fa22dcd0dfb01b2e5a895ca27b01246
--- regress/cmdline/blame.sh
+++ regress/cmdline/blame.sh
@@ -57,4 +57,45 @@ function test_blame_basic {
 	test_done "$testroot" "$ret"
 }
 
+function test_blame_tag {
+	local testroot=`test_init blame_tag`
+	local tag=1.0.0
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+	echo 1 > $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 1" > /dev/null)
+	local commit1=`git_show_head $testroot/repo`
+
+	echo 2 >> $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 2" > /dev/null)
+	local commit2=`git_show_head $testroot/repo`
+
+	(cd $testroot/repo && git tag -a -m "test" $tag)
+
+	echo 3 >> $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m "change 3" > /dev/null)
+	local commit3=`git_show_head $testroot/repo`
+
+	(cd $testroot/wt && got blame -c $tag alpha > $testroot/stdout)
+
+	local short_commit1=`trim_obj_id 32 $commit1`
+	local short_commit2=`trim_obj_id 32 $commit2`
+
+	echo "$short_commit1 1" > $testroot/stdout.expected
+	echo "$short_commit2 2" >> $testroot/stdout.expected
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_blame_basic
+run_test test_blame_tag
blob - 2f03e3f22ca34a05036830cc0af2ab635a0799fb
blob + b967040ffd7b21b1c0cda99ba37b84c58b0a8cb3
--- regress/cmdline/checkout.sh
+++ regress/cmdline/checkout.sh
@@ -210,8 +210,51 @@ function test_checkout_commit_from_wrong_branch {
 	test_done "$testroot" "$ret"
 }
 
+function test_checkout_tag {
+	local testroot=`test_init checkout_tag`
+	local tag="1.0.0"
+
+	(cd $testroot/repo && git tag -a -m "test" $tag)
+
+	echo "A  $testroot/wt/alpha" > $testroot/stdout.expected
+	echo "A  $testroot/wt/beta" >> $testroot/stdout.expected
+	echo "A  $testroot/wt/epsilon/zeta" >> $testroot/stdout.expected
+	echo "A  $testroot/wt/gamma/delta" >> $testroot/stdout.expected
+	echo "Now shut up and hack" >> $testroot/stdout.expected
+
+	got checkout -c $tag $testroot/repo $testroot/wt > $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "alpha" > $testroot/content.expected
+	echo "beta" >> $testroot/content.expected
+	echo "zeta" >> $testroot/content.expected
+	echo "delta" >> $testroot/content.expected
+	cat $testroot/wt/alpha $testroot/wt/beta $testroot/wt/epsilon/zeta \
+	    $testroot/wt/gamma/delta > $testroot/content
+
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_checkout_basic
 run_test test_checkout_dir_exists
 run_test test_checkout_dir_not_empty
 run_test test_checkout_sets_xbit
 run_test test_checkout_commit_from_wrong_branch
+run_test test_checkout_tag
blob - 11f0724fc95a705812f15b4d42b2e204fbaebb8f
blob + fa21749675c7093587f9e3be79dea42b3d341fe4
--- regress/cmdline/log.sh
+++ regress/cmdline/log.sh
@@ -110,6 +110,33 @@ function test_log_in_worktree {
 	test_done "$testroot" "0"
 }
 
+function test_log_tag {
+	local testroot=`test_init log_tag`
+	local commit_id=`git_show_head $testroot/repo`
+	local tag="1.0.0"
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/repo && git tag -a -m "test" $tag)
+
+	echo "commit $commit_id (master)" > $testroot/stdout.expected
+	(cd $testroot/wt && got log -l1 -c $tag | grep ^commit \
+		> $testroot/stdout)
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
+
 run_test test_log_in_repo
 run_test test_log_in_bare_repo
 run_test test_log_in_worktree
+run_test test_log_tag
blob - e4c536758c2ec2596e840c7ca32b464bf1864be3
blob + ec4a92da12fc55731c312cca75266994641a944b
--- regress/cmdline/update.sh
+++ regress/cmdline/update.sh
@@ -1500,6 +1500,39 @@ function test_update_bumps_base_commit_id {
 	local head_rev=`git_show_head $testroot/repo`
 	echo "M  epsilon/zeta" > $testroot/stdout.expected
 	echo "Created commit $head_rev" >> $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	test_done "$testroot" "$ret"
+}
+
+function test_update_tag {
+	local testroot=`test_init update_tag`
+	local tag="1.0.0"
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "modified alpha" > $testroot/repo/alpha
+	git_commit $testroot/repo -m "modified alpha"
+	(cd $testroot/repo && git tag -m "test" -a $tag)
+
+	echo "U  alpha" > $testroot/stdout.expected
+	echo -n "Updated to commit " >> $testroot/stdout.expected
+	git_show_head $testroot/repo >> $testroot/stdout.expected
+	echo >> $testroot/stdout.expected
+
+	(cd $testroot/wt && got update -c $tag > $testroot/stdout)
+
 	cmp -s $testroot/stdout.expected $testroot/stdout
 	ret="$?"
 	if [ "$ret" != "0" ]; then
@@ -1508,6 +1541,14 @@ function test_update_bumps_base_commit_id {
 		return 1
 	fi
 
+	echo "modified alpha" > $testroot/content.expected
+	cat $testroot/wt/alpha > $testroot/content
+
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+	fi
 	test_done "$testroot" "$ret"
 }
 
@@ -1540,3 +1581,4 @@ run_test test_update_moved_branch_ref
 run_test test_update_to_another_branch
 run_test test_update_to_commit_on_wrong_branch
 run_test test_update_bumps_base_commit_id
+run_test test_update_tag