Skip to content

fix v2.56.0.vfs.0.0 "requires the GVFS protocol" regression when ref tip is not local - #1005

Merged
Johannes Schindelin (dscho) merged 11 commits into
microsoft:vfs-2.56.0from
tyrielv:vfs-fix-readobject-hook-skip-fetch
Oct 15, 2026
Merged

Johannes Schindelin (dscho) merged 11 commits into
microsoft:vfs-2.56.0from
tyrielv:vfs-fix-readobject-hook-skip-fetch

Conversation

@tyrielv

@tyrielv Tyrie Vella (tyrielv) commented Oct 12, 2026 •

Copy link
Copy Markdown

In a mounted VFS for Git enlistment, git fetch fails when a fetched ref
tip is not yet in the local object store:

remote: This repository requires the GVFS protocol (use "gvfs clone" or "scalar clone").
fatal: protocol error: bad pack header

The same fetch succeeds with v2.55.0.vfs.0.10. This happens on branches
that move faster than the cache server publishes prefetch packs, so the
new tip is in no prefetch pack yet.

The reason is an overzealous fix for a test failure introduced during the
v2.56.0-rc0 rebase: 37a0468 (fixup! gvfs-helper: create tool to fetch
objects using the GVFS Protocol, 2026-09-22) broadened the scope too
much where the object fetches were skipped.

This fixes #1004

Jeff King (peff) and others added 8 commits September 28, 2026 10:49
When we detect that curl performed a redirect of a URL we requested, we
update our base URL to match the new location and flush the http_auth
credentials. This goes back to c93c92f (http: update base URLs when
we see redirects, 2013-09-28).

We detect the redirect by comparing the requested URL to the response
from CURLINFO_EFFECTIVE_URL, using a simple string comparison. This has
worked fine for years, but a change in the upcoming curl 8.23.0 adds a
complication. If our URL directly contains credentials (like
"https://user:pass@example.com/foo.git"), then as of 7a6bd027d0
(getinfo: make sure CURLINFO_EFFECTIVE_URL does not contain creds,
2026-09-21), curl will strip the credentials from what it returns (so
just "https://example.com/foo.git" in this case).

This breaks our direct string comparison, and we believe that we've been
redirected. We flush our http_auth credentials, and now subsequent
requests will use the reduced URL, causing us to re-request credentials
from the user. Notably this causes t5550.15 (among others) to complain;
it tries a clone with credentials in the URL, and fails if the user is
prompted at all.

We can handle this new behavior by doing a more careful comparison: if
the direct string comparison fails, we'll strip out the credentials
ourselves and compare. This is a little extra work, but in practice it
should only happen once per process.

I've used curl's curl_url() interface to do the stripping here, mostly
because its behavior should match the stripping it does internally. And
also, though we have code to parse a URL, we don't have any to
reconstruct it, making a single string comparison hard.

One alternative would be to parse with url_parse() or similar, and
compare the individual fields (skipping username/password). I think that
would probably also work in practice, but it seemed to me that the
simplest change would be sticking with string comparisons.

The curl_url() interface appeared in 7.62.0. We document that 7.61.0 is
still supported, so I've made it conditional here. Only new versions
strip the result from CURLINFO_EFFECTIVE_URL, so it's OK for very old
versions to skip the extra comparison. Likewise if we encounter any
errors, we just quietly skip the comparison. That's fine if you don't
have creds in your URLs, and if you do, you'll get end up in the
existing error path (a redirect warning, and eventually an auth
failure).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
In a mounted VFS for Git enlistment, `git fetch` fails when a fetched
ref tip is not yet in the local object store:

    remote: This repository requires the GVFS protocol (use "gvfs clone"
    or "scalar clone").
    fatal: protocol error: bad pack header

The same fetch succeeds with v2.55.0.vfs.0.10. This happens on branches
that move faster than the cache server publishes prefetch packs, so the
new tip is in no prefetch pack yet.

`check_exist_and_connected()` in builtin/fetch.c calls
`odb_has_object()` with `ODB_HAS_OBJECT_RECHECK_PACKED` for each ref
tip. That call does not pass `ODB_HAS_OBJECT_FETCH_PROMISOR`, so
`odb_has_object()` sets `OBJECT_INFO_SKIP_FETCH_OBJECT`.

During the 2.56 rebase, 07a540e ("gvfs-helper: create tool to fetch
objects using the GVFS Protocol") added
`!(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)` to the read-object hook gate
in `do_oid_object_info_extended()`. That condition is correct for the
gvfs-helper call, which is a remote fetch. It is wrong for the hook
call. The read-object hook is the local virtualization layer, not a
promisor-remote fetch, so `OBJECT_INFO_SKIP_FETCH_OBJECT` must not
suppress it.

With the condition, the hook does not run for the ref tip,
`odb_has_object()` returns false, and git runs fetch-pack against the
server, which rejects the request. Without it, the hook downloads the
tip and `check_connected()` returns early because of
`GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK`.

Trace2 for the same fetch in the same enlistment confirms this. v2.55
runs `subprocess/read_object` and then `fetch/consume_refs`. v2.56 runs
`fetch/fetch_refs` and then `git fetch-pack` with no `read_object`
event.

Simply removing the condition is not correct. `rev-list --missing` also
sets `OBJECT_INFO_SKIP_FETCH_OBJECT` and must not run the hook, because
it enumerates missing objects instead of materializing them. The fetch
existence check and the `rev-list --missing` parse reach the hook gate
with identical flags, so they cannot be told apart by the existing
flags alone.

The two paths differ in intent. The fetch path is an existence recheck
through `odb_has_object(ODB_HAS_OBJECT_RECHECK_PACKED)`. The
`rev-list --missing` path parses a commit and never sets
`RECHECK_PACKED`. Propagate that signal: `odb_has_object()` now sets a
new `OBJECT_INFO_RECHECK_PACKED` flag when the caller passes
`ODB_HAS_OBJECT_RECHECK_PACKED`, and the hook gate runs when the caller
is rechecking existence even if `OBJECT_INFO_SKIP_FETCH_OBJECT` is set.
This restores the v2.55 behavior for existence rechecks and keeps the
enumerate-only behavior for `rev-list --missing`.

Add a regression test to t0499 that fetches a ref whose tip is not
local, with a read-object hook installed. The hook must run and supply
the tip, and git must not download a pack. The test uses a new
type-preserving read-object hook helper, t/t0410/read-object-all,
because the existing helper only serves blobs.

Fixes microsoft#1004

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
The new read-object test helper's CR/LF shebang prevents Linux from
resolving its interpreter. Make the missing-tip fetch test runnable
without changing the helper's command or protocol behavior.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
An explicit virtualized fetch should be allowed to acquire missing ref
tips through its object providers, rather than rely on `fetch-pack`, which
a GVFS-only server rejects. This matches the permission already used by
`update_local_ref()` and leaves non-virtualized fetches unchanged.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Rechecking local packs should not fetch promised objects by default.
Virtualized fetches now ask for missing objects explicitly, so other
no-fetch checks can keep working as before. They should still retry
local packs and report corrupt objects.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Small fetches can be unpacked as loose objects, so the no-pack assertion
alone does not prove that regular object transfer was avoided. The
regression test must also rule out the `fetch_refs` path, while allowing
`upload-pack` for local ref advertisement.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
amend! gvfs-helper: create tool to fetch objects using the GVFS Protocol

gvfs-helper: create tool to fetch objects using the GVFS Protocol

Create gvfs-helper to download objects and configuration data from a
GVFS cache server or Git server through the GVFS Protocol REST API.
It uses libcurl and writes loose objects or packfiles. Git talks to
it through gvfs-helper-client, a long-running subprocess.

When Git is asked to check for an object without downloading it, the
helper and read-object hook must not download it. Git should still
retry local packs and report corrupt objects. This lets it find an
object in another local pack when a stale multi-pack-index points to
a removed pack.

During `git fetch`, however, a virtualized repository may need to
download the objects its remote refs point to. Let that check use the
helper or read-object hook instead of falling back to a regular fetch,
which a GVFS-only server does not support.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This doubled empty line was introduced by 3c7bab0 (fetch: add
--refetch option, 2022-03-28).

Assisted-by: GPT-6.1 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho Johannes Schindelin (dscho) changed the title odb: run read-object hook during existence rechecks fix v2.56.0.vfs.0.0 "requires the GVFS protocol" regression when ref tip is not local Oct 15, 2026
@dscho

Copy link
Copy Markdown
Member

For the record, those CI failures seem to be related to a libcurl update. At least when I ran t5540 before and after that update locally, I saw success before and failure afterwards. Will keep investigating.

The `jk/http-curl-strip-creds` topic introduces callers to yet-uncovered
cURL library functions, which we hereby cover in the lazyload-curl shim.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
The HTTP transport has been updated to handle changes in recent curl
versions that strip credentials from the effective URL, preventing
unnecessary credential prompts during redirects.

* jk/http-curl-strip-creds:
  http: handle curl stripping creds from effective url

Backported to Git for Windows, to address the newly-flaky t5540 and
t5550 tests (because CI updated libcurl to v8.23.0).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Restore URL-based authentication with Ubuntu's libcurl 8.18.0-1ubuntu2.8,
whose credential-stripping backport makes Git mistake the sanitized
effective URL for a redirect. Bring in the upstream fix and matching
Windows lazy-loading support from
microsoft#1007.

Assisted-by: GPT-6.1 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Johannes Schindelin (dscho) added a commit that referenced this pull request Oct 15, 2026
I [just
ran](#1005 (comment))
into new test failures. Failures unrelated to the PR in question. And
failures that have, of course, been fixed in upstream Git, but not in
any tagged version.

Let's backport them _real_ quick.

This is a replacement for #1006 (where I mistakenly thought I could
re-use the upstream branch from gitgitgadget/git without any
adjustements).
@dscho
Johannes Schindelin (dscho) marked this pull request as ready for review October 15, 2026 13:39
@dscho
Johannes Schindelin (dscho) merged commit 7134103 into microsoft:vfs-2.56.0 Oct 15, 2026
95 of 96 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants