Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2011-03-17 00:13:28 +0300
committerVicent Marti <tanoku@gmail.com>2011-03-17 00:28:27 +0300
commit7341bf87b111dfa0cf12761389e0e7a118363f42 (patch)
treeeb696fa7dbaf0d867b2c2e727b1888cacfd2d4cf
parent9f81a37aa416446726a00996dce01b02372a43b1 (diff)
Refs are now always in-sync on disk
-rw-r--r--src/refs.c185
-rw-r--r--src/refs.h2
2 files changed, 131 insertions, 56 deletions
diff --git a/src/refs.c b/src/refs.c
index ea39608df..40b80ec9b 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -59,16 +59,16 @@ static uint32_t reftable_hash(const void *key, int hash_id)
static void reference_free(git_reference *reference);
static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type);
+static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name);
/* loose refs */
static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content);
static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content);
-static int loose_read(gitfo_buf *file_content, const char *name, const char *repo_path);
-static int loose_lookup( git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic);
+static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic);
static int loose_write(git_reference *ref);
+static int loose_update(git_reference *ref);
/* packed refs */
-static int packed_readpack(gitfo_buf *packfile, const char *repo_path);
static int packed_parse_peel(reference_oid *tag_ref, const char **buffer_out, const char *buffer_end);
static int packed_parse_oid(reference_oid **ref_out, git_repository *repo, const char **buffer_out, const char *buffer_end);
static int packed_load(git_repository *repo);
@@ -146,12 +146,73 @@ cleanup:
return error;
}
+static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name)
+{
+ struct stat st;
+ char path[GIT_PATH_MAX];
+
+ /* Determine the full path of the file */
+ git__joinpath(path, repo_path, ref_name);
+
+ if (gitfo_stat(path, &st) < 0)
+ return GIT_ENOTFOUND;
+
+ if (S_ISDIR(st.st_mode))
+ return GIT_EOBJCORRUPTED;
+
+ if (mtime)
+ *mtime = st.st_mtime;
+
+ if (file_content)
+ return gitfo_read_file(file_content, path);
+
+ return GIT_SUCCESS;
+}
+
/*****************************************
* Internal methods - Loose references
*****************************************/
+static int loose_update(git_reference *ref)
+{
+ int error;
+ time_t ref_time;
+ gitfo_buf ref_file = GITFO_BUF_INIT;
+
+ if (ref->type & GIT_REF_PACKED)
+ return packed_load(ref->owner);
+
+ error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ if (ref_time == ref->mtime)
+ return GIT_SUCCESS;
+
+ error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ if (ref->type == GIT_REF_SYMBOLIC)
+ error = loose_parse_symbolic(ref, &ref_file);
+ else if (ref->type == GIT_REF_OID)
+ error = loose_parse_oid(ref, &ref_file);
+ else
+ error = GIT_EINVALIDREFSTATE;
+
+ gitfo_free_buf(&ref_file);
+
+cleanup:
+ if (error != GIT_SUCCESS) {
+ reference_free(ref);
+ git_hashtable_remove(ref->owner->references.loose_cache, ref->name);
+ }
+
+ return error;
+}
+
static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
{
const unsigned int header_len = strlen(GIT_SYMREF);
@@ -172,6 +233,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
refname_start += header_len;
+ free(ref_sym->target);
ref_sym->target = git__strdup(refname_start);
if (ref_sym->target == NULL)
return GIT_ENOMEM;
@@ -213,27 +275,6 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
return GIT_SUCCESS;
}
-static int loose_read(gitfo_buf *file_content, const char *name, const char *repo_path)
-{
- int error = GIT_SUCCESS;
- char ref_path[GIT_PATH_MAX];
-
- /* Determine the full path of the ref */
- git__joinpath(ref_path, repo_path, name);
-
- /* Does it even exist ? */
- if (gitfo_exists(ref_path) < GIT_SUCCESS)
- return GIT_ENOTFOUND;
-
- /* A ref can not be a directory */
- if (!gitfo_isdir(ref_path))
- return GIT_ENOTFOUND;
-
- if (file_content != NULL)
- error = gitfo_read_file(file_content, ref_path);
-
- return error;
-}
static git_rtype loose_guess_rtype(const char *full_path)
{
@@ -262,10 +303,11 @@ static int loose_lookup(
int error = GIT_SUCCESS;
gitfo_buf ref_file = GITFO_BUF_INIT;
git_reference *ref = NULL;
+ time_t ref_time;
*ref_out = NULL;
- error = loose_read(&ref_file, name, repo->path_repository);
+ error = reference_read(&ref_file, &ref_time, repo->path_repository, name);
if (error < GIT_SUCCESS)
goto cleanup;
@@ -289,6 +331,7 @@ static int loose_lookup(
if (error < GIT_SUCCESS)
goto cleanup;
+ ref->mtime = ref_time;
*ref_out = ref;
return GIT_SUCCESS;
@@ -304,6 +347,7 @@ static int loose_write(git_reference *ref)
char ref_path[GIT_PATH_MAX];
int error, contents_size;
char *ref_contents = NULL;
+ struct stat st;
assert((ref->type & GIT_REF_PACKED) == 0);
@@ -349,6 +393,9 @@ static int loose_write(git_reference *ref)
error = git_filebuf_commit(&file);
+ if (gitfo_stat(ref_path, &st) == GIT_SUCCESS)
+ ref->mtime = st.st_mtime;
+
free(ref_contents);
return error;
@@ -366,19 +413,6 @@ unlock:
/*****************************************
* Internal methods - Packed references
*****************************************/
-static int packed_readpack(gitfo_buf *packfile, const char *repo_path)
-{
- char ref_path[GIT_PATH_MAX];
-
- /* Determine the full path of the file */
- git__joinpath(ref_path, repo_path, GIT_PACKEDREFS_FILE);
-
- /* Does it even exist ? */
- if (gitfo_exists(ref_path) < GIT_SUCCESS)
- return GIT_ENOTFOUND;
-
- return gitfo_read_file(packfile, ref_path);
-}
static int packed_parse_peel(
reference_oid *tag_ref,
@@ -483,19 +517,40 @@ static int packed_load(git_repository *repo)
git_refcache *ref_cache = &repo->references;
/* already loaded */
- if (repo->references.packfile != NULL)
- return GIT_SUCCESS;
+ if (repo->references.packfile != NULL) {
+ time_t packed_time;
- repo->references.packfile = git_hashtable_alloc(
- default_table_size,
- reftable_hash,
- (git_hash_keyeq_ptr)strcmp);
+ /* check if we can read the time of the index;
+ * if we can read it and it matches the time of the
+ * index we had previously loaded, we don't need to do
+ * anything else.
+ *
+ * if we cannot load the time (e.g. the packfile
+ * has disappeared) or the time is different, we
+ * have to reload the packfile */
- if (repo->references.packfile == NULL)
- return GIT_ENOMEM;
+ if (!reference_read(NULL, &packed_time, repo->path_repository, GIT_PACKEDREFS_FILE) &&
+ packed_time == ref_cache->packfile_time)
+ return GIT_SUCCESS;
- /* read the packfile from disk */
- error = packed_readpack(&packfile, repo->path_repository);
+ git_hashtable_clear(repo->references.packfile);
+ } else {
+ ref_cache->packfile = git_hashtable_alloc(
+ default_table_size,
+ reftable_hash,
+ (git_hash_keyeq_ptr)strcmp);
+
+ if (ref_cache->packfile == NULL)
+ return GIT_ENOMEM;
+ }
+
+ /* read the packfile from disk;
+ * store its modification time to check for future reloads */
+ error = reference_read(
+ &packfile,
+ &ref_cache->packfile_time,
+ repo->path_repository,
+ GIT_PACKEDREFS_FILE);
/* there is no packfile on disk; that's ok */
if (error == GIT_ENOTFOUND)
@@ -536,7 +591,12 @@ static int packed_load(git_repository *repo)
}
}
+ gitfo_free_buf(&packfile);
+ return GIT_SUCCESS;
+
cleanup:
+ git_hashtable_free(ref_cache->packfile);
+ ref_cache->packfile = NULL;
gitfo_free_buf(&packfile);
return error;
}
@@ -834,8 +894,14 @@ cleanup:
/* when and only when the packfile has been properly written,
* we can go ahead and remove the loose refs */
- if (error == GIT_SUCCESS)
+ if (error == GIT_SUCCESS) {
+ struct stat st;
+
error = packed_remove_loose(repo, &packing_list);
+
+ if (gitfo_stat(pack_file_path, &st) == GIT_SUCCESS)
+ repo->references.packfile_time = st.st_mtime;
+ }
}
else git_filebuf_cleanup(&pack_file);
@@ -870,7 +936,7 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch
/* First, check has been previously loaded and cached */
*ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name);
if (*ref_out != NULL)
- return GIT_SUCCESS;
+ return loose_update(*ref_out);
/* Then check if there is a loose file for that reference.*/
error = loose_lookup(ref_out, repo, normalized_name, 0);
@@ -888,12 +954,10 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch
* If we cannot find a loose reference, we look into the packfile
* Load the packfile first if it hasn't been loaded
*/
- if (!repo->references.packfile) {
- /* load all the packed references */
- error = packed_load(repo);
- if (error < GIT_SUCCESS)
- return error;
- }
+ /* load all the packed references */
+ error = packed_load(repo);
+ if (error < GIT_SUCCESS)
+ return error;
/* Look up on the packfile */
*ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name);
@@ -1000,6 +1064,9 @@ const git_oid *git_reference_oid(git_reference *ref)
if ((ref->type & GIT_REF_OID) == 0)
return NULL;
+ if (loose_update(ref) < GIT_SUCCESS)
+ return NULL;
+
return &((reference_oid *)ref)->oid;
}
@@ -1010,6 +1077,9 @@ const char *git_reference_target(git_reference *ref)
if ((ref->type & GIT_REF_SYMBOLIC) == 0)
return NULL;
+ if (loose_update(ref) < GIT_SUCCESS)
+ return NULL;
+
return ((reference_symbolic *)ref)->target;
}
@@ -1293,6 +1363,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
assert(resolved_ref && ref);
*resolved_ref = NULL;
+
+ if ((error = loose_update(ref)) < GIT_SUCCESS)
+ return error;
repo = ref->owner;
diff --git a/src/refs.h b/src/refs.h
index a542ac0f2..bebb1b97d 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -23,11 +23,13 @@ struct git_reference {
git_repository *owner;
char *name;
unsigned int type;
+ time_t mtime;
};
typedef struct {
git_hashtable *packfile;
git_hashtable *loose_cache;
+ time_t packfile_time;
} git_refcache;