Commit ab5b3f37f5e74805250685a08fa9f4220e5dba12

Edward Thomson 2021-12-23T14:09:09

Merge pull request #6095 from yoichi/better-compatiblity-for-at-time-notation Better revparse compatibility for at time notation

diff --git a/src/date.c b/src/date.c
index 1529276..0e5ffc9 100644
--- a/src/date.c
+++ b/src/date.c
@@ -853,7 +853,7 @@ static git_time_t approxidate_str(const char *date,
 	}
 	pending_number(&tm, &number);
 	if (!touched)
-		*error_ret = 1;
+		*error_ret = -1;
 	return update_tm(&tm, &now, 0);
 }
 
@@ -872,7 +872,7 @@ int git_date_parse(git_time_t *out, const char *date)
 		return -1;
 
 	*out = approxidate_str(date, time_sec, &error_ret);
-   return error_ret;
+	return error_ret;
 }
 
 int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset)
diff --git a/src/revparse.c b/src/revparse.c
index 52dd072..5d3ff77 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -208,7 +208,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
 {
 	git_reflog *reflog;
 	size_t numentries;
-	const git_reflog_entry *entry;
+	const git_reflog_entry *entry = NULL;
 	bool search_by_pos = (identifier <= 100000000);
 
 	if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
@@ -237,8 +237,15 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
 			break;
 		}
 
-		if (i == numentries)
-			goto notfound;
+		if (i == numentries) {
+			if (entry == NULL)
+				goto notfound;
+
+			/*
+			 * TODO: emit a warning (log for 'branch' only goes back to ...)
+			 */
+			git_oid_cpy(oid, git_reflog_entry_id_new(entry));
+		}
 	}
 
 	git_reflog_free(reflog);
@@ -345,8 +352,10 @@ static int handle_at_syntax(git_object **out, git_reference **ref, const char *s
 		goto cleanup;
 	}
 
-	if (git_date_parse(&timestamp, curly_braces_content) < 0)
+	if (git_date_parse(&timestamp, curly_braces_content) < 0) {
+		error = GIT_EINVALIDSPEC;
 		goto cleanup;
+	}
 
 	error = retrieve_revobject_from_reflog(out, ref, repo, git_str_cstr(&identifier), (size_t)timestamp);
 
diff --git a/tests/date/date.c b/tests/date/date.c
index f70b4fe..82b5c67 100644
--- a/tests/date/date.c
+++ b/tests/date/date.c
@@ -13,3 +13,10 @@ void test_date_date__overflow(void)
    cl_assert(d2038 < d2039);
 #endif
 }
+
+void test_date_date__invalid_date(void)
+{
+   git_time_t d;
+   cl_git_fail(git_date_parse(&d, ""));
+   cl_git_fail(git_date_parse(&d, "NEITHER_INTEGER_NOR_DATETIME"));
+}
diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
index 0e446d9..ab31d36 100644
--- a/tests/fetchhead/nonetwork.c
+++ b/tests/fetchhead/nonetwork.c
@@ -508,7 +508,7 @@ void test_fetchhead_nonetwork__create_with_multiple_refspecs(void)
 		int i;
 		struct prefix_count prefix_counts[] = {
 			{"refs/notes/", 0, 1},
-			{"refs/heads/", 0, 12},
+			{"refs/heads/", 0, 13},
 			{"refs/tags/", 0, 7},
 			{NULL, 0, 0},
 		};
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
index e299d3e..c54f454 100644
--- a/tests/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -45,7 +45,7 @@ void test_network_fetchlocal__complete(void)
 	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(19, (int)refnames.count);
+	cl_assert_equal_i(20, (int)refnames.count);
 	cl_assert(callcount > 0);
 
 	git_strarray_dispose(&refnames);
@@ -74,7 +74,7 @@ void test_network_fetchlocal__prune(void)
 	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(19, (int)refnames.count);
+	cl_assert_equal_i(20, (int)refnames.count);
 	cl_assert(callcount > 0);
 	git_strarray_dispose(&refnames);
 	git_remote_free(origin);
@@ -88,7 +88,7 @@ void test_network_fetchlocal__prune(void)
 	cl_git_pass(git_remote_prune(origin, &options.callbacks));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(18, (int)refnames.count);
