Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9ec7abf
Auto-recover corrupt packfiles in packfile maintenance
tyrielv Jul 9, 2026
9b0787a
HttpRequestor: do not reject credentials on HTTP 400
tyrielv Aug 13, 2026
227c58d
HttpRequestor: only treat a 400 as auth failure for the cache-server …
tyrielv Aug 14, 2026
a0f5fc3
Pin GitHub Actions to full-length commit SHAs
danfiedler-msft Aug 18, 2026
2ce95d7
Merge pull request #2093 from danfiedler-msft/danfiedler/pin-actions
tyrielv Aug 18, 2026
8890e09
Merge pull request #2088 from tyrielv/user/tyvella/no-reject-credenti…
tyrielv Aug 18, 2026
109677a
Merge pull request #2052 from tyrielv/tyrielv/packfile-corruption-rec…
tyrielv Aug 26, 2026
f7babbd
Fix upgrade tests when the LKG release has multiple installers
tyrielv Aug 26, 2026
c24182d
Harden LKG installer selection to the x64 asset by name
tyrielv Aug 26, 2026
0de158e
Merge pull request #2097 from tyrielv/tyrielv/fix-upgrade-lkg-arm64
tyrielv Aug 27, 2026
f22daab
FunctionalTests: capture mount dumps and preserve logs on failure
tyrielv Aug 27, 2026
d2adb5a
Split enumeration-miss cause and de-duplicate the error telemetry
tyrielv Aug 5, 2026
6ba0bb2
Share transient libgit2 config lookup helper
tyrielv Aug 7, 2026
c511208
Merge pull request #2066 from tyrielv/tyrielv/shared-libgit2-config-l…
tyrielv Aug 27, 2026
82a228d
FunctionalTests: make shared control-repo cache setup resilient
tyrielv Aug 27, 2026
183f5eb
FunctionalTests: build control-repo cache with --mirror, not --bare
tyrielv Aug 27, 2026
6d4d4d7
FunctionalTests: serve control-repo cache SHAs, stop rebuilding cache
tyrielv Aug 27, 2026
b5d75b6
Merge pull request #2062 from tyrielv/tyrielv/ft-failure-diagnostics
tyrielv Aug 31, 2026
3c5ba6e
Merge pull request #2099 from tyrielv/tyrielv/fix-flaky-controlrepo
tyrielv Aug 31, 2026
ff32b9d
Merge pull request #2076 from tyrielv/tyrielv/enum-miss-split
tyrielv Aug 31, 2026
d2215eb
feat: Route GVFS endpoints to dedicated cache servers
derrickstolee Sep 2, 2026
d423ed1
fix: Fall back safely from dedicated cache endpoints
derrickstolee Sep 2, 2026
63f8dc0
docs: Explain endpoint-specific cache routing
derrickstolee Sep 2, 2026
f248096
test: Cover prefetch failure telemetry redaction
derrickstolee Sep 2, 2026
113b3f9
Merge pull request #2103 Route GVFS endpoints to dedicated cache servers
derrickstolee Sep 4, 2026
35272aa
Disable Git fsmonitor in virtual repositories
Sep 14, 2026
d9c9b54
Merge pull request #2106 from tyrielv/tyrielv/core-fsmonitor
tyrielv Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
cooldown:
default-days: 7
20 changes: 10 additions & 10 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Look for prior successful runs
id: check
if: github.event.inputs.git_version == ''
uses: actions/github-script@v9
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
result-encoding: string
Expand Down Expand Up @@ -199,7 +199,7 @@ jobs:

- name: Checkout source
if: steps.check.outputs.result == ''
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Validate Microsoft Git version
if: steps.check.outputs.result == ''
Expand Down Expand Up @@ -249,7 +249,7 @@ jobs:

- name: Upload microsoft/git installers
if: steps.check.outputs.result == ''
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: MicrosoftGit
path: MicrosoftGit
Expand All @@ -269,27 +269,27 @@ jobs:
- name: Skip this job if there is a previous successful run
if: needs.validate.outputs.skip != ''
id: skip
uses: actions/github-script@v9
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
core.info(`Skipping: There already is a successful run: ${{ needs.validate.outputs.skip }}`)
return true

- name: Checkout source
if: steps.skip.outputs.result != 'true'
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: src

