Commit a9a7967ac0657fea84ab0aaa97a1cb8f75c29e0e

Edward Thomson 2022-03-22T22:16:57

fetch: support OID refspec without dst Support the ability to create a refspec that is a single object ID without a destination.

diff --git a/src/libgit2/fetch.c b/src/libgit2/fetch.c
index 03d3845..e9f30d9 100644
--- a/src/libgit2/fetch.c
+++ b/src/libgit2/fetch.c
@@ -76,8 +76,11 @@ static int maybe_want_oid(git_remote *remote, git_refspec *spec)
 	GIT_ERROR_CHECK_ALLOC(oid_head);
 
 	git_oid_fromstr(&oid_head->oid, spec->src);
-	oid_head->name = git__strdup(spec->dst);
-	GIT_ERROR_CHECK_ALLOC(oid_head->name);
+
+	if (spec->dst) {
+		oid_head->name = git__strdup(spec->dst);
+		GIT_ERROR_CHECK_ALLOC(oid_head->name);
+	}
 
 	if (git_vector_insert(&remote->local_heads, oid_head) < 0 ||
 	    git_vector_insert(&remote->refs, oid_head) < 0)
diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c
index f6421b9..1a79faa 100644
--- a/src/libgit2/remote.c
+++ b/src/libgit2/remote.c
@@ -1895,8 +1895,11 @@ static int update_tips_for_spec(
 	if (git_oid__is_hexstr(spec->src)) {
 		git_oid id;
 
-		if ((error = git_oid_fromstr(&id, spec->src)) < 0 ||
-		    (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
+		if ((error = git_oid_fromstr(&id, spec->src)) < 0)
+			goto on_error;
+
+		if (spec->dst &&
+		     (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
 			goto on_error;
 
 		git_oid_cpy(&oid_head.oid, &id);
diff --git a/tests/libgit2/online/fetch.c b/tests/libgit2/online/fetch.c
index 7334f7e..5beb5b6 100644
--- a/tests/libgit2/online/fetch.c
+++ b/tests/libgit2/online/fetch.c
@@ -321,3 +321,32 @@ void test_online_fetch__reachable_commit(void)
 	git_object_free(obj);
 	git_remote_free(remote);
 }
+
+void test_online_fetch__reachable_commit_without_destination(void)
+{
+	git_remote *remote;
+	git_strarray refspecs;
+	git_object *obj;
+	git_oid expected_id;
+	git_str fetchhead = GIT_STR_INIT;
+	char *refspec = "2c349335b7f797072cf729c4f3bb0914ecb6dec9";
+
+	refspecs.strings = &refspec;
+	refspecs.count = 1;
+
+	git_oid_fromstr(&expected_id, "2c349335b7f797072cf729c4f3bb0914ecb6dec9");
+
+	cl_git_pass(git_remote_create(&remote, _repo, "test",
+		"https://github.com/libgit2/TestGitRepository"));
+	cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+
+	cl_git_fail_with(GIT_ENOTFOUND, git_revparse_single(&obj, _repo, "refs/success"));
+
+	cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
+	cl_assert_equal_s(fetchhead.ptr,
+		"2c349335b7f797072cf729c4f3bb0914ecb6dec9\t\t'2c349335b7f797072cf729c4f3bb0914ecb6dec9' of https://github.com/libgit2/TestGitRepository\n");
+
+	git_str_dispose(&fetchhead);
+	git_object_free(obj);
+	git_remote_free(remote);
+}