From e8f4b6b55274bcbcc48b2f3762f7fe2619c87206 Mon Sep 17 00:00:00 2001 From: Nathan Baird Date: Wed, 15 Jul 2026 16:48:01 -0700 Subject: [PATCH] Clone: surface the real clone failure instead of a misleading InvalidDataException When 'gvfs clone --branch ' failed, CloneVerb.Execute constructed a LibGit2RepoInvoker against the enlistment's src folder unconditionally - even after the clone had already failed. On a failed clone that src folder was never created, so opening it logged a "Couldn't open repo at ...\src" warning and threw an InvalidDataException. That exception escaped the tracer scope, bypassing the else branch that prints the real error, and was caught by the generic handler which printed the misleading "Cannot clone @ ...: InvalidDataException: Couldn't open repo" message. The actionable error ("Remote branch ... not found in upstream origin") was only written to the clone log. Guard the LibGit2RepoInvoker construction behind cloneResult.Success so failed clones fall through to the existing else branch that prints the real error. Add a functional test asserting the branch-not-found error is shown and the misleading messages are not. --- .../Tests/EnlistmentPerFixture/CloneTests.cs | 25 +++++++++++++++++++ GVFS/GVFS/CommandLine/CloneVerb.cs | 15 ++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs index b554cfb591..04ed2010f7 100644 --- a/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs +++ b/GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/CloneTests.cs @@ -73,6 +73,31 @@ public void CloneCreatesCorrectFilesInRoot() } } + [TestCase] + public void CloneWithNonExistentBranchFailsWithBranchError() + { + string newEnlistmentRoot = GVFSFunctionalTestEnlistment.GetUniqueEnlistmentRoot(); + + ProcessStartInfo processInfo = new ProcessStartInfo(GVFSTestConfig.PathToGVFS); + processInfo.Arguments = $"clone {GVFSTestConfig.RepoToClone} {newEnlistmentRoot} --branch nonexistent/branch/that/does/not/exist"; + processInfo.WindowStyle = ProcessWindowStyle.Hidden; + processInfo.CreateNoWindow = true; + processInfo.WorkingDirectory = Path.GetDirectoryName(this.Enlistment.EnlistmentRoot); + processInfo.UseShellExecute = false; + processInfo.RedirectStandardOutput = true; + + ProcessResult result = ProcessHelper.Run(processInfo); + result.ExitCode.ShouldEqual(GVFSGenericError); + + // The clone should surface the real, actionable failure... + result.Output.ShouldContain("Remote branch nonexistent/branch/that/does/not/exist not found in upstream origin"); + + // ...and NOT the misleading downstream exception that used to be thrown when + // a LibGit2RepoInvoker was constructed against a src folder that was never created. + result.Output.ShouldNotContain(false, "Couldn't open repo"); + result.Output.ShouldNotContain(false, "InvalidDataException"); + } + private void SubfolderCloneShouldFail() { ProcessStartInfo processInfo = new ProcessStartInfo(GVFSTestConfig.PathToGVFS); diff --git a/GVFS/GVFS/CommandLine/CloneVerb.cs b/GVFS/GVFS/CommandLine/CloneVerb.cs index e64749d8ce..acf75cc0b5 100644 --- a/GVFS/GVFS/CommandLine/CloneVerb.cs +++ b/GVFS/GVFS/CommandLine/CloneVerb.cs @@ -152,7 +152,7 @@ public override void Execute() CacheServerInfo cacheServer = null; ServerGVFSConfig serverGVFSConfig = null; - bool trustPackIndexes; + bool trustPackIndexes = false; using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "GVFSClone")) { @@ -248,10 +248,17 @@ public override void Execute() { tracer.RelatedError(cloneResult.ErrorMessage); } - - using (var repo = new LibGit2RepoInvoker(tracer, enlistment.WorkingDirectoryBackingRoot)) + else { - trustPackIndexes = repo.GetConfigBoolOrDefault(GVFSConstants.GitConfig.TrustPackIndexes, GVFSConstants.GitConfig.TrustPackIndexesDefault); + // Only inspect the freshly-cloned repo when the clone succeeded. On a failed + // clone the 'src' working directory was never created, so constructing a + // LibGit2RepoInvoker here would log a misleading "Couldn't open repo" warning + // and throw an InvalidDataException that masks the real failure (e.g. the + // "Remote branch ... not found in upstream origin" error surfaced below). + using (var repo = new LibGit2RepoInvoker(tracer, enlistment.WorkingDirectoryBackingRoot)) + { + trustPackIndexes = repo.GetConfigBoolOrDefault(GVFSConstants.GitConfig.TrustPackIndexes, GVFSConstants.GitConfig.TrustPackIndexesDefault); + } } }