diff options
author | lhchavez <lhchavez@lhchavez.com> | 2021-01-05 17:20:27 -0800 |
---|---|---|
committer | lhchavez <lhchavez@lhchavez.com> | 2021-03-10 07:09:47 -0800 |
commit | 248606ebb0906076367fcfce9574f522f818c26f (patch) | |
tree | a3d349ff8cc13ca5fc3a432c9967703f26a63328 | |
parent | 4f4b1139d23a7b38cceb9d83acbfaf73151f522f (diff) | |
download | libgit2-248606ebb0906076367fcfce9574f522f818c26f.tar.gz |
commit-graph: Use the commit-graph in revwalks
This change makes revwalks a bit faster by using the `commit-graph` file
(if present). This is thanks to the `commit-graph` allow much faster
parsing of the commit information by requiring near-zero I/O (aside from
reading a few dozen bytes off of a `mmap(2)`-ed file) for each commit,
instead of having to read the ODB, inflate the commit, and parse it.
This is done by modifying `git_commit_list_parse()` and letting it use
the ODB-owned commit-graph file.
Part of: #5757
-rw-r--r-- | src/commit_list.c | 28 | ||||
-rw-r--r-- | src/commit_list.h | 1 | ||||
-rw-r--r-- | src/odb.c | 74 | ||||
-rw-r--r-- | src/odb.h | 15 |
4 files changed, 116 insertions, 2 deletions
diff --git a/src/commit_list.c b/src/commit_list.c index 2e103ec22..dfdd5daab 100644 --- a/src/commit_list.c +++ b/src/commit_list.c @@ -124,6 +124,7 @@ static int commit_quick_parse( return -1; } + node->generation = 0; node->time = commit->committer->when.time; node->out_degree = (uint16_t) git_array_size(commit->parent_ids); node->parents = alloc_parents(walk, node, node->out_degree); @@ -143,11 +144,38 @@ static int commit_quick_parse( int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit) { git_odb_object *obj; + git_commit_graph_file *cgraph = NULL; int error; if (commit->parsed) return 0; + /* Let's try to use the commit graph first. */ + git_odb__get_commit_graph(&cgraph, walk->odb); + if (cgraph) { + git_commit_graph_entry e; + + error = git_commit_graph_entry_find(&e, cgraph, &commit->oid, GIT_OID_RAWSZ); + if (error == 0 && git__is_uint16(e.parent_count)) { + size_t i; + commit->generation = (uint32_t)e.generation; + commit->time = e.commit_time; + commit->out_degree = (uint16_t)e.parent_count; + commit->parents = alloc_parents(walk, commit, commit->out_degree); + GIT_ERROR_CHECK_ALLOC(commit->parents); + + for (i = 0; i < commit->out_degree; ++i) { + git_commit_graph_entry parent; + error = git_commit_graph_entry_parent(&parent, cgraph, &e, i); + if (error < 0) + return error; + commit->parents[i] = git_revwalk__commit_lookup(walk, &parent.sha1); + } + commit->parsed = 1; + return 0; + } + } + if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0) return error; diff --git a/src/commit_list.h b/src/commit_list.h index 6a65f8a76..a32377030 100644 --- a/src/commit_list.h +++ b/src/commit_list.h @@ -26,6 +26,7 @@ typedef struct git_commit_list_node { git_oid oid; int64_t time; + uint32_t generation; unsigned int seen:1, uninteresting:1, topo_delay:1, @@ -465,6 +465,13 @@ int git_odb_new(git_odb **out) git__free(db); return -1; } + if (git_buf_init(&db->objects_dir, 0) < 0) { + git_vector_free(&db->backends); + git_cache_dispose(&db->own_cache); + git_mutex_free(&db->lock); + git__free(db); + return -1; + } *out = db; GIT_REFCOUNT_INC(db); @@ -612,6 +619,17 @@ int git_odb__add_default_backends( git_mutex_unlock(&db->lock); #endif + if (git_mutex_lock(&db->lock) < 0) { + git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); + return -1; + } + if (git_buf_len(&db->objects_dir) == 0 && git_buf_sets(&db->objects_dir, objects_dir) < 0) { + git_mutex_unlock(&db->lock); + git_odb_free(db); + return -1; + } + git_mutex_unlock(&db->lock); + /* add the loose object backend */ if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 || add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0) @@ -742,6 +760,8 @@ static void odb_free(git_odb *db) if (locked) git_mutex_unlock(&db->lock); + git_buf_dispose(&db->objects_dir); + git_commit_graph_free(db->cgraph); git_vector_free(&db->backends); git_cache_dispose(&db->own_cache); git_mutex_free(&db->lock); @@ -786,6 +806,53 @@ static int odb_exists_1( return (int)found; } +int git_odb__get_commit_graph(git_commit_graph_file **out, git_odb *db) +{ + int error = 0; + + if ((error = git_mutex_lock(&db->lock)) < 0) { + git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock"); + return error; + } + if (!db->cgraph_checked) { + git_buf commit_graph_path = GIT_BUF_INIT; + git_commit_graph_file *cgraph = NULL; + + /* We only check once, no matter the result. */ + db->cgraph_checked = 1; + + if (git_buf_len(&db->objects_dir) == 0) { + /* + * This odb was not opened with an objects directory + * associated. Skip opening the commit graph. + */ + goto done; + } + + if ((error = git_buf_joinpath( + &commit_graph_path, + git_buf_cstr(&db->objects_dir), + "info/commit-graph")) + < 0) { + git_buf_dispose(&commit_graph_path); + goto done; + } + /* Best effort */ + error = git_commit_graph_open(&cgraph, git_buf_cstr(&commit_graph_path)); + git_buf_dispose(&commit_graph_path); + + if (error < 0) + goto done; + + db->cgraph = cgraph; + } + +done: + *out = db->cgraph; + git_mutex_unlock(&db->lock); + return 0; +} + static int odb_freshen_1( git_odb *db, const git_oid *id, @@ -1695,6 +1762,13 @@ int git_odb_refresh(struct git_odb *db) } } } + if (db->cgraph && git_commit_graph_needs_refresh(db->cgraph, NULL)) { + /* We just free the commit graph. The next time it is requested, it will be re-loaded. */ + git_commit_graph_free(db->cgraph); + db->cgraph = NULL; + } + /* Force a lazy re-check next time it is needed. */ + db->cgraph_checked = 0; git_mutex_unlock(&db->lock); return 0; @@ -13,10 +13,11 @@ #include "git2/oid.h" #include "git2/types.h" -#include "vector.h" #include "cache.h" -#include "posix.h" +#include "commit_graph.h" #include "filter.h" +#include "posix.h" +#include "vector.h" #define GIT_OBJECTS_DIR "objects/" #define GIT_OBJECT_DIR_MODE 0777 @@ -43,7 +44,10 @@ struct git_odb { git_mutex lock; /* protects backends */ git_vector backends; git_cache own_cache; + git_buf objects_dir; + git_commit_graph_file *cgraph; unsigned int do_fsync :1; + unsigned int cgraph_checked :1; }; typedef enum { @@ -127,6 +131,13 @@ int git_odb__read_header_or_object( git_odb_object **out, size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id); +/* + * Attempt to get the ODB's commit graph. This object is still owned by the + * ODB. If the repository does not contain a commit graph, it will return zero + * and `*out` will be set to NULL. + */ +int git_odb__get_commit_graph(git_commit_graph_file **out, git_odb *odb); + /* freshen an entry in the object database */ int git_odb__freshen(git_odb *db, const git_oid *id); |