- name: Install .NET SDK
if: steps.skip.outputs.result != 'true'
uses: actions/setup-dotnet@v6
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: src/global.json

- name: Add MSBuild to PATH
if: steps.skip.outputs.result != 'true'
uses: microsoft/setup-msbuild@v3.0.0
uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # v3.0.0

- name: Build VFS for Git
if: steps.skip.outputs.result != 'true'
Expand All @@ -308,21 +308,21 @@ jobs:

- name: Upload functional tests drop
if: steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: FunctionalTests_${{ matrix.configuration }}_${{ matrix.architecture }}
path: artifacts\GVFS.FunctionalTests

- name: Upload FastFetch drop
if: steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: FastFetch_${{ matrix.configuration }}_${{ matrix.architecture }}
path: artifacts\FastFetch

- name: Upload GVFS installer
if: steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: GVFS_${{ matrix.configuration }}_${{ matrix.architecture }}
path: artifacts\GVFS.Installers
Expand Down
32 changes: 21 additions & 11 deletions .github/workflows/functional-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Skip this job if there is a previous successful run
if: inputs.skip != ''
id: skip
uses: actions/github-script@v9
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
core.info(`Skipping: There already is a successful run: ${{ inputs.skip }}`)
Expand All @@ -80,7 +80,7 @@ jobs:
id: download-git
if: steps.skip.outputs.result != 'true'
continue-on-error: true
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ inputs.git_artifact_name }}
path: git
Expand All @@ -90,7 +90,7 @@ jobs:

- name: Download Git installer (retry)
if: steps.skip.outputs.result != 'true' && steps.download-git.outcome == 'failure'
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ inputs.git_artifact_name }}
path: git
Expand All @@ -102,7 +102,7 @@ jobs:
id: download-gvfs
if: steps.skip.outputs.result != 'true'
continue-on-error: true
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: GVFS_${{ matrix.configuration }}_${{ matrix.architecture }}
path: gvfs
Expand All @@ -112,7 +112,7 @@ jobs:

- name: Download GVFS installer (retry)
if: steps.skip.outputs.result != 'true' && steps.download-gvfs.outcome == 'failure'
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: GVFS_${{ matrix.configuration }}_${{ matrix.architecture }}
path: gvfs
Expand All @@ -124,7 +124,7 @@ jobs:
id: download-ft
if: steps.skip.outputs.result != 'true'
continue-on-error: true
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: FunctionalTests_${{ matrix.configuration }}_${{ matrix.architecture }}
path: ft
Expand All @@ -134,7 +134,7 @@ jobs:

- name: Download functional tests drop (retry)
if: steps.skip.outputs.result != 'true' && steps.download-ft.outcome == 'failure'
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: FunctionalTests_${{ matrix.configuration }}_${{ matrix.architecture }}
path: ft
Expand All @@ -145,7 +145,7 @@ jobs:
- name: Download FastFetch drop
if: steps.skip.outputs.result != 'true'
continue-on-error: true
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: FastFetch_${{ matrix.configuration }}_${{ matrix.architecture }}
path: ft
Expand Down Expand Up @@ -189,7 +189,7 @@ jobs:

- name: Upload installation logs
if: always() && steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
continue-on-error: true
with:
name: ${{ env.ARTIFACT_PREFIX }}InstallationLogs_${{ env.FT_MATRIX_NAME }}
Expand All @@ -204,18 +204,28 @@ jobs:
run: |
SET PATH=C:\Program Files\VFS for Git;%PATH%
SET GIT_TRACE2_PERF=C:\temp\git-trace2.log
SET GVFS_TEST_DIAGNOSTICS_DIR=C:\temp\gvfs-ft-diagnostics
ft\GVFS.FunctionalTests.exe /result:TestResult.xml --ci --slice=${{ matrix.nr }},12

- name: Upload functional test results
- name: Upload failure diagnostics (mount dumps + logs)
if: always() && steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
continue-on-error: true
with:
name: ${{ env.ARTIFACT_PREFIX }}FailureDiagnostics_${{ env.FT_MATRIX_NAME }}
path: C:\temp\gvfs-ft-diagnostics
if-no-files-found: ignore

