mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-03 23:16:23 +00:00
Refs are now always in-sync on disk
This commit is contained in:
parent
9f81a37aa4
commit
7341bf87b1
185
src/refs.c
185
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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user