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)
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;
......
......@@ -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;
......
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