- name: Upload functional test results
if: always() && steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ env.ARTIFACT_PREFIX }}FunctionalTests_Results_${{ env.FT_MATRIX_NAME }}
path: TestResult.xml

- name: Upload Git trace2 output
if: always() && steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ env.ARTIFACT_PREFIX }}GitTrace2_${{ env.FT_MATRIX_NAME }}
path: C:\temp\git-trace2.log
Expand Down
33 changes: 25 additions & 8 deletions .github/workflows/upgrade-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Skip this job if there is a previous successful run
if: inputs.skip != ''
id: skip
uses: actions/github-script@v9
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
core.info(`Skipping: There already is a successful run: ${{ inputs.skip }}`)
Expand All @@ -66,14 +66,14 @@ jobs:
id: download-git
if: steps.skip.outputs.result != 'true'
continue-on-error: true
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: MicrosoftGit
path: git

- name: Download Git installer (retry)
if: steps.skip.outputs.result != 'true' && steps.download-git.outcome == 'failure'
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: MicrosoftGit
path: git
Expand All @@ -82,14 +82,14 @@ jobs:
id: download-gvfs
if: steps.skip.outputs.result != 'true'
continue-on-error: true
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: GVFS_${{ matrix.configuration }}_x64
path: gvfs-new

- name: Download current GVFS installer (retry)
if: steps.skip.outputs.result != 'true' && steps.download-gvfs.outcome == 'failure'
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: GVFS_${{ matrix.configuration }}_x64
path: gvfs-new
Expand Down Expand Up @@ -118,8 +118,25 @@ jobs:
run: |
$ErrorActionPreference = 'Stop'

$lkgInstaller = (Get-ChildItem gvfs-lkg\SetupGVFS*.exe).FullName
$newInstaller = (Get-ChildItem gvfs-new\SetupGVFS*.exe).FullName
# Releases publish both x64 and arm64 installers. The x64 installer
# keeps the historical suffix-less name "SetupGVFS.<version>.exe"; other
# architectures add a suffix (e.g. "SetupGVFS.<version>-arm64.exe").
# These tests run on an x64 runner, so select the x64 installer by its
# suffix-less name and require exactly one match, rather than picking an
# arbitrary file when the directory holds more than one installer.
# NOTE: arm64 upgrade is not exercised here because the runner is x64;
# arm64 upgrade coverage is a known gap for when arm64 runners exist.
function Select-X64Installer($directory) {
$installers = @(Get-ChildItem "$directory\SetupGVFS*.exe" |
Where-Object { $_.Name -match '^SetupGVFS\.[\d.]+\.exe$' })
if ($installers.Count -ne 1) {
throw "Expected exactly one x64 installer in '$directory', found $($installers.Count): $($installers.Name -join ', ')"
}
return $installers[0].FullName
}

$lkgInstaller = Select-X64Installer "gvfs-lkg"
$newInstaller = Select-X64Installer "gvfs-new"
$installDir = "C:\Program Files\VFS for Git"
$testRepo = "https://dev.azure.com/gvfs/ci/_git/ForTests"
$enlistment = "C:\gvfs-upgrade-test"
Expand Down Expand Up @@ -370,7 +387,7 @@ jobs:

- name: Upload service logs
if: always() && steps.skip.outputs.result != 'true'
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
continue-on-error: true
with:
name: UpgradeTest_Logs_${{ matrix.scenario }}
Expand Down
4 changes: 3 additions & 1 deletion GVFS/FastFetch/FastFetchVerb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ private int ExecuteWithExitCode()
string fastfetchLogFile = Enlistment.GetNewLogFileName(enlistment.FastFetchLogRoot, "fastfetch");
tracer.AddLogFileEventListener(fastfetchLogFile, EventLevel.Informational, Keywords.Any);

CacheServerInfo cacheServer = new CacheServerInfo(this.GetRemoteUrl(enlistment), null);
CacheServerInfo cacheServer = string.IsNullOrWhiteSpace(this.CacheServerUrl)
? CacheServerResolver.GetCacheServerFromConfig(enlistment)
: new CacheServerInfo(this.GetRemoteUrl(enlistment), null);

