Commit 7341bf87 by Vicent Marti

Refs are now always in-sync on disk

parent 9f81a37a
...@@ -59,16 +59,16 @@ static uint32_t reftable_hash(const void *key, int hash_id) ...@@ -59,16 +59,16 @@ static uint32_t reftable_hash(const void *key, int hash_id)
static void reference_free(git_reference *reference); 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_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 */ /* loose refs */
static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content); 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_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_write(git_reference *ref);
static int loose_update(git_reference *ref);
/* packed refs */ /* 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_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_parse_oid(reference_oid **ref_out, git_repository *repo, const char **buffer_out, const char *buffer_end);
static int packed_load(git_repository *repo); static int packed_load(git_repository *repo);
...@@ -146,12 +146,73 @@ cleanup: ...@@ -146,12 +146,73 @@ cleanup:
return error; 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 * 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) static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
{ {
const unsigned int header_len = strlen(GIT_SYMREF); const unsigned int header_len = strlen(GIT_SYMREF);
...@@ -172,6 +233,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content) ...@@ -172,6 +233,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
refname_start += header_len; refname_start += header_len;
free(ref_sym->target);
ref_sym->target = git__strdup(refname_start); ref_sym->target = git__strdup(refname_start);
if (ref_sym->target == NULL) if (ref_sym->target == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
...@@ -213,27 +275,6 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content) ...@@ -213,27 +275,6 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
return GIT_SUCCESS; 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) static git_rtype loose_guess_rtype(const char *full_path)
{ {
...@@ -262,10 +303,11 @@ static int loose_lookup( ...@@ -262,10 +303,11 @@ static int loose_lookup(
int error = GIT_SUCCESS; int error = GIT_SUCCESS;
gitfo_buf ref_file = GITFO_BUF_INIT; gitfo_buf ref_file = GITFO_BUF_INIT;
git_reference *ref = NULL; git_reference *ref = NULL;
time_t ref_time;
*ref_out = NULL; *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) if (error < GIT_SUCCESS)
goto cleanup; goto cleanup;
...@@ -289,6 +331,7 @@ static int loose_lookup( ...@@ -289,6 +331,7 @@ static int loose_lookup(
if (error < GIT_SUCCESS) if (error < GIT_SUCCESS)
goto cleanup; goto cleanup;
ref->mtime = ref_time;
*ref_out = ref; *ref_out = ref;
return GIT_SUCCESS; return GIT_SUCCESS;
...@@ -304,6 +347,7 @@ static int loose_write(git_reference *ref) ...@@ -304,6 +347,7 @@ static int loose_write(git_reference *ref)
char ref_path[GIT_PATH_MAX]; char ref_path[GIT_PATH_MAX];
int error, contents_size; int error, contents_size;
char *ref_contents = NULL; char *ref_contents = NULL;
struct stat st;
assert((ref->type & GIT_REF_PACKED) == 0); assert((ref->type & GIT_REF_PACKED) == 0);
...@@ -349,6 +393,9 @@ static int loose_write(git_reference *ref) ...@@ -349,6 +393,9 @@ static int loose_write(git_reference *ref)
error = git_filebuf_commit(&file); error = git_filebuf_commit(&file);
if (gitfo_stat(ref_path, &st) == GIT_SUCCESS)
ref->mtime = st.st_mtime;
free(ref_contents); free(ref_contents);
return error; return error;
...@@ -366,19 +413,6 @@ unlock: ...@@ -366,19 +413,6 @@ unlock:
/***************************************** /*****************************************
* Internal methods - Packed references * 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( static int packed_parse_peel(
reference_oid *tag_ref, reference_oid *tag_ref,
...@@ -483,19 +517,40 @@ static int packed_load(git_repository *repo) ...@@ -483,19 +517,40 @@ static int packed_load(git_repository *repo)
git_refcache *ref_cache = &repo->references; git_refcache *ref_cache = &repo->references;
/* already loaded */ /* already loaded */
if (repo->references.packfile != NULL) if (repo->references.packfile != NULL) {
return GIT_SUCCESS; time_t packed_time;
repo->references.packfile = git_hashtable_alloc( /* check if we can read the time of the index;
default_table_size, * if we can read it and it matches the time of the
reftable_hash, * index we had previously loaded, we don't need to do
(git_hash_keyeq_ptr)strcmp); * 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) if (!reference_read(NULL, &packed_time, repo->path_repository, GIT_PACKEDREFS_FILE) &&
return GIT_ENOMEM; packed_time == ref_cache->packfile_time)
return GIT_SUCCESS;
/* read the packfile from disk */ git_hashtable_clear(repo->references.packfile);
error = packed_readpack(&packfile, repo->path_repository); } 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 */ /* there is no packfile on disk; that's ok */
if (error == GIT_ENOTFOUND) if (error == GIT_ENOTFOUND)
...@@ -536,7 +591,12 @@ static int packed_load(git_repository *repo) ...@@ -536,7 +591,12 @@ static int packed_load(git_repository *repo)
} }
} }
gitfo_free_buf(&packfile);
return GIT_SUCCESS;
cleanup: cleanup:
git_hashtable_free(ref_cache->packfile);
ref_cache->packfile = NULL;
gitfo_free_buf(&packfile); gitfo_free_buf(&packfile);
return error; return error;
} }
...@@ -834,8 +894,14 @@ cleanup: ...@@ -834,8 +894,14 @@ cleanup:
/* when and only when the packfile has been properly written, /* when and only when the packfile has been properly written,
* we can go ahead and remove the loose refs */ * 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); 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); else git_filebuf_cleanup(&pack_file);
...@@ -870,7 +936,7 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch ...@@ -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 */ /* First, check has been previously loaded and cached */
*ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name); *ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name);
if (*ref_out != NULL) if (*ref_out != NULL)
return GIT_SUCCESS; return loose_update(*ref_out);
/* Then check if there is a loose file for that reference.*/ /* Then check if there is a loose file for that reference.*/
error = loose_lookup(ref_out, repo, normalized_name, 0); 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 ...@@ -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 * If we cannot find a loose reference, we look into the packfile
* Load the packfile first if it hasn't been loaded * Load the packfile first if it hasn't been loaded
*/ */
if (!repo->references.packfile) { /* load all the packed references */
/* load all the packed references */ error = packed_load(repo);
error = packed_load(repo); if (error < GIT_SUCCESS)
if (error < GIT_SUCCESS) return error;
return error;
}
/* Look up on the packfile */ /* Look up on the packfile */
*ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name); *ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name);
...@@ -1000,6 +1064,9 @@ const git_oid *git_reference_oid(git_reference *ref) ...@@ -1000,6 +1064,9 @@ const git_oid *git_reference_oid(git_reference *ref)
if ((ref->type & GIT_REF_OID) == 0) if ((ref->type & GIT_REF_OID) == 0)
return NULL; return NULL;
if (loose_update(ref) < GIT_SUCCESS)
return NULL;
return &((reference_oid *)ref)->oid; return &((reference_oid *)ref)->oid;
} }
...@@ -1010,6 +1077,9 @@ const char *git_reference_target(git_reference *ref) ...@@ -1010,6 +1077,9 @@ const char *git_reference_target(git_reference *ref)
if ((ref->type & GIT_REF_SYMBOLIC) == 0) if ((ref->type & GIT_REF_SYMBOLIC) == 0)
return NULL; return NULL;
if (loose_update(ref) < GIT_SUCCESS)
return NULL;
return ((reference_symbolic *)ref)->target; return ((reference_symbolic *)ref)->target;
} }
...@@ -1293,6 +1363,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) ...@@ -1293,6 +1363,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
assert(resolved_ref && ref); assert(resolved_ref && ref);
*resolved_ref = NULL; *resolved_ref = NULL;
if ((error = loose_update(ref)) < GIT_SUCCESS)
return error;
repo = ref->owner; repo = ref->owner;
......
...@@ -23,11 +23,13 @@ struct git_reference { ...@@ -23,11 +23,13 @@ struct git_reference {
git_repository *owner; git_repository *owner;
char *name; char *name;
unsigned int type; unsigned int type;
time_t mtime;
}; };
typedef struct { typedef struct {
git_hashtable *packfile; git_hashtable *packfile;
git_hashtable *loose_cache; git_hashtable *loose_cache;
time_t packfile_time;
} git_refcache; } git_refcache;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment