Skip to content

Fix clone authentication when includeIf is in git config - #2100

Open
tyrielv wants to merge 1 commit into
microsoft:vnextfrom
tyrielv:tyrielv/fix-clone-includeif
Open

Fix clone authentication when includeIf is in git config#2100
tyrielv wants to merge 1 commit into
microsoft:vnextfrom
tyrielv:tyrielv/fix-clone-includeif

Conversation

@tyrielv

@tyrielv tyrielv commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

gvfs clone cannot authenticate when the user's git config contains an
includeIf "gitdir:..." section. The clone fails with:

Authenticating...Failed.
Cannot clone because authentication failed:
fatal: Invalid path '<enlistment>/src': No such file or directory

Cause

Clone authenticates before it creates the enlistment, but the credential verbs
run against the enlistment's .git folder through --git-dir:

  1. CloneVerb builds a GVFSEnlistment whose GitProcess targets <root>\src\.git.
  2. CloneVerb authenticates. Only <root> exists at this point.
  3. TryGetCredential runs git credential fill with --git-dir="<root>\src\.git".
  4. Git resolves that path so it can evaluate the includeIf condition, does not
    find it, and aborts before it asks for credentials.

The condition does not have to match. Git resolves the repository path before it
compares the pattern, so any gitdir: condition triggers the failure. An
includeIf "onbranch:..." condition does not, because it needs no path.

This affects VFS for Git only. Plain git clone reaches the credential helper
normally with the same config.

Fix

Pass --git-dir only when .git exists. Credential helpers still see repo-local
configuration in an established enlistment. Before clone creates the repository
there is no repo-local configuration to read, so nothing is lost by omitting it.

A linked worktree stores .git as a file that points at the real git directory
rather than as a folder, so the check accepts both. Git resolves the gitdir:
pointer in that file, so the shared repository's configuration is still used.

Two other pre-repository git calls are routed the same way, because they fail
identically during clone:

  • TryGetConfigUrlMatch runs from the GitAuthentication constructor. When it
    failed, GitSsl was never constructed and the user's http.sslCert /
    client-certificate configuration was silently dropped.
  • TryGetCertificatePassword is itself a credential fill.

Without these, a user with includeIf and certificate authentication was
still broken.

The fix is deliberately narrow. Always running the credential verbs outside the
enlistment would also fix the reported failure, but it would stop credential
helpers from reading a repository's local credential.* configuration after
clone. Four of the new unit tests fail if someone widens it that way.

The chosen route is recorded in the credential failure metadata, so a later
report of "the wrong credentials were used" can be diagnosed from the logs.

Tests

New unit tests cover all three states of .git, for all three credential verbs
(fill, approve, reject):

.git state --git-dir passed
absent (during clone) no
folder (normal enlistment) yes
file (linked worktree) yes

New functional tests clone with and without an includeIf section and confirm
that both reach the credential helper. The test without the section is the
control: it passes before and after this change, which shows the other test fails
only because of the includeIf section.

The functional tests assert that authentication was actually reached, not only
that the failure message changed. A clone that cannot reach the server exits with
the same code and prints neither of the strings under test, so absence checks
alone would pass with no network and prove nothing.

Verified by mutation. Forcing --git-dir on always fails 3 unit tests. Dropping
it always fails 4. A real clone of an authenticated repository with an includeIf
section fails on the unfixed build and succeeds on the fixed one.

Branch

This targets vnext rather than master. The defective code path is identical
in v1.0.26014.1, so this is a long-standing defect and not a 2.0 regression.
It changes runtime behavior on the credential path, so it soaks in vnext.

Also included

Two corrections to AGENTS.md that this work proved wrong:

  • GVFS.Payload has no ProjectReference items and never builds or publishes
    the projects layout.bat copies from. A project you did not publish contributes
    a binary from an earlier build, so the functional tests can silently exercise
    stale code. The documented steps now publish each changed project explicitly and
    show how to confirm the payload actually changed.
  • RunFunctionalTests-Dev.ps1 binds positional arguments to -Arch, so
    RunFunctionalTests-Dev.ps1 Debug --test=... fails its ValidateSet. The
    documented invocations now name both parameters.

'gvfs clone' failed to authenticate when the user's git config contained an
'includeIf "gitdir:..."' section:

    Cannot clone because authentication failed:
    fatal: Invalid path '<root>/src': No such file or directory

Clone authenticates before it creates the enlistment, but the credential verbs
ran against the enlistment's .git folder through --git-dir. Git resolves that
path before it evaluates an includeIf condition, so git aborted instead of
asking for credentials. The condition did not need to match. Any 'gitdir:'
condition was enough.

Pass --git-dir only when .git exists. Credential helpers still see repo-local
configuration in an established enlistment, and there is no local configuration
to read before clone creates the repository. A linked worktree stores .git as a
file rather than a folder, so accept both. Git resolves the 'gitdir:' pointer in
that file, so the shared repository's configuration is still used.

Route the other two pre-repository git calls the same way. Both hit the same
failure during clone:

  - TryGetConfigUrlMatch runs from the GitAuthentication constructor. When it
    failed, GitSsl was never built and the user's client-certificate settings
    were silently dropped.
  - TryGetCertificatePassword is itself a credential fill.

Record which route ran in the credential failure metadata, so a later report of
"the wrong credentials were used" can be diagnosed from the logs.

Add unit tests for all three states of .git (missing, folder, worktree file)
across all three credential verbs, and functional tests that clone with and
without an includeIf section. The functional tests assert that authentication
was reached, not only that the failure message changed: a clone that cannot
reach the server also exits with the same code and prints neither of the
strings under test, so absence checks alone would pass with no network.

Also correct two entries in AGENTS.md that this work proved wrong. GVFS.Payload
has no ProjectReference items and never builds or publishes the projects
layout.bat copies from, so a stale binary can be packaged and tested silently.
And RunFunctionalTests-Dev.ps1 binds positional arguments to -Arch, so a bare
'Debug --test=...' fails validation.

Assisted-by: Claude Opus 5
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv
tyrielv force-pushed the tyrielv/fix-clone-includeif branch from 6fe24c8 to 15a0fc0 Compare August 28, 2026 22:41
@tyrielv
tyrielv marked this pull request as ready for review August 28, 2026 22:52
@tyrielv
tyrielv enabled auto-merge August 28, 2026 22:53
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.

1 participant