Re: [PATCH on sb/more-repo-in-api] revision: use commit graph in get_reference()
- From
Derrick Stolee <stolee@gmail.com>
- Date
- Dec 7, 2018, 13:49 UTC
- Message-ID
- <aa0cd481-c135-47aa-2a69-e3dc71661caa@gmail.com>
- In-Reply-To
- <20181206233626.144072-1-jonathantanmy@google.com>
On 12/6/2018 6:36 PM, Jonathan Tan wrote:
Show 17 quoted lines
>> AFAICT oid_object_info doesn't take advantage of the commit graph, >> but just looks up the object header, which is still less than completely >> parsing it. Then lookup_commit is overly strict, as it may return >> NULL as when there still is a type mismatch (I don't think a mismatch >> could happen here, as both rely on just the object store, and not the >> commit graph.), so this would be just defensive programming for >> the sake of it. I dunno. >> >> struct commit *c; >> >> if (oid_object_info(revs->repo, oid, NULL) == OBJ_COMMIT && >> (c = lookup_commit(revs->repo, oid)) && >> !repo_parse_commit(revs->repo, c)) >> object = (struct object *) c; >> else >> object = parse_object(revs->repo, oid); > I like this way better - I'll do it in the next version.
If we do _not_ have a commit-graph or if the commit-graph does not have that commit, this will have the same performance problem, right?
Should we instead create a direct dependence on the commit-graph, and try to parse the oid from the graph directly? If it succeeds, then we learn that the object is a commit, in addition to all of the parsing work. This means we could avoid oid_object_info() loading data if we succeed. We would fall back to parse_object() if it fails.
I was thinking this should be a simple API call to parse_commit_in_graph(), but that requires a struct commit filled with an oid, which is not the best idea if we don't actually know it is a commit yet.
The approach I recommend would then be more detailed:
1. Modify find_commit_in_graph() to take a struct object_id instead of a struct commit. This helps find the integer position in the graph. That position can be used in fill_commit_in_graph() to load the commit contents. Keep find_commit_in_graph() static as it should not be a public function.
2. Create a public function with prototype
struct commit *try_parse_commit_from_graph(struct repository *r, struct object_id *oid)
that returns a commit struct fully parsed if and only if the repository has that oid. It can call find_commit_in_graph(), then lookup_commit() and fill_commit_in_graph() to create the commit and parse the data.
3. In replace of the snippet above, do:
struct commit *c;
if ((c = try_parse_commit_from_graph(revs->repo, oid)) object = (struct object *)c; else object = parse_object(revs->repo, oid);
A similar pattern _could_ be used in parse_object(), but I don't recommend doing this pattern unless we have a reasonable suspicion that we are going to parse commits more often than other objects. (It adds an O(log(# commits)) binary search to each object.)
A final thought: consider making this "try the commit graph first, but fall back to parse_object()" a library function with a name like
struct object *parse_probably_commit(struct repository *r, struct object_id *oid)
so other paths that are parsing a lot of commits (but also maybe tags) could use the logic.
Thanks! -Stolee