Commit 68642bce4b5ec895b25f48f402e8537b069f1924

Russell Belfer 2013-09-10T16:54:37

Merge pull request #1841 from libgit2/ntk/fix/loose_ambiguous Make odb_loose return EAMBIGUOUS when required

diff --git a/src/fileops.c b/src/fileops.c
index 92cda82..126d45f 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -869,6 +869,7 @@ typedef struct {
 	uint32_t flags;
 	uint32_t mkdir_flags;
 	mode_t dirmode;
+	int error;
 } cp_r_info;
 
 #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
@@ -907,20 +908,23 @@ static int _cp_r_callback(void *ref, git_buf *from)
 		return 0;
 
 	if (git_buf_joinpath(
-			&info->to, info->to_root, from->ptr + info->from_prefix) < 0)
-		return -1;
+			&info->to, info->to_root, from->ptr + info->from_prefix) < 0) {
+		error = -1;
+		goto exit;
+	}
 
 	if (p_lstat(info->to.ptr, &to_st) < 0) {
 		if (errno != ENOENT && errno != ENOTDIR) {
 			giterr_set(GITERR_OS,
 				"Could not access %s while copying files", info->to.ptr);
-			return -1;
+			error = -1;
+			goto exit;
 		}
 	} else
 		exists = true;
 
 	if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
-		return error;
+		goto exit;
 
 	if (S_ISDIR(from_st.st_mode)) {
 		mode_t oldmode = info->dirmode;
@@ -934,13 +938,14 @@ static int _cp_r_callback(void *ref, git_buf *from)
 			error = _cp_r_mkdir(info, from);
 
 		/* recurse onto target directory */
-		if (!error && (!exists || S_ISDIR(to_st.st_mode)))
-			error = git_path_direach(from, _cp_r_callback, info);
+		if (!error && (!exists || S_ISDIR(to_st.st_mode)) &&
+			((error = git_path_direach(from, _cp_r_callback, info)) == GIT_EUSER))
+			error = info->error;
 
 		if (oldmode != 0)
 			info->dirmode = oldmode;
 
-		return error;
+		goto exit;
 	}
 
 	if (exists) {
@@ -950,7 +955,8 @@ static int _cp_r_callback(void *ref, git_buf *from)
 		if (p_unlink(info->to.ptr) < 0) {
 			giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
 				info->to.ptr);
-			return -1;
+			error = -1;
+			goto exit;
 		}
 	}
 
@@ -963,7 +969,7 @@ static int _cp_r_callback(void *ref, git_buf *from)
 	/* Make container directory on demand if needed */
 	if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
 		(error = _cp_r_mkdir(info, from)) < 0)
-		return error;
+		goto exit;
 
 	/* make symlink or regular file */
 	if (S_ISLNK(from_st.st_mode))
@@ -977,6 +983,8 @@ static int _cp_r_callback(void *ref, git_buf *from)
 		error = git_futils_cp(from->ptr, info->to.ptr, usemode);
 	}
 
+exit:
+	info->error = error;
 	return error;
 }
 
@@ -997,6 +1005,7 @@ int git_futils_cp_r(
 	info.flags   = flags;
 	info.dirmode = dirmode;
 	info.from_prefix = path.size;
+	info.error = 0;
 	git_buf_init(&info.to, 0);
 
 	/* precalculate mkdir flags */
@@ -1018,6 +1027,9 @@ int git_futils_cp_r(
 	git_buf_free(&path);
 	git_buf_free(&info.to);
 
+	if (error == GIT_EUSER)
+		error = info.error;
+
 	return error;
 }
 
diff --git a/src/odb_loose.c b/src/odb_loose.c
index ce63f46..4ff5715 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -499,7 +499,7 @@ static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
 	}
 
 	if (sstate->found > 1)
-		return git_odb__error_ambiguous("multiple matches in loose objects");
+		return GIT_EAMBIGUOUS;
 
 	return 0;
 }
@@ -545,12 +545,16 @@ static int locate_object_short_oid(
 	/* Explore directory to find a unique object matching short_oid */
 	error = git_path_direach(
 		object_location, fn_locate_object_short_oid, &state);
-	if (error)
+
+	if (error && error != GIT_EUSER)
 		return error;
 
 	if (!state.found)
 		return git_odb__error_notfound("no matching loose object for prefix", short_oid);
 
+	if (state.found > 1)
+		return git_odb__error_ambiguous("multiple matches in loose objects");
+
 	/* Convert obtained hex formatted oid to raw */
 	error = git_oid_fromstr(res_oid, (char *)state.res_oid);
 	if (error)
diff --git a/src/odb_pack.c b/src/odb_pack.c
index d24b4aa..cadc93a 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -336,7 +336,7 @@ static int pack_backend__refresh(git_odb_backend *_backend)
 	git_buf_free(&path);
 
 	if (error < 0)
-		return error;
+		return -1;
 
 	git_vector_sort(&backend->packs);
 	return 0;
diff --git a/src/path.c b/src/path.c
index 7c1ec2c..56b0b49 100644
--- a/src/path.c
+++ b/src/path.c
@@ -765,10 +765,10 @@ int git_path_direach(
 
 		git_buf_truncate(path, wd_len); /* restore path */
 
-		if (result < 0) {
+		if (result) {
 			closedir(dir);
 			git__free(de_buf);
-			return -1;
+			return GIT_EUSER;
 		}
 	}
 
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 04516a5..894ff7c 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -299,7 +299,7 @@ static int packed_loadloose(refdb_fs_backend *backend)
 
 	git_buf_free(&refs_path);
 
-	return error;
+	return (error == GIT_EUSER) ? -1 : error;
 }
 
 static int refdb_fs_backend__exists(
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
index 9657054..37d3981 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests-clar/refs/revparse.c
@@ -544,6 +544,37 @@ void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void)
 		GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90"));
 }
 
+/*
+ * $ echo "aabqhq" | git hash-object -t blob --stdin
+ * dea509d0b3cb8ee0650f6ca210bc83f4678851ba
+ * 
+ * $ echo "aaazvc" | git hash-object -t blob --stdin
+ * dea509d097ce692e167dfc6a48a7a280cc5e877e
+ */
+void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void)
+{
+	git_repository *repo;
+	git_index *index;
+	git_object *obj;
+
+	repo = cl_git_sandbox_init("testrepo");
+
+	cl_git_mkfile("testrepo/one.txt", "aabqhq\n");
+	cl_git_mkfile("testrepo/two.txt", "aaazvc\n");
+	
+	cl_git_pass(git_repository_index(&index, repo));
+	cl_git_pass(git_index_add_bypath(index, "one.txt"));
+	cl_git_pass(git_index_add_bypath(index, "two.txt"));
+	
+	cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS);
+
+	cl_git_pass(git_revparse_single(&obj, repo, "dea509d09"));
+
+	git_object_free(obj);
+	git_index_free(index);
+	cl_git_sandbox_cleanup();
+}
+
 void test_refs_revparse__issue_994(void)
 {
 	git_repository *repo;