Commit 3ab57816015f59e9741017f866d3d87a4651b54f

Edward Thomson 2014-03-31T23:23:32

Merge pull request #2178 from libgit2/rb/fix-short-id Fix git_odb_short_id and git_odb_exists_prefix bugs

diff --git a/src/odb.c b/src/odb.c
index df21719..72d1506 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -640,7 +640,7 @@ int git_odb_exists_prefix(
 {
 	int error = GIT_ENOTFOUND, num_found = 0;
 	size_t i;
-	git_oid last_found = {{0}}, found;
+	git_oid key = {{0}}, last_found = {{0}}, found;
 
 	assert(db && short_id);
 
@@ -659,6 +659,11 @@ int git_odb_exists_prefix(
 		}
 	}
 
+	/* just copy valid part of short_id */
+	memcpy(&key.id, short_id->id, (len + 1) / 2);
+	if (len & 1)
+		key.id[len / 2] &= 0xF0;
+
 	for (i = 0; i < db->backends.length; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
@@ -666,7 +671,7 @@ int git_odb_exists_prefix(
 		if (!b->exists_prefix)
 			continue;
 
-		error = b->exists_prefix(&found, b, short_id, len);
+		error = b->exists_prefix(&found, b, &key, len);
 		if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
 			continue;
 		if (error)
@@ -683,11 +688,11 @@ int git_odb_exists_prefix(
 	}
 
 	if (!num_found)
-		return git_odb__error_notfound("no match for id prefix", short_id);
+		return git_odb__error_notfound("no match for id prefix", &key);
 	if (out)
 		git_oid_cpy(out, &last_found);
 
-	return error;
+	return 0;
 }
 
 int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
@@ -790,7 +795,7 @@ int git_odb_read_prefix(
 {
 	size_t i;
 	int error = GIT_ENOTFOUND;
-	git_oid found_full_oid = {{0}};
+	git_oid key = {{0}}, found_full_oid = {{0}};
 	git_rawobj raw;
 	void *data = NULL;
 	bool found = false;
@@ -809,13 +814,18 @@ int git_odb_read_prefix(
 			return 0;
 	}
 
+	/* just copy valid part of short_id */
+	memcpy(&key.id, short_id->id, (len + 1) / 2);
+	if (len & 1)
+		key.id[len / 2] &= 0xF0;
+
 	for (i = 0; i < db->backends.length; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
 
 		if (b->read_prefix != NULL) {
 			git_oid full_oid;
-			error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
+			error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len);
 			if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
 				continue;
 
@@ -836,7 +846,7 @@ int git_odb_read_prefix(
 	}
 
 	if (!found)
-		return git_odb__error_notfound("no match for prefix", short_id);
+		return git_odb__error_notfound("no match for prefix", &key);
 
 	if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
 		return -1;
diff --git a/tests/object/shortid.c b/tests/object/shortid.c
index fa1dac0..d854cb7 100644
--- a/tests/object/shortid.c
+++ b/tests/object/shortid.c
@@ -26,6 +26,13 @@ void test_object_shortid__select(void)
 	cl_assert_equal_s("ce01362", shorty.ptr);
 	git_object_free(obj);
 
+	git_oid_fromstr(&full, "038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2");
+	cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJ_ANY));
+	cl_git_pass(git_object_short_id(&shorty, obj));
+	cl_assert_equal_i(7, shorty.size);
+	cl_assert_equal_s("038d718", shorty.ptr);
+	git_object_free(obj);
+
 	git_oid_fromstr(&full, "dea509d097ce692e167dfc6a48a7a280cc5e877e");
 	cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJ_ANY));
 	cl_git_pass(git_object_short_id(&shorty, obj));
diff --git a/tests/odb/loose.c b/tests/odb/loose.c
index ef7136e..c91927c 100644
--- a/tests/odb/loose.c
+++ b/tests/odb/loose.c
@@ -66,23 +66,25 @@ void test_odb_loose__cleanup(void)
 
 void test_odb_loose__exists(void)
 {
-    git_oid id, id2;
+	git_oid id, id2;
 	git_odb *odb;
 
-    write_object_files(&one);
+	write_object_files(&one);
 	cl_git_pass(git_odb_open(&odb, "test-objects"));
 
-    cl_git_pass(git_oid_fromstr(&id, one.id));
+	cl_git_pass(git_oid_fromstr(&id, one.id));
+	cl_assert(git_odb_exists(odb, &id));
 
-    cl_assert(git_odb_exists(odb, &id));
+	cl_git_pass(git_oid_fromstrp(&id, "8b137891"));
+	cl_git_pass(git_odb_exists_prefix(&id2, odb, &id, 8));
+	cl_assert_equal_i(0, git_oid_streq(&id2, one.id));
 
-	cl_assert(git_odb_exists_prefix(&id2, odb, &id, 8));
-	cl_assert(git_oid_equal(&id, &id2));
+	/* Test for a missing object */
+	cl_git_pass(git_oid_fromstr(&id, "8b137891791fe96927ad78e64b0aad7bded08baa"));
+	cl_assert(!git_odb_exists(odb, &id));
 
-	/* Test for a non-existant object */
-    cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
-    cl_assert(!git_odb_exists(odb, &id2));
-	cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(NULL, odb, &id2, 8));
+	cl_git_pass(git_oid_fromstrp(&id, "8b13789a"));
+	cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(&id2, odb, &id, 8));
 
 	git_odb_free(odb);
 }
diff --git a/tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2 b/tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2
new file mode 100644
index 0000000..7350d98
Binary files /dev/null and b/tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2 differ