Commit 5bb0dc9390edf9f056cd67c12c258b10d0648871

Ben Straub 2012-09-13T14:02:46

ODB: re-load packfiles on failed lookup The old method was avoiding re-loading of packfiles by watching the mtime of the pack directory. This causes the ODB to become stale if the directory and packfile are written within the same clock millisecond, as when cloning a fairly small repo. This method tries to find the object in the cached packs, and forces a refresh when that fails. This will cause extra stat'ing on a miss, but speeds up the success case and avoids this race condition.

diff --git a/src/odb_pack.c b/src/odb_pack.c
index b4f958b..5d3f0e6 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -233,10 +233,11 @@ static int packfile_load__cb(void *_data, git_buf *path)
 	return git_vector_insert(&backend->packs, pack);
 }
 
-static int packfile_refresh_all(struct pack_backend *backend)
+static int packfile_refresh_all_maybe(struct pack_backend *backend, bool force)
 {
 	int error;
 	struct stat st;
+	git_buf path = GIT_BUF_INIT;
 
 	if (backend->pack_folder == NULL)
 		return 0;
@@ -244,8 +245,7 @@ static int packfile_refresh_all(struct pack_backend *backend)
 	if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
 		return git_odb__error_notfound("failed to refresh packfiles", NULL);
 
-	if (st.st_mtime != backend->pack_folder_mtime) {
-		git_buf path = GIT_BUF_INIT;
+	if (force || st.st_mtime != backend->pack_folder_mtime) {
 		git_buf_sets(&path, backend->pack_folder);
 
 		/* reload all packs */
@@ -263,9 +263,14 @@ static int packfile_refresh_all(struct pack_backend *backend)
 	return 0;
 }
 
-static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
+static int packfile_refresh_all(struct pack_backend *backend) {
+	return packfile_refresh_all_maybe(backend, false);
+}
+
+static int pack_entry_find_inner(struct git_pack_entry *e,
+											struct pack_backend *backend,
+											const git_oid *oid)
 {
-	int error;
 	unsigned int i;
 	struct git_pack_file *last_found = backend->last_found;
 
@@ -273,9 +278,6 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
 		git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
 		return 0;
 
-	if ((error = packfile_refresh_all(backend)) < 0)
-		return error;
-
 	for (i = 0; i < backend->packs.length; ++i) {
 		struct git_pack_file *p;
 
@@ -289,6 +291,24 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
 		}
 	}
 
+	return -1;
+}
+
+static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
+{
+	int error;
+
+	if (backend->last_found &&
+		git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
+		return 0;
+
+	if (!pack_entry_find_inner(e, backend, oid))
+		return 0;
+	if ((error = packfile_refresh_all_maybe(backend, true)) < 0)
+		return error;
+	if (!pack_entry_find_inner(e, backend, oid))
+		return 0;
+
 	return git_odb__error_notfound("failed to find pack entry", oid);
 }
 
diff --git a/tests-clar/clone/clone.c b/tests-clar/clone/clone.c
index 4cca15f..237e607 100644
--- a/tests-clar/clone/clone.c
+++ b/tests-clar/clone/clone.c
@@ -5,7 +5,7 @@
 
 #define DO_LOCAL_TEST 0
 #define DO_LIVE_NETWORK_TESTS 0
-#define LIVE_REPO_URL "http://github.com/libgit2/node-gitteh"
+#define LIVE_REPO_URL "git://github.com/nulltoken/TestGitRepository"
 
 
 static git_repository *g_repo;