+	cl_assert_equal_i(19, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 	git_remote_free(origin);
 
@@ -101,7 +101,7 @@ void test_network_fetchlocal__prune(void)
 	cl_git_pass(git_remote_prune(origin, &options.callbacks));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(17, (int)refnames.count);
+	cl_assert_equal_i(18, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 	git_remote_free(origin);
 
@@ -166,7 +166,7 @@ void test_network_fetchlocal__prune_overlapping(void)
 	assert_ref_exists(repo, "refs/remotes/origin/master");
 	assert_ref_exists(repo, "refs/remotes/origin/pr/42");
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(20, (int)refnames.count);
+	cl_assert_equal_i(21, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 
 	cl_git_pass(git_config_delete_multivar(config, "remote.origin.fetch", "refs"));
@@ -181,7 +181,7 @@ void test_network_fetchlocal__prune_overlapping(void)
 	assert_ref_exists(repo, "refs/remotes/origin/master");
 	assert_ref_exists(repo, "refs/remotes/origin/pr/42");
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(20, (int)refnames.count);
+	cl_assert_equal_i(21, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 
 	cl_git_pass(git_config_delete_multivar(config, "remote.origin.fetch", "refs"));
@@ -221,7 +221,7 @@ void test_network_fetchlocal__fetchprune(void)
 	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(19, (int)refnames.count);
+	cl_assert_equal_i(20, (int)refnames.count);
 	cl_assert(callcount > 0);
 	git_strarray_dispose(&refnames);
 	git_remote_free(origin);
@@ -235,7 +235,7 @@ void test_network_fetchlocal__fetchprune(void)
 	cl_git_pass(git_remote_prune(origin, &options.callbacks));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(18, (int)refnames.count);
+	cl_assert_equal_i(19, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 	git_remote_free(origin);
 
@@ -251,7 +251,7 @@ void test_network_fetchlocal__fetchprune(void)
 	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(17, (int)refnames.count);
+	cl_assert_equal_i(18, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 	git_remote_free(origin);
 
@@ -335,7 +335,7 @@ void test_network_fetchlocal__partial(void)
 	git_strarray_dispose(&refnames);
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(20, (int)refnames.count); /* 18 remote + 1 local */
+	cl_assert_equal_i(21, (int)refnames.count); /* 18 remote + 1 local */
 	cl_assert(callcount > 0);
 
 	git_strarray_dispose(&refnames);
@@ -418,7 +418,7 @@ void test_network_fetchlocal__multi_remotes(void)
 	cl_git_pass(git_remote_fetch(test, NULL, &options, NULL));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(33, (int)refnames.count);
+	cl_assert_equal_i(35, (int)refnames.count);
 	git_strarray_dispose(&refnames);
 
 	cl_git_pass(git_remote_set_url(repo, "test_with_pushurl", cl_git_fixture_url("testrepo.git")));
@@ -426,7 +426,7 @@ void test_network_fetchlocal__multi_remotes(void)
 	cl_git_pass(git_remote_fetch(test2, NULL, &options, NULL));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
-	cl_assert_equal_i(45, (int)refnames.count);
+	cl_assert_equal_i(48, (int)refnames.count);
 
 	git_strarray_dispose(&refnames);
 	git_remote_free(test);
diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c
index f174044..2007f37 100644
--- a/tests/network/remote/local.c
+++ b/tests/network/remote/local.c
@@ -61,7 +61,7 @@ void test_network_remote_local__retrieve_advertised_references(void)
 
 	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
 
-	cl_assert_equal_i(refs_len, 29);
+	cl_assert_equal_i(refs_len, 30);
 }
 
 void test_network_remote_local__retrieve_advertised_before_connect(void)
@@ -85,7 +85,7 @@ void test_network_remote_local__retrieve_advertised_references_after_disconnect(
 
 	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
 
-	cl_assert_equal_i(refs_len, 29);
+	cl_assert_equal_i(refs_len, 30);
 }
 
 void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
@@ -100,7 +100,7 @@ void test_network_remote_local__retrieve_advertised_references_from_spaced_repos
 
 	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
 
-	cl_assert_equal_i(refs_len, 29);
+	cl_assert_equal_i(refs_len, 30);
 
 	git_remote_free(remote);	/* Disconnect from the "spaced repo" before the cleanup */
 	remote = NULL;
diff --git a/tests/refs/branches/iterator.c b/tests/refs/branches/iterator.c
index ca366c9..e086681 100644
--- a/tests/refs/branches/iterator.c
+++ b/tests/refs/branches/iterator.c
@@ -48,7 +48,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count)
 
 void test_refs_branches_iterator__retrieve_all_branches(void)
 {
-	assert_retrieval(GIT_BRANCH_ALL, 14);
+	assert_retrieval(GIT_BRANCH_ALL, 15);
 }
 
 void test_refs_branches_iterator__retrieve_remote_branches(void)
@@ -58,7 +58,7 @@ void test_refs_branches_iterator__retrieve_remote_branches(void)
 
 void test_refs_branches_iterator__retrieve_local_branches(void)
 {
-	assert_retrieval(GIT_BRANCH_LOCAL, 12);
+	assert_retrieval(GIT_BRANCH_LOCAL, 13);
 }
 
 struct expectations {
diff --git a/tests/refs/foreachglob.c b/tests/refs/foreachglob.c
index 3ff18a2..b208a95 100644
--- a/tests/refs/foreachglob.c
+++ b/tests/refs/foreachglob.c
@@ -48,8 +48,8 @@ static void assert_retrieval(const char *glob, int expected_count)
 
 void test_refs_foreachglob__retrieve_all_refs(void)
 {
-	/* 12 heads (including one packed head) + 1 note + 2 remotes + 7 tags + 1 blob */
-	assert_retrieval("*", 23);
+	/* 13 heads (including one packed head) + 1 note + 2 remotes + 7 tags + 1 blob */
+	assert_retrieval("*", 24);
 }
 
 void test_refs_foreachglob__retrieve_remote_branches(void)
@@ -59,7 +59,7 @@ void test_refs_foreachglob__retrieve_remote_branches(void)
 
 void test_refs_foreachglob__retrieve_local_branches(void)
 {
-	assert_retrieval("refs/heads/*", 12);
+	assert_retrieval("refs/heads/*", 13);
 }
 
 void test_refs_foreachglob__retrieve_nonexistant(void)
diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c
index 8d52755..a4f9e62 100644
--- a/tests/refs/iterator.c
+++ b/tests/refs/iterator.c
@@ -28,6 +28,7 @@ static const char *refnames[] = {
 	"refs/heads/test",
 	"refs/heads/track-local",
 	"refs/heads/trailing",
+	"refs/heads/with-empty-log",
 	"refs/notes/fanout",
 	"refs/remotes/test/master",
 	"refs/tags/annotated_tag_to_blob",
@@ -58,6 +59,7 @@ static const char *refnames_with_symlink[] = {
 	"refs/heads/test",
 	"refs/heads/track-local",
 	"refs/heads/trailing",
+	"refs/heads/with-empty-log",
 	"refs/notes/fanout",
 	"refs/remotes/test/master",
 	"refs/tags/annotated_tag_to_blob",
diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c
index d022497..5fb7585 100644
--- a/tests/refs/revparse.c
+++ b/tests/refs/revparse.c
@@ -399,7 +399,7 @@ void test_refs_revparse__date(void)
 	 * a65fedf HEAD@{1335806603 -0900}: commit:
 	 * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour
 	 */
-	test_object("HEAD@{10 years ago}", NULL);
+	test_object("HEAD@{10 years ago}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
 
 	test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
 	test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
@@ -417,11 +417,12 @@ void test_refs_revparse__date(void)
 
 
 	/*
-	 * $ git reflog -1 "master@{2012-04-30 17:22:42 +0000}"
-	 * warning: Log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800.
+	 * $ git rev-parse "master@{2012-04-30 17:22:42 +0000}"
+	 * warning: log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800
+	 * be3563ae3f795b2b4353bcce3a527ad0a4f7f644
 	 */
-	test_object("master@{2012-04-30 17:22:42 +0000}", NULL);
-	test_object("master@{2012-04-30 09:22:42 -0800}", NULL);
+	test_object("master@{2012-04-30 17:22:42 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+	test_object("master@{2012-04-30 09:22:42 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
 
 	/*
 	 * $ git reflog -1 "master@{2012-04-30 17:22:43 +0000}"
@@ -451,6 +452,25 @@ void test_refs_revparse__date(void)
 	 */
 	test_object("master@{1335806603}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
 	test_object("master@{1335806602}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+	/*
+	 * $ git rev-parse "with-empty-log@{2 days ago}" --
+	 * fatal: log for refs/heads/with-empty-log is empty
+	 */
+	test_object("with-empty-log@{2 days ago}", NULL);
+}
+
+void test_refs_revparse__invalid_date(void)
+{
+	/*
+	 * $ git rev-parse HEAD@{} --
+	 * fatal: bad revision 'HEAD@{}'
+	 *
+	 * $ git rev-parse HEAD@{NEITHER_INTEGER_NOR_DATETIME} --
+	 * fatal: bad revision 'HEAD@{NEITHER_INTEGER_NOR_DATETIME}'
+	 */
+	test_object("HEAD@{}", NULL);
+	test_object("HEAD@{NEITHER_INTEGER_NOR_DATETIME}", NULL);
 }
 
 void test_refs_revparse__colon(void)
diff --git a/tests/resources/testrepo.git/logs/refs/heads/with-empty-log b/tests/resources/testrepo.git/logs/refs/heads/with-empty-log
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/with-empty-log
diff --git a/tests/resources/testrepo.git/refs/heads/with-empty-log b/tests/resources/testrepo.git/refs/heads/with-empty-log
new file mode 100644
index 0000000..dae4cb2
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/with-empty-log
@@ -0,0 +1 @@
+8496071c1b46c854b31185ea97743be6a8774479