transports: Avoid walking over non-commit objects - #4383
Conversation
References may point to objects that are not commits, such as tags to blobs. In that case the reference must not be walked over further. Fixes libgit2#3595.
| if ((error = git_object_lookup(&oobj, repo, oid, GIT_OBJ_ANY)) < 0) | ||
| goto on_error; | ||
|
|
||
| error = git_object_peel(&obj, oobj, GIT_OBJ_COMMIT); |
There was a problem hiding this comment.
Can you explain why are you peeling here? What type of objects could you find above (in the lookup) that would need to be peeled? It seems like this could be simplified as:
oid = git_reference_target(ref);
if ((error = git_object_lookup(&obj, repo, oid, GIT_OBJ_ANY)) < 0)
goto on_error;
if (git_object_type(obj) != GIT_OBJ_COMMIT) {
git_object_free(obj);
continue;
}
It might be nicer to specify the type to git_object_lookup and modify git_object_lookup to return a unique error code when the type of the object is not the type requested.
There was a problem hiding this comment.
In my use-case the first object is actually an annotated tag, which when peeled points to a blob. But in other cases it could point to a commit, and in this case the commit can be added to the revwalk.
There was a problem hiding this comment.
Got it, thanks for the explanation.
|
Nice - thanks for the pull request and for adding tests! ✨ My assumption here is that there's not a significant perf impact to looking up these objects ahead of time just to validate their type... presumably they'll get cached and then the revwalk will pull them from the cache. Another option is to move this logic into the revwalk enqueue functionality, which could prevent a second odb lookup. |
|
Right - looking at Couple of options:
@carlosmn do you have an opinion here? |
pks-t
left a comment
There was a problem hiding this comment.
This review is regarding your changes only, without judgement whether it'd be better to push this into git_revwalk_push.
Re @ethomson. I'm not a fan of the first option. It seems a bit fragile to simply pretend everything is alright when we in fact just ignore what we were told to do. The second possibility looks much saner to me. I think we should aim for that solution -- I'm not a fan of premature optimization, but if we can easily avoid a second lookup/peel while making the API more useful for callers it sounds like a win-win to me.
| continue; | ||
|
|
||
| if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0) | ||
| /* only walk over commits */ |
There was a problem hiding this comment.
The comment here is a bit terse, in my opinion. I'd love to have it mention why we need the separate lookup/peel steps and why we need to filter out tags-to-blobs here if there's a comment 10 lines before which pretends to filter out tags already. I've been confused by that, as well, as I didn't expect you to have tags outside of the "refs/tags" hierarchy.
| git_object_free(oobj); | ||
| if (error == GIT_ENOTFOUND || error == GIT_EPEEL) { | ||
| continue; | ||
| } else if (error != 0) |
There was a problem hiding this comment.
We usually avoid braces around single-line statements
|
|
||
| for (i = 0; i < refs.count; ++i) { | ||
| const git_oid *oid; | ||
| git_object *obj, *oobj; |
There was a problem hiding this comment.
These object are leaking as soon as we encounter an error. You could instead just initialize them to NULL, git_object_free at the beginning of each iteration as well as freeing in the on_error section. This requires us to define these variables at the beginning of the function.
| } | ||
|
|
||
| static const char *refnames[] = { | ||
| "refs/blobs/annotated_tag_to_blob", |
There was a problem hiding this comment.
This thing here was missing to complete my picture of your issue. I didn't realize you had tags in non-standard locations. Anyway, we should definitly be able to cope with that as users do strange things.
@ethomson, I've had this patch for a couple of years now so I forgot how I wrote it, but probably I got the code from there. :) |
This situation was caught previously but returned a generic error code.
Instead, use the return code to check if reference does not point to a committish.
|
I'm not fond of adding a new error case for this. We already have error codes for peeling (though they are admittedly more complex than I'd like) which is what we're doing. The point of the the revwalk during fetch is to convert as many reference tips into commits as we can, so we can reduce the amount of data that's transmitted, so we should go with that idea into the code. The loop under This code predates We should actually be able to get rid of the whole of (plus error handling) with the extra advantage that we're using an iterator instead of having to allocate an array. |
|
Closed for #5195 |
References may point to objects that are not commits, such as tags to
blobs. In that case the reference must not be walked over further.
Fixes #3595.