tracer.WriteStartEvent(
enlistment.PrimaryEnlistmentRoot,
Expand Down
38 changes: 37 additions & 1 deletion GVFS/GVFS.CommandLine.Tests/GvfsMainCliTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,36 @@ public void Clone_FullCommandLine_ParsesCorrectly()
{
"clone", "https://example.com/repo", @"C:\Users\test\repo",
"--cache-server-url", "https://cache.test",
"--prefetch-cache-server-url", "https://prefetch-cache.test",
"--get-cache-server-url", "https://get-cache.test",
"--post-cache-server-url", "https://post-cache.test",
"--sizes-cache-server-url", "https://sizes-cache.test",
"-b", "develop",
"--single-branch",
"--no-mount",
"--no-prefetch"
});
Assert.That(parseResult.Errors, Is.Empty, "Full clone command should parse without errors");
Assert.Multiple(() =>
{
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--cache-server-url")), Is.EqualTo("https://cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--prefetch-cache-server-url")), Is.EqualTo("https://prefetch-cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--get-cache-server-url")), Is.EqualTo("https://get-cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--post-cache-server-url")), Is.EqualTo("https://post-cache.test"));
Assert.That(parseResult.GetValue((Option<string>)FindOptionOnCommand("clone", "--sizes-cache-server-url")), Is.EqualTo("https://sizes-cache.test"));
});
}

[TestCase("--prefetch-cache-server-url")]
[TestCase("--get-cache-server-url")]
[TestCase("--post-cache-server-url")]
[TestCase("--sizes-cache-server-url")]
public void Clone_EndpointCacheServerUrl_RejectsInvalidUrl(string optionName)
{
var parseResult = rootCommand.Parse(new[] { "clone", "https://example.com/repo", optionName, "not-a-url" });

Assert.That(parseResult.Errors, Has.Count.EqualTo(1));
Assert.That(parseResult.Errors[0].Message, Does.Contain("requires an absolute URL"));
}

[Test]
Expand Down Expand Up @@ -342,7 +366,19 @@ public void Repair_FullCommandLine_ParsesCorrectly()
[Test]
public void Clone_HasAllExpectedOptions()
{
var expected = new[] { "--cache-server-url", "--branch", "--single-branch", "--no-mount", "--no-prefetch", "--local-cache-path" };
var expected = new[]
{
"--cache-server-url",
"--prefetch-cache-server-url",
"--get-cache-server-url",
"--post-cache-server-url",
"--sizes-cache-server-url",
"--branch",
"--single-branch",
"--no-mount",
"--no-prefetch",
"--local-cache-path",
};
foreach (var optName in expected)
{
Assert.That(FindOptionOnCommand("clone", optName), Is.Not.Null,
Expand Down
11 changes: 11 additions & 0 deletions GVFS/GVFS.Common/GVFSConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public static class GitConfig
public const string MountId = GVFSPrefix + "mount-id";
public const string EnlistmentId = GVFSPrefix + "enlistment-id";
public const string CacheServer = GVFSPrefix + "cache-server";
public const string PrefetchCacheServer = GVFSPrefix + "prefetch.cache-server";
public const string GetCacheServer = GVFSPrefix + "get.cache-server";
public const string PostCacheServer = GVFSPrefix + "post.cache-server";
public const string SizesCacheServer = GVFSPrefix + "sizes.cache-server";
public const string DeprecatedCacheEndpointSuffix = ".cache-server-url";
public const string HooksPrefix = GitConfig.GVFSPrefix + "clone.default-";
public const string GVFSTelemetryId = GitConfig.GVFSPrefix + "telemetry-id";
Expand All @@ -45,6 +49,13 @@ public static class GitConfig
public const string TrustPackIndexes = GVFSPrefix + "trust-pack-indexes";
public const bool TrustPackIndexesDefault = true;

/* Kill switch for the destructive part of packfile-maintenance corruption recovery: when
* false, GVFS still detects and reports corrupt packs but does not delete them (or later
* prefetch packs) and does not request a restoring prefetch. Detection/telemetry is
* unaffected; the non-destructive multi-pack-index rewrite still runs. */
public const string EnablePackfileRecovery = GVFSPrefix + "enable-packfile-recovery";
public const bool EnablePackfileRecoveryDefault = true;

public const string ShowHydrationStatus = GVFSPrefix + "show-hydration-status";
public const bool ShowHydrationStatusDefault = false;

Expand Down
Loading
Loading