-
-
Notifications
You must be signed in to change notification settings - Fork 414
feat: filters and partial cloning: initial support #2375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: filters and partial cloning: initial support #2375
Conversation
Creates a blobless bare clone using `gix`, creates (using real `git`) a worktree from that, and then using `gix` verify that `--filter=blob:none` is working! [Upstream-Status: Appropriate for OSS Release]
64d4362 to
3f30421
Compare
Introduce `gix::remote::fetch::ObjectFilter` (currently blob filters only) and plumb it through clone/fetch all the way into the fetch protocol, returning a clear error if the remote doesn’t advertise `filter` capability. Also persist the partial-clone configuration on clone (`remote.<name>.partialclonefilter`, `remote.<name>.promisor`, `extensions.partialclone`) so the repository is marked as a promisor/partial clone in the same way as Git. On the CLI/plumbing side, add `--filter <spec>` to `gix clone`, and allow fetch arguments to be either refspecs or raw object IDs (treated as additional wants). Includes tests for filter parsing and for persisting partial-clone settings during clone. Credit: - Original work by Cameron Esfahani <cesfahani@roku.com> - Rewritten for modern Gitoxide by Jake Staehle <jstaehle@roku.com> [Upstream-Status: Appropriate for OSS Release]
3f30421 to
ddd9fd1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
| self.shallow, | ||
| negotiate::make_refmapping_ignore_predicate(self.tags, self.ref_map), | ||
| ) | ||
| if self.additional_wants.is_empty() { |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition is inverted. When additional_wants is empty, the code calls negotiate::add_wants() but then still iterates over additional_wants (lines 315-318), which does nothing. When additional_wants is NOT empty, it skips the normal wants logic and manually implements it (lines 323-350). This should be !self.additional_wants.is_empty() to properly handle the case when additional wants are present.
| if self.additional_wants.is_empty() { | |
| if !self.additional_wants.is_empty() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks confusing so I get why copilot doesn't understand, but the condition is not inverted, and the behavior matches the surrounding design.
When additional_wants is empty, add_wants() delegates to negotiate::add_wants and then iterates self.additional_wants (which is empty), so that loop is a harmless no-op. This is the "normal fetch" path.
When additional_wants is non-empty, the code intentionally avoids negotiate::add_wants and instead builds the wants list itself (including want_ref() when supported) and then appends the additional object-id wants. This aligns with the other deliberate behavior you already saw:
mark_complete_and_common_ref()forcesMustNegotiatewhen additional wants exist.one_round()returns an "empty round" and intentionally skips have exchange/negotiation rounds since we want to ask for these objects directly.
So, flipping the condition to !self.additional_wants.is_empty() would make the non-empty case call negotiate::add_wants and then fall through into the manual wants logic, which would duplicate/alter behavior and be wrong.
| .map(crate::remote::fetch::ObjectFilter::to_argument_string) | ||
| .filter(|_| self.additional_wants.is_empty()); |
Copilot
AI
Jan 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filter is being suppressed when additional_wants is present (line 105). This means users cannot use object filters when fetching specific objects by ID. Unless there's a technical limitation preventing filters with additional wants, this restriction should be removed or clearly documented why filters and additional wants are mutually exclusive.
| .map(crate::remote::fetch::ObjectFilter::to_argument_string) | |
| .filter(|_| self.additional_wants.is_empty()); | |
| .map(crate::remote::fetch::ObjectFilter::to_argument_string); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hard disagree. This is intentional -- additional_wants SHOULD ignore blob filters, since if you specifically want to checkout a commit, you NEED those blobs.
Resolves #1046
Hi @Byron! I'm Jake Staehle and I work at Roku here with @cesfahani. We've had an internal version of this for our internal tools for quite some time, but I recently updated our
gitoxideversion to0.49and had to refactor this code myself for all the changes since0.40-- I would very much rather not do that again, so it's upstreaming time :)Introduce
gix::remote::fetch::ObjectFilter(currently blob filters only) and plumb it through clone/fetch all the way into the fetch protocol, returning a clear error if the remote doesn’t advertisefiltercapability.Also persist the partial-clone configuration on clone (
remote.<name>.partialclonefilter,remote.<name>.promisor,extensions.partialclone) so the repository is marked as a promisor/partial clone in the same way as Git.On the CLI/plumbing side, add
--filter <spec>togix clone, and allow fetch arguments to be either refspecs or raw object IDs (treated as additional wants).Includes tests for filter parsing and for persisting partial-clone settings during clone, as well as a full round-trip test creating a blobless bare clone using
gix, creating (using realgit) a worktree from that, and then usinggixverify that--filter=blob:noneis working!I did create a new repo https://github.com/staehle/gitoxide-testing for that last test to work properly, with a small set of known commits, tags, and blob objects. If you think this is useful, I'd be glad to transfer the repo to you / the
GitoxideLabsgroup!Let me know what you think! Thanks!
Credit: