mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-19 18:39:26 +00:00
refs: Partial rewrite for read-only refs
This new version of the references code is significantly faster and hopefully easier to read. External API stays the same. A new method `git_reference_reload()` has been added to force updating a memory reference from disk. In-memory references are no longer updated automagically -- this was killing us. If a reference is deleted externally and the user doesn't reload the memory object, nothing critical happens: any functions using that reference should fail gracefully (e.g. deletion, renaming, and so on). All generated references from the API are read only and must be free'd by the user. There is no reference counting and no traces of generated references are kept in the library. There is no longer an internal representation for references. There is only one reference struct `git_reference`, and symbolic/oid targets are stored inside an union. Packfile references are stored using an optimized struct with flex array for reference names. This should significantly reduce the memory cost of loading the packfile from disk.
This commit is contained in:
parent
549bbd1342
commit
d4a0b124d0
@ -116,8 +116,13 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
|
|||||||
* Thie method iteratively peels a symbolic reference
|
* Thie method iteratively peels a symbolic reference
|
||||||
* until it resolves to a direct reference to an OID.
|
* until it resolves to a direct reference to an OID.
|
||||||
*
|
*
|
||||||
|
* The peeled reference is returned in the `resolved_ref`
|
||||||
|
* argument, and must be freed manually once it's no longer
|
||||||
|
* needed.
|
||||||
|
*
|
||||||
* If a direct reference is passed as an argument,
|
* If a direct reference is passed as an argument,
|
||||||
* that reference is returned immediately
|
* a copy of that reference is returned. This copy must
|
||||||
|
* be manually freed too.
|
||||||
*
|
*
|
||||||
* @param resolved_ref Pointer to the peeled reference
|
* @param resolved_ref Pointer to the peeled reference
|
||||||
* @param ref The reference
|
* @param ref The reference
|
||||||
@ -170,11 +175,19 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
|
|||||||
* The new name will be checked for validity and may be
|
* The new name will be checked for validity and may be
|
||||||
* modified into a normalized form.
|
* modified into a normalized form.
|
||||||
*
|
*
|
||||||
* The given git_reference will be updated.
|
* The given git_reference will be updated in place.
|
||||||
*
|
*
|
||||||
* The reference will be immediately renamed in-memory
|
* The reference will be immediately renamed in-memory
|
||||||
* and on disk.
|
* and on disk.
|
||||||
*
|
*
|
||||||
|
* If the `force` flag is not enabled, and there's already
|
||||||
|
* a reference with the given name, the renaming will fail.
|
||||||
|
*
|
||||||
|
* @param ref The reference to rename
|
||||||
|
* @param new_name The new name for the reference
|
||||||
|
* @param force Overwrite an existing reference
|
||||||
|
* @return GIT_SUCCESS or an error code
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
|
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
|
||||||
|
|
||||||
@ -186,6 +199,8 @@ GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, i
|
|||||||
* The reference will be immediately removed on disk and from
|
* The reference will be immediately removed on disk and from
|
||||||
* memory. The given reference pointer will no longer be valid.
|
* memory. The given reference pointer will no longer be valid.
|
||||||
*
|
*
|
||||||
|
* @param ref The reference to remove
|
||||||
|
* @return GIT_SUCCESS or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
||||||
|
|
||||||
@ -250,13 +265,33 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo,
|
|||||||
GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
|
GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a reference is packed
|
* Check if a reference has been loaded from a packfile
|
||||||
*
|
*
|
||||||
* @param ref git_reference
|
* @param ref A git reference
|
||||||
* @return 0 in case it's not packed; 1 otherwise
|
* @return 0 in case it's not packed; 1 otherwise
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_is_packed(git_reference *ref);
|
GIT_EXTERN(int) git_reference_is_packed(git_reference *ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload a reference from disk
|
||||||
|
*
|
||||||
|
* Reference pointers may become outdated if the Git
|
||||||
|
* repository is accessed simultaneously by other clients
|
||||||
|
* whilt the library is open.
|
||||||
|
*
|
||||||
|
* This method forces a reload of the reference from disk,
|
||||||
|
* to ensure that the provided information is still
|
||||||
|
* reliable.
|
||||||
|
*
|
||||||
|
* If the reload fails (e.g. the reference no longer exists
|
||||||
|
* on disk, or has become corrupted), an error code will be
|
||||||
|
* returned and the reference pointer will be invalidated.
|
||||||
|
*
|
||||||
|
* @param ref The reference to reload
|
||||||
|
* @return GIT_SUCCESS on success, or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_reference_reload(git_reference *ref);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free the given reference
|
* Free the given reference
|
||||||
*
|
*
|
||||||
|
1496
src/refs.c
1496
src/refs.c
File diff suppressed because it is too large
Load Diff
10
src/refs.h
10
src/refs.h
@ -33,19 +33,23 @@
|
|||||||
#define GIT_REFNAME_MAX 1024
|
#define GIT_REFNAME_MAX 1024
|
||||||
|
|
||||||
struct git_reference {
|
struct git_reference {
|
||||||
|
unsigned int flags;
|
||||||
git_repository *owner;
|
git_repository *owner;
|
||||||
char *name;
|
char *name;
|
||||||
|
time_t mtime;
|
||||||
|
|
||||||
|
union {
|
||||||
|
git_oid oid;
|
||||||
|
char *symbolic;
|
||||||
|
} target;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_hashtable *packfile;
|
git_hashtable *packfile;
|
||||||
git_hashtable *loose_cache;
|
|
||||||
time_t packfile_time;
|
time_t packfile_time;
|
||||||
} git_refcache;
|
} git_refcache;
|
||||||
|
|
||||||
|
|
||||||
void git_repository__refcache_free(git_refcache *refs);
|
void git_repository__refcache_free(git_refcache *refs);
|
||||||
int git_repository__refcache_init(git_refcache *refs);
|
|
||||||
|
|
||||||
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
|
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
|
||||||
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
|
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
|
||||||
|
@ -172,11 +172,6 @@ static git_repository *repository_alloc(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) {
|
|
||||||
git__free(repo);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,7 +772,7 @@ int git_repository_head_orphan(git_repository *repo)
|
|||||||
|
|
||||||
int git_repository_is_empty(git_repository *repo)
|
int git_repository_is_empty(git_repository *repo)
|
||||||
{
|
{
|
||||||
git_reference *head, *branch;
|
git_reference *head = NULL, *branch = NULL;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = git_reference_lookup(&head, repo, "HEAD");
|
error = git_reference_lookup(&head, repo, "HEAD");
|
||||||
|
@ -143,22 +143,23 @@ BEGIN_TEST(readsym2, "lookup the HEAD and resolve the master branch")
|
|||||||
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
|
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
|
||||||
|
|
||||||
must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name));
|
must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name));
|
||||||
must_pass(git_reference_resolve(&resolved_ref, reference));
|
must_pass(git_reference_resolve(&comp_base_ref, reference));
|
||||||
comp_base_ref = resolved_ref;
|
git_reference_free(reference);
|
||||||
|
|
||||||
must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
|
must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
|
||||||
must_pass(git_reference_resolve(&resolved_ref, reference));
|
must_pass(git_reference_resolve(&resolved_ref, reference));
|
||||||
must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
|
must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
|
||||||
|
git_reference_free(reference);
|
||||||
|
git_reference_free(resolved_ref);
|
||||||
|
|
||||||
must_pass(git_reference_lookup(&reference, repo, current_head_target));
|
must_pass(git_reference_lookup(&reference, repo, current_head_target));
|
||||||
must_pass(git_reference_resolve(&resolved_ref, reference));
|
must_pass(git_reference_resolve(&resolved_ref, reference));
|
||||||
must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
|
must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
|
||||||
|
|
||||||
git_repository_free(repo);
|
|
||||||
|
|
||||||
git_reference_free(reference);
|
git_reference_free(reference);
|
||||||
git_reference_free(resolved_ref);
|
git_reference_free(resolved_ref);
|
||||||
|
|
||||||
git_reference_free(comp_base_ref);
|
git_reference_free(comp_base_ref);
|
||||||
|
git_repository_free(repo);
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
BEGIN_TEST(readsym3, "lookup the master branch and then the HEAD")
|
BEGIN_TEST(readsym3, "lookup the master branch and then the HEAD")
|
||||||
@ -902,6 +903,9 @@ BEGIN_TEST(delete1, "can delete a just packed reference")
|
|||||||
/* Pack all existing references */
|
/* Pack all existing references */
|
||||||
must_pass(git_reference_packall(repo));
|
must_pass(git_reference_packall(repo));
|
||||||
|
|
||||||
|
/* Reload the reference from disk */
|
||||||
|
must_pass(git_reference_reload(ref));
|
||||||
|
|
||||||
/* Ensure it's a packed reference */
|
/* Ensure it's a packed reference */
|
||||||
must_be_true(git_reference_is_packed(ref) == 1);
|
must_be_true(git_reference_is_packed(ref) == 1);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user