Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 19 additions & 2 deletions .github/workflows/upgrade-tests.yaml
Original file line number Diff line number Diff line change
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
7 changes: 7 additions & 0 deletions GVFS/GVFS.Common/GVFSConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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
11 changes: 11 additions & 0 deletions GVFS/GVFS.Common/Git/GitProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,17 @@ public Result VerifyMultiPackIndex(string objectDir)
return this.InvokeGitAgainstDotGitFolder("-c core.multiPackIndex=true multi-pack-index verify --object-dir=\"" + objectDir + "\" --no-progress");
}

/// <summary>
/// Verifies the integrity of a single packfile via its .idx. Returns a failure exit code if the
/// pack is truncated or otherwise unreadable. Used by pack maintenance recovery to determine
/// which pack is corrupt - the "could not load pack N" ordinal reported by the multi-pack-index
/// is an internal position, not a filename, so it cannot be mapped to a file directly.
/// </summary>
public Result VerifyPack(string packIndexPath)
{
return this.InvokeGitAgainstDotGitFolder("verify-pack \"" + packIndexPath + "\"");
}

public Result RemoteAdd(string remoteName, string url)
{
return this.InvokeGitAgainstDotGitFolder("remote add " + remoteName + " " + url);
Expand Down
14 changes: 12 additions & 2 deletions GVFS/GVFS.Common/Maintenance/GitMaintenanceScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ private void ScheduleRecurringSteps()
return;
}

if (this.gitObjects.IsUsingCacheServer())
bool usingCacheServer = this.gitObjects.IsUsingCacheServer();

if (usingCacheServer)
{
TimeSpan prefetchPeriod = TimeSpan.FromMinutes(15);
this.stepTimers.Add(new Timer(
Expand All @@ -70,8 +72,16 @@ private void ScheduleRecurringSteps()
dueTime: this.looseObjectsDueTime,
period: this.looseObjectsPeriod));

// When packfile-maintenance recovery removes a corrupt prefetch pack (and the later prefetch
// packs that depend on it), it needs a prefetch to re-download them and rebuild the
// commit-graph. This is only meaningful when a cache server is in use; otherwise the objects
// are restored on demand.
Action requestPrefetch = usingCacheServer
? () => this.queue.TryEnqueue(new PrefetchStep(this.context, this.gitObjects))
: (Action)null;

this.stepTimers.Add(new Timer(
(state) => this.queue.TryEnqueue(new PackfileMaintenanceStep(this.context)),
(state) => this.queue.TryEnqueue(new PackfileMaintenanceStep(this.context, requestPrefetch: requestPrefetch)),
state: null,
dueTime: this.packfileDueTime,
period: this.packfilePeriod));
Expand Down
Loading
Loading