/// <param name="branchOrCommit">A specific branch to filter for, or null for all branches returned from info/refs</param> public override void FastFetch(string branchOrCommit, bool isBranch) { if (string.IsNullOrWhiteSpace(branchOrCommit)) { throw new FetchException("Must specify branch or commit to fetch"); } GitRefs refs = null; string commitToFetch; if (isBranch) { refs = this.ObjectRequestor.QueryInfoRefs(branchOrCommit); if (refs == null) { throw new FetchException("Could not query info/refs from: {0}", this.Enlistment.RepoUrl); } else if (refs.Count == 0) { throw new FetchException("Could not find branch {0} in info/refs from: {1}", branchOrCommit, this.Enlistment.RepoUrl); } commitToFetch = refs.GetTipCommitId(branchOrCommit); } else { commitToFetch = branchOrCommit; } this.DownloadMissingCommit(commitToFetch, this.GitObjects); // Configure pipeline // Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper like in FetchHelper.cs // Checkout diff output => FindMissingBlobs => BatchDownload => IndexPack => Checkout available blobs CheckoutJob checkout = new CheckoutJob(this.checkoutThreadCount, this.PathWhitelist, commitToFetch, this.Tracer, this.Enlistment); FindMissingBlobsJob blobFinder = new FindMissingBlobsJob(this.SearchThreadCount, checkout.RequiredBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment); BatchObjectDownloadJob downloader = new BatchObjectDownloadJob(this.DownloadThreadCount, this.ChunkSize, blobFinder.DownloadQueue, checkout.AvailableBlobShas, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects); IndexPackJob packIndexer = new IndexPackJob(this.IndexThreadCount, downloader.AvailablePacks, checkout.AvailableBlobShas, this.Tracer, this.GitObjects); // Start pipeline downloader.Start(); blobFinder.Start(); checkout.Start(); blobFinder.WaitForCompletion(); this.HasFailures |= blobFinder.HasFailures; // Delay indexing. It interferes with FindMissingBlobs, and doesn't help Bootstrapping. packIndexer.Start(); downloader.WaitForCompletion(); this.HasFailures |= downloader.HasFailures; packIndexer.WaitForCompletion(); this.HasFailures |= packIndexer.HasFailures; // Since pack indexer is the last to finish before checkout finishes, it should propagate completion. // This prevents availableObjects from completing before packIndexer can push its objects through this link. checkout.AvailableBlobShas.CompleteAdding(); checkout.WaitForCompletion(); this.HasFailures |= checkout.HasFailures; if (!this.SkipConfigUpdate && !this.HasFailures) { this.UpdateRefs(branchOrCommit, isBranch, refs); if (isBranch) { // Update the refspec before setting the upstream or git will complain the remote branch doesn't exist this.HasFailures |= !this.UpdateRefSpec(this.Tracer, this.Enlistment, branchOrCommit, refs); using (ITracer activity = this.Tracer.StartActivity("SetUpstream", EventLevel.Informational)) { string remoteBranch = refs.GetBranchRefPairs().Single().Key; GitProcess git = new GitProcess(this.Enlistment); GitProcess.Result result = git.SetUpstream(branchOrCommit, remoteBranch); if (result.HasErrors) { activity.RelatedError("Could not set upstream for {0} to {1}: {2}", branchOrCommit, remoteBranch, result.Errors); this.HasFailures = true; } } } bool shouldSignIndex = !this.GetIsIndexSigningOff(); // Update the index EventMetadata updateIndexMetadata = new EventMetadata(); updateIndexMetadata.Add("IndexSigningIsOff", shouldSignIndex); using (ITracer activity = this.Tracer.StartActivity("UpdateIndex", EventLevel.Informational, Keywords.Telemetry, updateIndexMetadata)) { Index sourceIndex = this.GetSourceIndex(); GitIndexGenerator indexGen = new GitIndexGenerator(this.Tracer, this.Enlistment, shouldSignIndex); indexGen.CreateFromHeadTree(indexVersion: 2); this.HasFailures |= indexGen.HasFailures; if (!indexGen.HasFailures) { Index newIndex = new Index( this.Enlistment.EnlistmentRoot, this.Tracer, Path.Combine(this.Enlistment.DotGitRoot, GVFSConstants.DotGit.IndexName), readOnly: false); // Update from disk only if the caller says it is ok via command line // or if we updated the whole tree and know that all files are up to date bool allowIndexMetadataUpdateFromWorkingTree = this.allowIndexMetadataUpdateFromWorkingTree || checkout.UpdatedWholeTree; newIndex.UpdateFileSizesAndTimes(checkout.AddedOrEditedLocalFiles, allowIndexMetadataUpdateFromWorkingTree, shouldSignIndex, sourceIndex); } } } }
public CloneVerb.Result CreateClone(GitRefs refs, string branch) { GitObjects gitObjects = new GitObjects(this.tracer, this.enlistment, this.objectRequestor); CloneVerb.Result initRepoResult = this.TryInitRepo(refs, this.enlistment); if (!initRepoResult.Success) { return(initRepoResult); } string errorMessage; if (!this.enlistment.TryConfigureAlternate(out errorMessage)) { return(new CloneVerb.Result("Error configuring alternate: " + errorMessage)); } if (!gitObjects.TryDownloadAndSaveCommits(refs.GetTipCommitIds(), commitDepth: 2)) { return(new CloneVerb.Result("Could not download tip commits from: " + Uri.EscapeUriString(this.enlistment.ObjectsEndpointUrl))); } GitProcess git = new GitProcess(this.enlistment); if (!this.SetConfigSettings(git)) { return(new CloneVerb.Result("Unable to configure git repo")); } string originBranchName = "origin/" + branch; GitProcess.Result createBranchResult = git.CreateBranchWithUpstream(branch, originBranchName); if (createBranchResult.HasErrors) { return(new CloneVerb.Result("Unable to create branch '" + originBranchName + "': " + createBranchResult.Errors + "\r\n" + createBranchResult.Output)); } File.WriteAllText( Path.Combine(this.enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Head), "ref: refs/heads/" + branch); File.AppendAllText( Path.Combine(this.enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Info.SparseCheckoutPath), GVFSConstants.GitPathSeparatorString + GVFSConstants.SpecialGitFiles.GitAttributes + "\n"); CloneVerb.Result hydrateResult = this.HydrateRootGitAttributes(gitObjects, branch); if (!hydrateResult.Success) { return(hydrateResult); } this.CreateGitScript(); GitProcess.Result forceCheckoutResult = git.ForceCheckout(branch); if (forceCheckoutResult.HasErrors) { string[] errorLines = forceCheckoutResult.Errors.Split('\n'); StringBuilder checkoutErrors = new StringBuilder(); foreach (string gitError in errorLines) { if (IsForceCheckoutErrorCloneFailure(gitError)) { checkoutErrors.AppendLine(gitError); } } if (checkoutErrors.Length > 0) { string error = "Could not complete checkout of branch: " + branch + ", " + checkoutErrors.ToString(); this.tracer.RelatedError(error); return(new CloneVerb.Result(error)); } } GitProcess.Result updateIndexresult = git.UpdateIndexVersion4(); if (updateIndexresult.HasErrors) { string error = "Could not update index, error: " + updateIndexresult.Errors; this.tracer.RelatedError(error); return(new CloneVerb.Result(error)); } string installHooksError; if (!HooksInstallHelper.InstallHooks(this.enlistment, out installHooksError)) { this.tracer.RelatedError(installHooksError); return(new CloneVerb.Result(installHooksError)); } using (RepoMetadata repoMetadata = new RepoMetadata(this.enlistment.DotGVFSRoot)) { repoMetadata.SaveCurrentDiskLayoutVersion(); } // Prepare the working directory folder for GVFS last to ensure that gvfs mount will fail if gvfs clone has failed string prepGVFltError; if (!GVFltCallbacks.TryPrepareFolderForGVFltCallbacks(this.enlistment.WorkingDirectoryRoot, out prepGVFltError)) { this.tracer.RelatedError(prepGVFltError); return(new CloneVerb.Result(prepGVFltError)); } return(new CloneVerb.Result(true)); }
private Result CreateClone( ITracer tracer, GVFSEnlistment enlistment, GitObjectsHttpRequestor objectRequestor, GitRefs refs, string branch) { Result initRepoResult = this.TryInitRepo(tracer, refs, enlistment); if (!initRepoResult.Success) { return(initRepoResult); } PhysicalFileSystem fileSystem = new PhysicalFileSystem(); string errorMessage; if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out errorMessage)) { return(new Result("Error configuring alternate: " + errorMessage)); } GitRepo gitRepo = new GitRepo(tracer, enlistment, fileSystem); GVFSContext context = new GVFSContext(tracer, fileSystem, gitRepo, enlistment); GVFSGitObjects gitObjects = new GVFSGitObjects(context, objectRequestor); if (!this.TryDownloadCommit( refs.GetTipCommitId(branch), enlistment, objectRequestor, gitObjects, gitRepo, out errorMessage)) { return(new Result(errorMessage)); } if (!GVFSVerb.TrySetRequiredGitConfigSettings(enlistment) || !GVFSVerb.TrySetOptionalGitConfigSettings(enlistment)) { return(new Result("Unable to configure git repo")); } CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); if (!cacheServerResolver.TrySaveUrlToLocalConfig(objectRequestor.CacheServer, out errorMessage)) { return(new Result("Unable to configure cache server: " + errorMessage)); } GitProcess git = new GitProcess(enlistment); string originBranchName = "origin/" + branch; GitProcess.Result createBranchResult = git.CreateBranchWithUpstream(branch, originBranchName); if (createBranchResult.HasErrors) { return(new Result("Unable to create branch '" + originBranchName + "': " + createBranchResult.Errors + "\r\n" + createBranchResult.Output)); } File.WriteAllText( Path.Combine(enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Head), "ref: refs/heads/" + branch); if (!this.TryDownloadRootGitAttributes(enlistment, gitObjects, gitRepo, out errorMessage)) { return(new Result(errorMessage)); } this.CreateGitScript(enlistment); string installHooksError; if (!HooksInstaller.InstallHooks(context, out installHooksError)) { tracer.RelatedError(installHooksError); return(new Result(installHooksError)); } GitProcess.Result forceCheckoutResult = git.ForceCheckout(branch); if (forceCheckoutResult.HasErrors && forceCheckoutResult.Errors.IndexOf("unable to read tree") > 0) { // It is possible to have the above TryDownloadCommit() fail because we // already have the commit and root tree we intend to check out, but // don't have a tree further down the working directory. If we fail // checkout here, its' because we don't have these trees and the // read-object hook is not available yet. Force downloading the commit // again and retry the checkout. if (!this.TryDownloadCommit( refs.GetTipCommitId(branch), enlistment, objectRequestor, gitObjects, gitRepo, out errorMessage, checkLocalObjectCache: false)) { return(new Result(errorMessage)); } forceCheckoutResult = git.ForceCheckout(branch); } if (forceCheckoutResult.HasErrors) { string[] errorLines = forceCheckoutResult.Errors.Split('\n'); StringBuilder checkoutErrors = new StringBuilder(); foreach (string gitError in errorLines) { if (IsForceCheckoutErrorCloneFailure(gitError)) { checkoutErrors.AppendLine(gitError); } } if (checkoutErrors.Length > 0) { string error = "Could not complete checkout of branch: " + branch + ", " + checkoutErrors.ToString(); tracer.RelatedError(error); return(new Result(error)); } } if (!RepoMetadata.TryInitialize(tracer, enlistment.DotGVFSRoot, out errorMessage)) { tracer.RelatedError(errorMessage); return(new Result(errorMessage)); } try { RepoMetadata.Instance.SaveCloneMetadata(tracer, enlistment); this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment); } catch (Exception e) { tracer.RelatedError(e.ToString()); return(new Result(e.Message)); } finally { RepoMetadata.Shutdown(); } // Prepare the working directory folder for GVFS last to ensure that gvfs mount will fail if gvfs clone has failed Exception exception; string prepFileSystemError; if (!GVFSPlatform.Instance.KernelDriver.TryPrepareFolderForCallbacks(enlistment.WorkingDirectoryRoot, out prepFileSystemError, out exception)) { EventMetadata metadata = new EventMetadata(); metadata.Add(nameof(prepFileSystemError), prepFileSystemError); if (exception != null) { metadata.Add("Exception", exception.ToString()); } tracer.RelatedError(metadata, $"{nameof(this.CreateClone)}: TryPrepareFolderForCallbacks failed"); return(new Result(prepFileSystemError)); } return(new Result(true)); }
protected virtual GitProcess Start(string [] command, Action<ProcessStartInfo> initialize) { var startInfo = new ProcessStartInfo(); startInfo.FileName = "git"; startInfo.SetArguments(command); startInfo.CreateNoWindow = true; startInfo.UseShellExecute = false; startInfo.EnvironmentVariables["GIT_PAGER"] = "cat"; RedirectStderr(startInfo); initialize(startInfo); Trace.WriteLine("Starting process: " + startInfo.FileName + " " + startInfo.Arguments, "git command"); var process = new GitProcess(Process.Start(startInfo)); process.ConsumeStandardError(); return process; }
private void PostFetchJob(List <string> packIndexes) { try { using (FileBasedLock postFetchFileLock = GVFSPlatform.Instance.CreateFileBasedLock( this.context.FileSystem, this.context.Tracer, Path.Combine(this.context.Enlistment.GitObjectsRoot, PostFetchLock))) { if (!postFetchFileLock.TryAcquireLock()) { this.context.Tracer.RelatedInfo(PostFetchTelemetryKey + ": Skipping post-fetch work since another process holds the lock"); return; } this.postFetchGitProcess = new GitProcess(this.context.Enlistment); using (ITracer activity = this.context.Tracer.StartActivity("TryWriteMultiPackIndex", EventLevel.Informational, Keywords.Telemetry, metadata: null)) { string midxLockFile = Path.Combine(this.context.Enlistment.GitPackRoot, MultiPackIndexLock); this.context.FileSystem.TryDeleteFile(midxLockFile); if (this.stopping) { this.context.Tracer.RelatedWarning( metadata: null, message: PostFetchTelemetryKey + ": Not launching 'git multi-pack-index' because the mount is stopping", keywords: Keywords.Telemetry); return; } GitProcess.Result result = this.postFetchGitProcess.WriteMultiPackIndex(this.context.Enlistment.GitObjectsRoot); if (!this.stopping && result.HasErrors) { this.context.Tracer.RelatedWarning( metadata: null, message: PostFetchTelemetryKey + ": Failed to generate multi-pack-index for new packfiles:" + result.Errors, keywords: Keywords.Telemetry); return; } } if (packIndexes == null || packIndexes.Count == 0) { this.context.Tracer.RelatedInfo(PostFetchTelemetryKey + ": Skipping commit-graph write due to no new packfiles"); return; } using (ITracer activity = this.context.Tracer.StartActivity("TryWriteGitCommitGraph", EventLevel.Informational, Keywords.Telemetry, metadata: null)) { string graphLockFile = Path.Combine(this.context.Enlistment.GitObjectsRoot, "info", CommitGraphLock); this.context.FileSystem.TryDeleteFile(graphLockFile); if (this.stopping) { this.context.Tracer.RelatedWarning( metadata: null, message: PostFetchTelemetryKey + ": Not launching 'git commit-graph' because the mount is stopping", keywords: Keywords.Telemetry); return; } GitProcess.Result result = this.postFetchGitProcess.WriteCommitGraph(this.context.Enlistment.GitObjectsRoot, packIndexes); if (!this.stopping && result.HasErrors) { this.context.Tracer.RelatedWarning( metadata: null, message: PostFetchTelemetryKey + ": Failed to generate commit-graph for new packfiles:" + result.Errors, keywords: Keywords.Telemetry); return; } } } } catch (IOException e) { this.context.Tracer.RelatedWarning( metadata: this.CreateEventMetadata(null, e), message: PostFetchTelemetryKey + ": IOException while running post-fetch job: " + e.Message, keywords: Keywords.Telemetry); } catch (Exception e) { this.context.Tracer.RelatedError( metadata: this.CreateEventMetadata(null, e), message: PostFetchTelemetryKey + ": Exception while running post-fetch job: " + e.Message, keywords: Keywords.Telemetry); Environment.Exit((int)ReturnCode.GenericError); } }
public override void Execute() { int exitCode = 0; this.ValidatePathParameter(this.EnlistmentRootPathParameter); this.ValidatePathParameter(this.LocalCacheRoot); string fullEnlistmentRootPathParameter; string normalizedEnlistmentRootPath = this.GetCloneRoot(out fullEnlistmentRootPathParameter); if (!string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { string fullLocalCacheRootPath = Path.GetFullPath(this.LocalCacheRoot); string errorMessage; string normalizedLocalCacheRootPath; if (!GSDPlatform.Instance.FileSystem.TryGetNormalizedPath(fullLocalCacheRootPath, out normalizedLocalCacheRootPath, out errorMessage)) { this.ReportErrorAndExit($"Failed to determine normalized path for '--local-cache-path' path {fullLocalCacheRootPath}: {errorMessage}"); } if (normalizedLocalCacheRootPath.StartsWith( Path.Combine(normalizedEnlistmentRootPath, GSDConstants.WorkingDirectoryRootName), StringComparison.OrdinalIgnoreCase)) { this.ReportErrorAndExit("'--local-cache-path' cannot be inside the src folder"); } } this.CheckNotInsideExistingRepo(normalizedEnlistmentRootPath); this.BlockEmptyCacheServerUrl(this.CacheServerUrl); try { GSDEnlistment enlistment; Result cloneResult = new Result(false); CacheServerInfo cacheServer = null; ServerGSDConfig serverGSDConfig = null; using (JsonTracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "GSDClone")) { cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out enlistment); if (cloneResult.Success) { tracer.AddLogFileEventListener( GSDEnlistment.GetNewGSDLogFileName(enlistment.GSDLogsRoot, GSDConstants.LogFileTypes.Clone), EventLevel.Informational, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, this.CacheServerUrl, new EventMetadata { { "Branch", this.Branch }, { "LocalCacheRoot", this.LocalCacheRoot }, { "SingleBranch", this.SingleBranch }, { "NoMount", this.NoMount }, { "NoPrefetch", this.NoPrefetch }, { "Unattended", this.Unattended }, { "IsElevated", GSDPlatform.Instance.IsElevated() }, { "NamedPipeName", enlistment.NamedPipeName }, { "ProcessID", Process.GetCurrentProcess().Id }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, { nameof(fullEnlistmentRootPathParameter), fullEnlistmentRootPathParameter }, }); CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); cacheServer = cacheServerResolver.ParseUrlOrFriendlyName(this.CacheServerUrl); string resolvedLocalCacheRoot; if (string.IsNullOrWhiteSpace(this.LocalCacheRoot)) { string localCacheRootError; if (!LocalCacheResolver.TryGetDefaultLocalCacheRoot(enlistment, out resolvedLocalCacheRoot, out localCacheRootError)) { this.ReportErrorAndExit( tracer, $"Failed to determine the default location for the local GSD cache: `{localCacheRootError}`"); } } else { resolvedLocalCacheRoot = Path.GetFullPath(this.LocalCacheRoot); } this.Output.WriteLine("Clone parameters:"); this.Output.WriteLine(" Repo URL: " + enlistment.RepoUrl); this.Output.WriteLine(" Branch: " + (string.IsNullOrWhiteSpace(this.Branch) ? "Default" : this.Branch)); this.Output.WriteLine(" Cache Server: " + cacheServer); this.Output.WriteLine(" Local Cache: " + resolvedLocalCacheRoot); this.Output.WriteLine(" Destination: " + enlistment.EnlistmentRoot); string authErrorMessage; if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage)) { this.ReportErrorAndExit(tracer, "Cannot clone because authentication failed: " + authErrorMessage); } RetryConfig retryConfig = this.GetRetryConfig(tracer, enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes)); serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig); cacheServer = this.ResolveCacheServer(tracer, cacheServer, cacheServerResolver, serverGSDConfig); this.ValidateClientVersions(tracer, enlistment, serverGSDConfig, showWarnings: true); this.ShowStatusWhileRunning( () => { cloneResult = this.TryClone(tracer, enlistment, cacheServer, retryConfig, serverGSDConfig, resolvedLocalCacheRoot); return(cloneResult.Success); }, "Cloning", normalizedEnlistmentRootPath); } if (!cloneResult.Success) { tracer.RelatedError(cloneResult.ErrorMessage); } } if (cloneResult.Success) { if (!this.NoPrefetch) { ReturnCode result = this.Execute <PrefetchVerb>( enlistment, verb => { verb.Commits = true; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = cacheServer; verb.ServerGSDConfig = serverGSDConfig; }); if (result != ReturnCode.Success) { this.Output.WriteLine("\r\nError during prefetch @ {0}", fullEnlistmentRootPathParameter); exitCode = (int)result; } } if (this.NoMount) { this.Output.WriteLine("\r\nIn order to mount, first cd to within your enlistment, then call: "); this.Output.WriteLine("gvfs mount"); } else { this.Execute <MountVerb>( enlistment, verb => { verb.SkipMountedCheck = true; verb.SkipVersionCheck = true; verb.ResolvedCacheServer = cacheServer; verb.DownloadedGSDConfig = serverGSDConfig; }); GitProcess git = new GitProcess(enlistment); git.ForceCheckoutAllFiles(); } } else { this.Output.WriteLine("\r\nCannot clone @ {0}", fullEnlistmentRootPathParameter); this.Output.WriteLine("Error: {0}", cloneResult.ErrorMessage); exitCode = (int)ReturnCode.GenericError; } } catch (AggregateException e) { this.Output.WriteLine("Cannot clone @ {0}:", fullEnlistmentRootPathParameter); foreach (Exception ex in e.Flatten().InnerExceptions) { this.Output.WriteLine("Exception: {0}", ex.ToString()); } exitCode = (int)ReturnCode.GenericError; } catch (VerbAbortedException) { throw; } catch (Exception e) { this.ReportErrorAndExit("Cannot clone @ {0}: {1}", fullEnlistmentRootPathParameter, e.ToString()); } Environment.Exit(exitCode); }
protected override void Execute(GVFSEnlistment enlistment) { using (JsonEtwTracer tracer = new JsonEtwTracer(GVFSConstants.GVFSEtwProviderName, "Prefetch")) { if (this.Verbose) { tracer.AddConsoleEventListener(EventLevel.Informational, Keywords.Any); } tracer.AddLogFileEventListener( GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Prefetch), EventLevel.Informational, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, enlistment.CacheServerUrl); try { EventMetadata metadata = new EventMetadata(); metadata.Add("Commits", this.Commits); metadata.Add("PathWhitelist", this.PathWhitelist); metadata.Add("PathWhitelistFile", this.PathWhitelistFile); tracer.RelatedEvent(EventLevel.Informational, "PerformPrefetch", metadata); if (this.Commits) { if (!string.IsNullOrEmpty(this.PathWhitelistFile) || !string.IsNullOrWhiteSpace(this.PathWhitelist)) { this.ReportErrorAndExit("Cannot supply both --commits (-c) and --folders (-f)"); } PrefetchHelper prefetchHelper = new PrefetchHelper( tracer, enlistment, DownloadThreadCount); if (this.Verbose) { prefetchHelper.TryPrefetchCommitsAndTrees(); } else { this.ShowStatusWhileRunning( () => { return(prefetchHelper.TryPrefetchCommitsAndTrees()); }, "Fetching commits and trees"); } return; } FetchHelper fetchHelper = new FetchHelper( tracer, enlistment, ChunkSize, SearchThreadCount, DownloadThreadCount, IndexThreadCount); if (!FetchHelper.TryLoadPathWhitelist(tracer, this.PathWhitelist, this.PathWhitelistFile, enlistment, fetchHelper.PathWhitelist)) { Environment.ExitCode = (int)ReturnCode.GenericError; return; } GitProcess gitProcess = new GitProcess(enlistment); GitProcess.Result result = gitProcess.RevParse(GVFSConstants.HeadCommitName); if (result.HasErrors) { tracer.RelatedError(result.Errors); this.Output.WriteLine(result.Errors); Environment.ExitCode = (int)ReturnCode.GenericError; return; } string headCommitId = result.Output; Func <bool> doPrefetch = () => { try { fetchHelper.FastFetch(headCommitId.Trim(), isBranch: false); return(!fetchHelper.HasFailures); } catch (FetchHelper.FetchException e) { tracer.RelatedError(e.Message); return(false); } }; if (this.Verbose) { doPrefetch(); } else { this.ShowStatusWhileRunning(doPrefetch, "Fetching blobs"); } if (fetchHelper.HasFailures) { Environment.ExitCode = 1; } } catch (AggregateException e) { this.Output.WriteLine("Cannot prefetch @ {0}:", enlistment.EnlistmentRoot); foreach (Exception ex in e.Flatten().InnerExceptions) { this.Output.WriteLine("Exception: {0}", ex.ToString()); } Environment.ExitCode = (int)ReturnCode.GenericError; } catch (VerbAbortedException) { throw; } catch (Exception e) { this.ReportErrorAndExit("Cannot prefetch @ {0}: {1}", enlistment.EnlistmentRoot, e.ToString()); } } }
public override void Execute() { string hooksPath = this.GetGVFSHooksPathAndCheckVersion(tracer: null); GVFSEnlistment enlistment = GVFSEnlistment.CreateWithoutRepoUrlFromDirectory( this.EnlistmentRootPath, GitProcess.GetInstalledGitBinPath(), hooksPath); if (enlistment == null) { this.ReportErrorAndExit("'gvfs repair' must be run within a GVFS enlistment"); } if (!this.Confirmed) { this.Output.WriteLine( @"WARNING: THIS IS AN EXPERIMENTAL FEATURE This command detects and repairs issues that prevent a GVFS repo from mounting. A few such checks are currently implemented, and some of them can be repaired. More repairs and more checks are coming soon. Without --confirm, it will non-invasively check if repairs are necessary. To actually execute any necessary repair(s), run 'gvfs repair --confirm' "); } string error; if (!DiskLayoutUpgrade.TryCheckDiskLayoutVersion(tracer: null, enlistmentRoot: enlistment.EnlistmentRoot, error: out error)) { this.ReportErrorAndExit(error); } if (!ConsoleHelper.ShowStatusWhileRunning( () => { // Don't use 'gvfs status' here. The repo may be corrupt such that 'gvfs status' cannot run normally, // causing repair to continue when it shouldn't. using (NamedPipeClient pipeClient = new NamedPipeClient(enlistment.NamedPipeName)) { if (!pipeClient.Connect()) { return(true); } } return(false); }, "Checking if GVFS is mounted", this.Output, showSpinner: true, gvfsLogEnlistmentRoot: null)) { this.ReportErrorAndExit("You can only run 'gvfs repair' if GVFS is not mounted. Run 'gvfs unmount' and try again."); } this.Output.WriteLine(); using (JsonEtwTracer tracer = new JsonEtwTracer(GVFSConstants.GVFSEtwProviderName, "RepairVerb")) { tracer.AddLogFileEventListener( GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Repair), EventLevel.Verbose, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, "N/A", enlistment.GitObjectsRoot, new EventMetadata { { "Confirmed", this.Confirmed }, { "IsElevated", ProcessHelper.IsAdminElevated() }, }); List <RepairJob> jobs = new List <RepairJob>(); // Repair databases jobs.Add(new BackgroundOperationDatabaseRepairJob(tracer, this.Output, enlistment)); jobs.Add(new RepoMetadataDatabaseRepairJob(tracer, this.Output, enlistment)); jobs.Add(new PlaceholderDatabaseRepairJob(tracer, this.Output, enlistment)); jobs.Add(new BlobSizeDatabaseRepairJob(tracer, this.Output, enlistment)); // Repair .git folder files jobs.Add(new GitHeadRepairJob(tracer, this.Output, enlistment)); jobs.Add(new GitIndexRepairJob(tracer, this.Output, enlistment)); jobs.Add(new GitConfigRepairJob(tracer, this.Output, enlistment)); Dictionary <RepairJob, List <string> > healthy = new Dictionary <RepairJob, List <string> >(); Dictionary <RepairJob, List <string> > cantFix = new Dictionary <RepairJob, List <string> >(); Dictionary <RepairJob, List <string> > fixable = new Dictionary <RepairJob, List <string> >(); foreach (RepairJob job in jobs) { List <string> messages = new List <string>(); switch (job.HasIssue(messages)) { case RepairJob.IssueType.None: healthy[job] = messages; break; case RepairJob.IssueType.CantFix: cantFix[job] = messages; break; case RepairJob.IssueType.Fixable: fixable[job] = messages; break; } } foreach (RepairJob job in healthy.Keys) { this.WriteMessage(tracer, string.Format("{0, -30}: Healthy", job.Name)); this.WriteMessages(tracer, healthy[job]); } if (healthy.Count > 0) { this.Output.WriteLine(); } foreach (RepairJob job in cantFix.Keys) { this.WriteMessage(tracer, job.Name); this.WriteMessages(tracer, cantFix[job]); this.Indent(); this.WriteMessage(tracer, "'gvfs repair' does not currently support fixing this problem"); this.Output.WriteLine(); } foreach (RepairJob job in fixable.Keys) { this.WriteMessage(tracer, job.Name); this.WriteMessages(tracer, fixable[job]); this.Indent(); if (this.Confirmed) { List <string> repairMessages = new List <string>(); switch (job.TryFixIssues(repairMessages)) { case RepairJob.FixResult.Success: this.WriteMessage(tracer, "Repair succeeded"); break; case RepairJob.FixResult.ManualStepsRequired: this.WriteMessage(tracer, "Repair succeeded, but requires some manual steps before remounting."); break; case RepairJob.FixResult.Failure: this.WriteMessage(tracer, "Repair failed. " + ConsoleHelper.GetGVFSLogMessage(enlistment.EnlistmentRoot)); break; } this.WriteMessages(tracer, repairMessages); } else { this.WriteMessage(tracer, "Run 'gvfs repair --confirm' to attempt a repair"); } this.Output.WriteLine(); } } }
private int ExecuteWithExitCode() { // CmdParser doesn't strip quotes, and Path.Combine will throw this.GitBinPath = this.GitBinPath.Replace("\"", string.Empty); if (!GitProcess.GitExists(this.GitBinPath)) { Console.WriteLine( "Could not find git.exe {0}", !string.IsNullOrWhiteSpace(this.GitBinPath) ? "at " + this.GitBinPath : "on %PATH%"); return(ExitFailure); } if (this.Commit != null && this.Branch != null) { Console.WriteLine("Cannot specify both a commit sha and a branch name."); return(ExitFailure); } this.CacheServerUrl = Enlistment.StripObjectsEndpointSuffix(this.CacheServerUrl); this.SearchThreadCount = this.SearchThreadCount > 0 ? this.SearchThreadCount : Environment.ProcessorCount; this.DownloadThreadCount = this.DownloadThreadCount > 0 ? this.DownloadThreadCount : Math.Min(Environment.ProcessorCount, MaxDefaultDownloadThreads); this.IndexThreadCount = this.IndexThreadCount > 0 ? this.IndexThreadCount : Environment.ProcessorCount; this.CheckoutThreadCount = this.CheckoutThreadCount > 0 ? this.CheckoutThreadCount : Environment.ProcessorCount; this.GitBinPath = !string.IsNullOrWhiteSpace(this.GitBinPath) ? this.GitBinPath : GitProcess.GetInstalledGitBinPath(); GitEnlistment enlistment = GitEnlistment.CreateFromCurrentDirectory(this.CacheServerUrl, this.GitBinPath); if (enlistment == null) { Console.WriteLine("Must be run within a git repo"); return(ExitFailure); } string commitish = this.Commit ?? this.Branch; if (string.IsNullOrWhiteSpace(commitish)) { GitProcess.Result result = new GitProcess(enlistment).GetCurrentBranchName(); if (result.HasErrors || string.IsNullOrWhiteSpace(result.Output)) { Console.WriteLine("Could not retrieve current branch name: " + result.Errors); return(ExitFailure); } commitish = result.Output.Trim(); } Guid parentActivityId = Guid.Empty; if (!string.IsNullOrWhiteSpace(this.ParentActivityId) && !Guid.TryParse(this.ParentActivityId, out parentActivityId)) { Console.WriteLine("The ParentActivityId provided (" + this.ParentActivityId + ") is not a valid GUID."); } using (JsonEtwTracer tracer = new JsonEtwTracer("Microsoft.Git.FastFetch", parentActivityId, "FastFetch")) { if (this.Verbose) { tracer.AddConsoleEventListener(EventLevel.Informational, Keywords.Any); } string fastfetchLogFile = Enlistment.GetNewLogFileName(enlistment.FastFetchLogRoot, "fastfetch"); tracer.AddLogFileEventListener(fastfetchLogFile, EventLevel.Informational, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, enlistment.CacheServerUrl, new EventMetadata { { "TargetCommitish", commitish }, { "Checkout", this.Checkout }, }); FetchHelper fetchHelper = this.GetFetchHelper(tracer, enlistment); fetchHelper.MaxRetries = this.MaxRetries; if (!FetchHelper.TryLoadPathWhitelist(tracer, this.PathWhitelist, this.PathWhitelistFile, enlistment, fetchHelper.PathWhitelist)) { return(ExitFailure); } bool isSuccess; try { Func <bool> doPrefetch = () => { try { bool isBranch = this.Commit == null; fetchHelper.FastFetch(commitish, isBranch); return(!fetchHelper.HasFailures); } catch (FetchHelper.FetchException e) { tracer.RelatedError(e.Message); return(false); } }; if (this.Verbose) { isSuccess = doPrefetch(); } else { isSuccess = ConsoleHelper.ShowStatusWhileRunning( doPrefetch, "Fetching", output: Console.Out, showSpinner: !Console.IsOutputRedirected); Console.WriteLine(); Console.WriteLine("FastFetch is complete. See the full logs at " + fastfetchLogFile); } isSuccess &= !fetchHelper.HasFailures; } catch (AggregateException e) { isSuccess = false; foreach (Exception ex in e.Flatten().InnerExceptions) { tracer.RelatedError(ex.ToString()); } } catch (Exception e) { isSuccess = false; tracer.RelatedError(e.ToString()); } EventMetadata stopMetadata = new EventMetadata(); stopMetadata.Add("Success", isSuccess); tracer.Stop(stopMetadata); return(isSuccess ? ExitSuccess : ExitFailure); } }
public ProcessStdoutReader(GitHelpers helper, GitProcess process) { this.helper = helper; this.process = process; }
public void InitRepo() { Directory.CreateDirectory(this.fastFetchRepoRoot); GitProcess.Invoke(this.fastFetchRepoRoot, "init"); GitProcess.Invoke(this.fastFetchRepoRoot, "remote add origin " + Settings.Default.RepoToClone); }
protected override void Execute(GVFSEnlistment enlistment) { string errorMessage = null; string mountExecutableLocation = null; using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "PreMount")) { PhysicalFileSystem fileSystem = new PhysicalFileSystem(); GitRepo gitRepo = new GitRepo(tracer, enlistment, fileSystem); GVFSContext context = new GVFSContext(tracer, fileSystem, gitRepo, enlistment); if (!HooksInstaller.InstallHooks(context, out errorMessage)) { this.ReportErrorAndExit("Error installing hooks: " + errorMessage); } CacheServerInfo cacheServer = this.ResolvedCacheServer ?? CacheServerResolver.GetCacheServerFromConfig(enlistment); tracer.AddLogFileEventListener( GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.MountVerb), EventLevel.Verbose, Keywords.Any); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, cacheServer.Url, new EventMetadata { { "Unattended", this.Unattended }, { "IsElevated", GVFSPlatform.Instance.IsElevated() }, { "NamedPipeName", enlistment.NamedPipeName }, { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter }, }); if (!GVFSPlatform.Instance.KernelDriver.IsReady(tracer, enlistment.EnlistmentRoot, out errorMessage)) { tracer.RelatedInfo($"{nameof(MountVerb)}.{nameof(this.Execute)}: Enabling and attaching ProjFS through service"); if (!this.ShowStatusWhileRunning( () => { return(this.TryEnableAndAttachPrjFltThroughService(enlistment.EnlistmentRoot, out errorMessage)); }, $"Attaching ProjFS to volume")) { this.ReportErrorAndExit(tracer, ReturnCode.FilterError, errorMessage); } } RetryConfig retryConfig = null; ServerGVFSConfig serverGVFSConfig = this.DownloadedGVFSConfig; if (!this.SkipVersionCheck) { string authErrorMessage; if (!this.TryAuthenticate(tracer, enlistment, out authErrorMessage)) { this.Output.WriteLine(" WARNING: " + authErrorMessage); this.Output.WriteLine(" Mount will proceed, but new files cannot be accessed until GVFS can authenticate."); } if (serverGVFSConfig == null) { if (retryConfig == null) { retryConfig = this.GetRetryConfig(tracer, enlistment); } serverGVFSConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig); } this.ValidateClientVersions(tracer, enlistment, serverGVFSConfig, showWarnings: true); CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServer.Url, serverGVFSConfig); this.Output.WriteLine("Configured cache server: " + cacheServer); } this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGVFSConfig, cacheServer); if (!this.ShowStatusWhileRunning( () => { return(this.PerformPreMountValidation(tracer, enlistment, out mountExecutableLocation, out errorMessage)); }, "Validating repo")) { this.ReportErrorAndExit(tracer, errorMessage); } if (!this.SkipVersionCheck) { string error; if (!RepoMetadata.TryInitialize(tracer, enlistment.DotGVFSRoot, out error)) { this.ReportErrorAndExit(tracer, error); } try { GitProcess git = new GitProcess(enlistment); this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment); } finally { RepoMetadata.Shutdown(); } } } if (!this.ShowStatusWhileRunning( () => { return(this.TryMount(enlistment, mountExecutableLocation, out errorMessage)); }, "Mounting")) { this.ReportErrorAndExit(errorMessage); } if (!this.Unattended && GVFSPlatform.Instance.SupportsGVFSService) { if (!this.ShowStatusWhileRunning( () => { return(this.RegisterMount(enlistment, out errorMessage)); }, "Registering for automount")) { this.Output.WriteLine(" WARNING: " + errorMessage); } } }
public void TruncatedObjectRedownloaded() { GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish); ProcessResult revParseResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded.txt"); string sha = revParseResult.Output.Trim(); sha.Length.ShouldEqual(40); string objectPath = Path.Combine(this.Enlistment.GetObjectRoot(this.fileSystem), sha.Substring(0, 2), sha.Substring(2, 38)); objectPath.ShouldNotExistOnDisk(this.fileSystem); string corruptObjectFolderPath = Path.Combine(this.Enlistment.DotGVFSRoot, "CorruptObjects"); int initialCorruptObjectCount = 0; if (this.fileSystem.DirectoryExists(corruptObjectFolderPath)) { initialCorruptObjectCount = new DirectoryInfo(corruptObjectFolderPath).EnumerateFileSystemInfos().Count(); } // Read a copy of TruncatedObjectRedownloaded.txt to force the object to be downloaded GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha); string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents(); objectPath.ShouldBeAFile(this.fileSystem); string modifedFile = "Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded.txt"; GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, modifedFile); // Truncate the contents of objectPath string tempTruncatedObjectPath = objectPath + "truncated"; FileInfo objectFileInfo = new FileInfo(objectPath); long objectLength = objectFileInfo.Length; using (FileStream objectStream = new FileStream(objectPath, FileMode.Open)) using (FileStream truncatedObjectStream = new FileStream(tempTruncatedObjectPath, FileMode.CreateNew)) { for (int i = 0; i < (objectStream.Length - 16); ++i) { truncatedObjectStream.WriteByte((byte)objectStream.ReadByte()); } } this.fileSystem.DeleteFile(objectPath); this.fileSystem.MoveFile(tempTruncatedObjectPath, objectPath); tempTruncatedObjectPath.ShouldNotExistOnDisk(this.fileSystem); objectPath.ShouldBeAFile(this.fileSystem); new FileInfo(objectPath).Length.ShouldEqual(objectLength - 16); if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // Mac can't correct corrupt objects, but it should detect them and add to ModifiedPaths.dat this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem); GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, modifedFile); GitHelpers.CheckGitCommandAgainstGVFSRepo( this.Enlistment.RepoRoot, "status", $"modified: {modifedFile}"); } else { // Windows should correct a corrupt obect // Read the original path and verify its contents are correct this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents); // Confirm there's a new item in the corrupt objects folder corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem).WithItems().Count().ShouldEqual(initialCorruptObjectCount + 1); // File should not be in ModifiedPaths.dat GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, "Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded.txt"); } }
protected override void Execute(GVFSEnlistment enlistment) { string diagnosticsRoot = Path.Combine(enlistment.DotGVFSRoot, "diagnostics"); if (!Directory.Exists(diagnosticsRoot)) { Directory.CreateDirectory(diagnosticsRoot); } string archiveFolderPath = Path.Combine(diagnosticsRoot, "gvfs_" + DateTime.Now.ToString("yyyyMMdd_HHmmss")); Directory.CreateDirectory(archiveFolderPath); using (FileStream diagnosticLogFile = new FileStream(Path.Combine(archiveFolderPath, "diagnostics.log"), FileMode.CreateNew)) using (this.diagnosticLogFileWriter = new StreamWriter(diagnosticLogFile)) { this.WriteMessage("Collecting diagnostic info into temp folder " + archiveFolderPath); this.WriteMessage(string.Empty); this.WriteMessage("gvfs version " + ProcessHelper.GetCurrentProcessVersion()); GitVersion gitVersion = null; string error = null; if (!string.IsNullOrEmpty(enlistment.GitBinPath) && GitProcess.TryGetVersion(enlistment.GitBinPath, out gitVersion, out error)) { this.WriteMessage("git version " + gitVersion.ToString()); } else { this.WriteMessage("Could not determine git version. " + error); } this.WriteMessage(enlistment.GitBinPath); this.WriteMessage(string.Empty); this.WriteMessage("Enlistment root: " + enlistment.EnlistmentRoot); this.WriteMessage("Cache Server: " + CacheServerResolver.GetCacheServerFromConfig(enlistment)); string localCacheRoot; string gitObjectsRoot; this.GetLocalCachePaths(enlistment, out localCacheRoot, out gitObjectsRoot); string actualLocalCacheRoot = !string.IsNullOrWhiteSpace(localCacheRoot) ? localCacheRoot : gitObjectsRoot; this.WriteMessage("Local Cache: " + actualLocalCacheRoot); this.WriteMessage(string.Empty); this.PrintDiskSpaceInfo(actualLocalCacheRoot, this.EnlistmentRootPathParameter); this.RecordVersionInformation(); this.ShowStatusWhileRunning( () => this.RunAndRecordGVFSVerb <StatusVerb>(archiveFolderPath, "gvfs_status.txt") != ReturnCode.Success || this.RunAndRecordGVFSVerb <UnmountVerb>(archiveFolderPath, "gvfs_unmount.txt", verb => verb.SkipLock = true) == ReturnCode.Success, "Unmounting", suppressGvfsLogMessage: true); this.ShowStatusWhileRunning( () => { // .gvfs this.CopyAllFiles(enlistment.EnlistmentRoot, archiveFolderPath, GVFSPlatform.Instance.Constants.DotGVFSRoot, copySubFolders: false); // driver if (this.FlushKernelDriverLogs()) { string kernelLogsFolderPath = GVFSPlatform.Instance.KernelDriver.LogsFolderPath; // This copy sometimes fails because the OS has an exclusive lock on the etl files. The error is not actionable // for the user so we don't write the error message to stdout, just to our own log file. this.CopyAllFiles(Path.GetDirectoryName(kernelLogsFolderPath), archiveFolderPath, Path.GetFileName(kernelLogsFolderPath), copySubFolders: false, hideErrorsFromStdout: true); } // .git this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Hooks.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Info.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Logs.Root, copySubFolders: true); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Refs.Root, copySubFolders: true); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Objects.Info.Root, copySubFolders: false); this.LogDirectoryEnumeration(enlistment.WorkingDirectoryRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGit.Objects.Root), GVFSConstants.DotGit.Objects.Pack.Root, "packs-local.txt"); this.LogLooseObjectCount(enlistment.WorkingDirectoryRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGit.Objects.Root), GVFSConstants.DotGit.Objects.Root, "objects-local.txt"); // databases this.CopyAllFiles(enlistment.DotGVFSRoot, Path.Combine(archiveFolderPath, GVFSPlatform.Instance.Constants.DotGVFSRoot), GVFSConstants.DotGVFS.Databases.Name, copySubFolders: false); // local cache this.CopyLocalCacheData(archiveFolderPath, localCacheRoot, gitObjectsRoot); // corrupt objects this.CopyAllFiles(enlistment.DotGVFSRoot, Path.Combine(archiveFolderPath, GVFSPlatform.Instance.Constants.DotGVFSRoot), GVFSConstants.DotGVFS.CorruptObjectsName, copySubFolders: false); // service this.CopyAllFiles( GVFSPlatform.Instance.GetDataRootForGVFS(), archiveFolderPath, this.ServiceName, copySubFolders: true); if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade) { // upgrader this.CopyAllFiles( ProductUpgraderInfo.GetParentLogDirectoryPath(), archiveFolderPath, DeprecatedUpgradeLogsDirectory, copySubFolders: true, targetFolderName: Path.Combine(ProductUpgraderInfo.UpgradeDirectoryName, DeprecatedUpgradeLogsDirectory)); this.CopyAllFiles( ProductUpgraderInfo.GetParentLogDirectoryPath(), archiveFolderPath, ProductUpgraderInfo.LogDirectory, copySubFolders: true, targetFolderName: Path.Combine(ProductUpgraderInfo.UpgradeDirectoryName, ProductUpgraderInfo.LogDirectory)); this.LogDirectoryEnumeration( ProductUpgraderInfo.GetUpgradeProtectedDataDirectory(), Path.Combine(archiveFolderPath, ProductUpgraderInfo.UpgradeDirectoryName), ProductUpgraderInfo.DownloadDirectory, "downloaded-assets.txt"); } if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSConfig) { this.CopyFile(GVFSPlatform.Instance.GetDataRootForGVFS(), archiveFolderPath, LocalGVFSConfig.FileName); } return(true); }, "Copying logs"); this.ShowStatusWhileRunning( () => this.RunAndRecordGVFSVerb <MountVerb>(archiveFolderPath, "gvfs_mount.txt") == ReturnCode.Success, "Mounting", suppressGvfsLogMessage: true); this.CopyAllFiles(enlistment.DotGVFSRoot, Path.Combine(archiveFolderPath, GVFSPlatform.Instance.Constants.DotGVFSRoot), "logs", copySubFolders: false); } string zipFilePath = archiveFolderPath + ".zip"; this.ShowStatusWhileRunning( () => { ZipFile.CreateFromDirectory(archiveFolderPath, zipFilePath); this.fileSystem.DeleteDirectory(archiveFolderPath); return(true); }, "Creating zip file", suppressGvfsLogMessage: true); this.Output.WriteLine(); this.Output.WriteLine("Diagnostics complete. All of the gathered info, as well as all of the output above, is captured in"); this.Output.WriteLine(zipFilePath); }
public static void StartGitk(string workingDirectory) { GitProcess.ExecGitk(workingDirectory, "--all").Dispose(); }
private int ExecuteWithExitCode() { // CmdParser doesn't strip quotes, and Path.Combine will throw this.GitBinPath = this.GitBinPath.Replace("\"", string.Empty); if (!GVFSPlatform.Instance.GitInstallation.GitExists(this.GitBinPath)) { Console.WriteLine( "Could not find git.exe {0}", !string.IsNullOrWhiteSpace(this.GitBinPath) ? "at " + this.GitBinPath : "on %PATH%"); return(ExitFailure); } if (this.Commit != null && this.Branch != null) { Console.WriteLine("Cannot specify both a commit sha and a branch name."); return(ExitFailure); } if (this.ForceCheckout && !this.Checkout) { Console.WriteLine("Cannot use --force-checkout option without --checkout option."); return(ExitFailure); } this.SearchThreadCount = this.SearchThreadCount > 0 ? this.SearchThreadCount : Environment.ProcessorCount; this.DownloadThreadCount = this.DownloadThreadCount > 0 ? this.DownloadThreadCount : Math.Min(Environment.ProcessorCount, MaxDefaultDownloadThreads); this.IndexThreadCount = this.IndexThreadCount > 0 ? this.IndexThreadCount : Environment.ProcessorCount; this.CheckoutThreadCount = this.CheckoutThreadCount > 0 ? this.CheckoutThreadCount : Environment.ProcessorCount; this.GitBinPath = !string.IsNullOrWhiteSpace(this.GitBinPath) ? this.GitBinPath : GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath(); GitEnlistment enlistment = GitEnlistment.CreateFromCurrentDirectory(this.GitBinPath); if (enlistment == null) { Console.WriteLine("Must be run within a git repo"); return(ExitFailure); } string commitish = this.Commit ?? this.Branch; if (string.IsNullOrWhiteSpace(commitish)) { GitProcess.Result result = new GitProcess(enlistment).GetCurrentBranchName(); if (result.HasErrors || string.IsNullOrWhiteSpace(result.Output)) { Console.WriteLine("Could not retrieve current branch name: " + result.Errors); return(ExitFailure); } commitish = result.Output.Trim(); } Guid parentActivityId = Guid.Empty; if (!string.IsNullOrWhiteSpace(this.ParentActivityId) && !Guid.TryParse(this.ParentActivityId, out parentActivityId)) { Console.WriteLine("The ParentActivityId provided (" + this.ParentActivityId + ") is not a valid GUID."); } using (JsonTracer tracer = new JsonTracer("Microsoft.Git.FastFetch", parentActivityId, "FastFetch", disableTelemetry: true)) { if (this.Verbose) { tracer.AddDiagnosticConsoleEventListener(EventLevel.Informational, Keywords.Any); } else { tracer.AddPrettyConsoleEventListener(EventLevel.Error, Keywords.Any); } string fastfetchLogFile = Enlistment.GetNewLogFileName(enlistment.FastFetchLogRoot, "fastfetch"); tracer.AddLogFileEventListener(fastfetchLogFile, EventLevel.Informational, Keywords.Any); CacheServerInfo cacheServer = new CacheServerInfo(this.GetRemoteUrl(enlistment), null); tracer.WriteStartEvent( enlistment.EnlistmentRoot, enlistment.RepoUrl, cacheServer.Url, new EventMetadata { { "TargetCommitish", commitish }, { "Checkout", this.Checkout }, }); RetryConfig retryConfig = new RetryConfig(this.MaxAttempts, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes)); BlobPrefetcher prefetcher = this.GetFolderPrefetcher(tracer, enlistment, cacheServer, retryConfig); string error; if (!BlobPrefetcher.TryLoadFolderList(enlistment, this.FolderList, this.FolderListFile, prefetcher.FolderList, out error)) { tracer.RelatedError(error); Console.WriteLine(error); return(ExitFailure); } bool isSuccess; try { Func <bool> doPrefetch = () => { try { bool isBranch = this.Commit == null; prefetcher.Prefetch(commitish, isBranch); return(!prefetcher.HasFailures); } catch (BlobPrefetcher.FetchException e) { tracer.RelatedError(e.Message); return(false); } }; if (this.Verbose) { isSuccess = doPrefetch(); } else { isSuccess = ConsoleHelper.ShowStatusWhileRunning( doPrefetch, "Fetching", output: Console.Out, showSpinner: !Console.IsOutputRedirected, gvfsLogEnlistmentRoot: null); Console.WriteLine(); Console.WriteLine("See the full log at " + fastfetchLogFile); } isSuccess &= !prefetcher.HasFailures; } catch (AggregateException e) { isSuccess = false; foreach (Exception ex in e.Flatten().InnerExceptions) { tracer.RelatedError(ex.ToString()); } } catch (Exception e) { isSuccess = false; tracer.RelatedError(e.ToString()); } EventMetadata stopMetadata = new EventMetadata(); stopMetadata.Add("Success", isSuccess); tracer.Stop(stopMetadata); return(isSuccess ? ExitSuccess : ExitFailure); } }
public void TryKillRunningProcess_NeverRan() { GitProcess process = new GitProcess(new MockGVFSEnlistment()); process.TryKillRunningProcess().ShouldBeTrue(); }
public void InitControlRepo() { Directory.CreateDirectory(this.fastFetchControlRoot); GitProcess.Invoke(this.fastFetchBaseRoot, "clone -b " + Settings.Default.Commitish + " " + GVFSTestConfig.RepoToClone + " " + this.fastFetchControlRoot); }
public void AddWhileInMergeConflict() { // Attempt to 'set' while you have a cherry-pick merge conflict. It will say it failed. // Abort the cherry-pick, switch branches, and switch back. Now the folders will be fully populated. // The correct behavior should be that if the 'set' fails the folders should not be added to sparse string conflictFilename = "conflict"; string fileToConflict = this.Enlistment.GetSourcePath(conflictFilename); // Make a new commit on a branch GitProcess.Invoke(this.Enlistment.RepoRoot, "checkout -b branch_with_conflict"); this.fileSystem.WriteAllText(fileToConflict, "ABC"); GitProcess.Invoke(this.Enlistment.RepoRoot, "add " + conflictFilename); string commitResult = GitProcess.Invoke(this.Enlistment.RepoRoot, "commit -m \"conflict on branch_with_conflict\""); // create a conflicting commit on the originating branch int startIndex = commitResult.IndexOf(' '); string commitId = commitResult.Substring(startIndex, commitResult.IndexOf(']') - startIndex); GitProcess.Invoke(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish); this.fileSystem.WriteAllText(fileToConflict, "DEF"); GitProcess.Invoke(this.Enlistment.RepoRoot, "add " + conflictFilename); GitProcess.Invoke(this.Enlistment.RepoRoot, "commit -m \"conflict on master\""); // Attempt to cherry-pick the commit we know will result in a merge conflict GitProcess.Invoke(this.Enlistment.RepoRoot, "cherry-pick " + commitId); // Should not be able to add which we have a merge conflict this.currentFolderList.Add(FolderTrailingSlashTests); string result = this.SparseSet(); result.ShouldContain(SetIndexStateMessage); // New directory should not be listed because of the error this.VerifyDirectory(this.Enlistment.RepoRoot, new List <string> { FolderDotGit, FolderEnumerateAndReadTestFiles, FolderFileNameEncoding, FolderGitCommandsTests, FolderGVFS, FolderScripts, FolderTest_MoveRenameFileTests, FolderTest_MoveRenameFileTests2 }); // Fix the error, switch to the new branch, and switch back and we should have all directories GitProcess.Invoke(this.Enlistment.RepoRoot, "cherry-pick --abort"); result = this.SparseSet(); result.ShouldNotContain(true, SetIndexStateMessage); GitProcess.Invoke(this.Enlistment.RepoRoot, "checkout branch_with_conflict"); GitProcess.Invoke(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish); // New directory should now exist this.VerifyDirectory(this.Enlistment.RepoRoot, new List <string> { FolderDotGit, FolderEnumerateAndReadTestFiles, FolderFileNameEncoding, FolderGitCommandsTests, FolderGVFS, FolderScripts, FolderTest_MoveRenameFileTests, FolderTest_MoveRenameFileTests2, FolderTrailingSlashTests }); }
protected override void Execute(GVFSEnlistment enlistment) { string diagnosticsRoot = Path.Combine(enlistment.DotGVFSRoot, "diagnostics"); if (!Directory.Exists(diagnosticsRoot)) { Directory.CreateDirectory(diagnosticsRoot); } string archiveFolderPath = Path.Combine(diagnosticsRoot, "gvfs_" + DateTime.Now.ToString("yyyyMMdd_HHmmss")); Directory.CreateDirectory(archiveFolderPath); using (FileStream diagnosticLogFile = new FileStream(Path.Combine(archiveFolderPath, "diagnostics.log"), FileMode.CreateNew)) using (this.diagnosticLogFileWriter = new StreamWriter(diagnosticLogFile)) { this.WriteMessage("Collecting diagnostic info into temp folder " + archiveFolderPath); this.WriteMessage(string.Empty); this.WriteMessage("gvfs version " + ProcessHelper.GetCurrentProcessVersion()); this.WriteMessage(GitProcess.Version(enlistment).Output); this.WriteMessage(GitProcess.GetInstalledGitBinPath()); this.WriteMessage(string.Empty); this.WriteMessage("Enlistment root: " + enlistment.EnlistmentRoot); this.WriteMessage("Cache Server: " + CacheServerResolver.GetUrlFromConfig(enlistment)); string localCacheRoot; string gitObjectsRoot; this.GetLocalCachePaths(enlistment, out localCacheRoot, out gitObjectsRoot); string actualLocalCacheRoot = !string.IsNullOrWhiteSpace(localCacheRoot) ? localCacheRoot : gitObjectsRoot; this.WriteMessage("Local Cache: " + actualLocalCacheRoot); this.WriteMessage(string.Empty); this.PrintDiskSpaceInfo(actualLocalCacheRoot, this.EnlistmentRootPath); this.RecordWindowsVersionInformation(); this.ShowStatusWhileRunning( () => this.RunAndRecordGVFSVerb <StatusVerb>(archiveFolderPath, "gvfs_status.txt") != ReturnCode.Success || this.RunAndRecordGVFSVerb <UnmountVerb>(archiveFolderPath, "gvfs_unmount.txt", verb => verb.SkipLock = true) == ReturnCode.Success, "Unmounting", suppressGvfsLogMessage: true); this.ShowStatusWhileRunning( () => { // .gvfs this.CopyAllFiles(enlistment.EnlistmentRoot, archiveFolderPath, GVFSConstants.DotGVFS.Root, copySubFolders: false); // filter this.FlushFilterLogBuffers(); string system32LogFilesPath = Environment.ExpandEnvironmentVariables(System32LogFilesRoot); // This copy sometimes fails because the OS has an exclusive lock on the etl files. The error is not actionable // for the user so we don't write the error message to stdout, just to our own log file. this.CopyAllFiles(system32LogFilesPath, archiveFolderPath, FilterLogFolderName, copySubFolders: false, hideErrorsFromStdout: true); // .git this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Hooks.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Info.Root, copySubFolders: false); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Logs.Root, copySubFolders: true); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Refs.Root, copySubFolders: true); this.CopyAllFiles(enlistment.WorkingDirectoryRoot, archiveFolderPath, GVFSConstants.DotGit.Objects.Info.Root, copySubFolders: false); this.LogDirectoryEnumeration(enlistment.WorkingDirectoryRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGit.Objects.Root), GVFSConstants.DotGit.Objects.Pack.Root, "packs-local.txt"); this.LogLooseObjectCount(enlistment.WorkingDirectoryRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGit.Objects.Root), GVFSConstants.DotGit.Objects.Root, "objects-local.txt"); // databases this.CopyAllFiles(enlistment.DotGVFSRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGVFS.Root), GVFSConstants.DotGVFS.Databases.Name, copySubFolders: false); // local cache this.CopyLocalCacheData(archiveFolderPath, localCacheRoot, gitObjectsRoot); // corrupt objects this.CopyAllFiles(enlistment.DotGVFSRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGVFS.Root), GVFSConstants.DotGVFS.CorruptObjectsName, copySubFolders: false); // service this.CopyAllFiles( Paths.GetServiceDataRoot(string.Empty), archiveFolderPath, this.ServiceName, copySubFolders: true); return(true); }, "Copying logs"); this.ShowStatusWhileRunning( () => this.RunAndRecordGVFSVerb <MountVerb>(archiveFolderPath, "gvfs_mount.txt") == ReturnCode.Success, "Mounting", suppressGvfsLogMessage: true); this.CopyAllFiles(enlistment.DotGVFSRoot, Path.Combine(archiveFolderPath, GVFSConstants.DotGVFS.Root), "logs", copySubFolders: false); } string zipFilePath = archiveFolderPath + ".zip"; this.ShowStatusWhileRunning( () => { ZipFile.CreateFromDirectory(archiveFolderPath, zipFilePath); PhysicalFileSystem.RecursiveDelete(archiveFolderPath); return(true); }, "Creating zip file", suppressGvfsLogMessage: true); this.Output.WriteLine(); this.Output.WriteLine("Diagnostics complete. All of the gathered info, as well as all of the output above, is captured in"); this.Output.WriteLine(zipFilePath); }
private void Close(GitProcess process) { // if caller doesn't read entire stdout to the EOF - it is possible that // child process will hang waiting until there will be free space in stdout // buffer to write the rest of the output. // See https://github.com/git-tfs/git-tfs/issues/121 for details. if (process.StartInfo.RedirectStandardOutput) { process.StandardOutput.BaseStream.CopyTo(Stream.Null); process.StandardOutput.Close(); } if (!process.WaitForExit((int)TimeSpan.FromSeconds(10).TotalMilliseconds)) throw new GitCommandException("Command did not terminate.", process); if(process.ExitCode != 0) throw new GitCommandException(string.Format("Command exited with error code: {0}\n{1}", process.ExitCode, process.StandardErrorString), process); }
public void LockToPreventUpdateAndDelete() { string testFileUpdate1Contents = "Commit2LockToPreventUpdateAndDelete \r\n"; string testFileUpdate2Contents = "Commit2LockToPreventUpdateAndDelete2 \r\n"; string testFileUpdate3Contents = "Commit2LockToPreventUpdateAndDelete3 \r\n"; string testFileDelete1Contents = "PreventDelete \r\n"; string testFileDelete2Contents = "PreventDelete2 \r\n"; string testFileDelete3Contents = "PreventDelete3 \r\n"; string testFileUpdate1OldContents = "TestFileLockToPreventUpdateAndDelete \r\n"; string testFileUpdate2OldContents = "TestFileLockToPreventUpdateAndDelete2 \r\n"; string testFileUpdate3OldContents = "TestFileLockToPreventUpdateAndDelete3 \r\n"; string testFileUpdate1Name = "test.txt"; string testFileUpdate2Name = "test2.txt"; string testFileUpdate3Name = "test3.txt"; string testFileDelete1Name = "test_delete.txt"; string testFileDelete2Name = "test_delete2.txt"; string testFileDelete3Name = "test_delete3.txt"; string testFileUpdate1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate1Name)); string testFileUpdate2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate2Name)); string testFileUpdate3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate3Name)); string testFileDelete1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete1Name)); string testFileDelete2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete2Name)); string testFileDelete3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete3Name)); testFileUpdate1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate1Contents); testFileUpdate2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate2Contents); testFileUpdate3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate3Contents); testFileDelete1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete1Contents); testFileDelete2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete2Contents); testFileDelete3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete3Contents); using (SafeFileHandle testFileUpdate1Handle = this.CreateFile(testFileUpdate1Path, FileShare.Read)) using (SafeFileHandle testFileUpdate2Handle = this.CreateFile(testFileUpdate2Path, FileShare.Read)) using (SafeFileHandle testFileUpdate3Handle = this.CreateFile(testFileUpdate3Path, FileShare.Read)) using (SafeFileHandle testFileDelete1Handle = this.CreateFile(testFileDelete1Path, FileShare.Read)) using (SafeFileHandle testFileDelete2Handle = this.CreateFile(testFileDelete2Path, FileShare.Read)) using (SafeFileHandle testFileDelete3Handle = this.CreateFile(testFileDelete3Path, FileShare.Read)) { testFileUpdate1Handle.IsInvalid.ShouldEqual(false); testFileUpdate2Handle.IsInvalid.ShouldEqual(false); testFileUpdate3Handle.IsInvalid.ShouldEqual(false); testFileDelete1Handle.IsInvalid.ShouldEqual(false); testFileDelete2Handle.IsInvalid.ShouldEqual(false); testFileDelete3Handle.IsInvalid.ShouldEqual(false); ProcessResult checkoutResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + OldCommitId); checkoutResult.Errors.ShouldContain( "HEAD is now at " + OldCommitId + @"... Add test files for update UpdatePlaceholder tests", "GVFS was unable to delete the following files. To recover, close all handles to the files and run these commands:", "git clean -f " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete1Name, "git clean -f " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete2Name, "git clean -f " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete3Name, "GVFS was unable to update the following files. To recover, close all handles to the files and run these commands:", "git checkout -- " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate1Name, "git checkout -- " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate2Name, "git checkout -- " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate3Name); GitHelpers.CheckGitCommandAgainstGVFSRepo( this.Enlistment.RepoRoot, "status", "HEAD detached at " + OldCommitId, "modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test.txt", "modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test2.txt", "modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test3.txt", "Untracked files:\n (use \"git add <file>...\" to include in what will be committed)\n\n\tTest_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test_delete.txt\n\tTest_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test_delete2.txt\n\tTest_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test_delete3.txt", "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"); } this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate1Name); this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate2Name); this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate3Name); this.GitCleanFile(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete1Name); this.GitCleanFile(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete2Name); this.GitCleanFile(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete3Name); this.GitStatusShouldBeClean(OldCommitId); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate1Name); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate2Name); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate3Name); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete1Name); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete2Name); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete3Name); testFileUpdate1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate1OldContents); testFileUpdate2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate2OldContents); testFileUpdate3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate3OldContents); testFileDelete1Path.ShouldNotExistOnDisk(this.fileSystem); testFileDelete2Path.ShouldNotExistOnDisk(this.fileSystem); testFileDelete3Path.ShouldNotExistOnDisk(this.fileSystem); this.GitCheckoutCommitId(NewFilesAndChangesCommitId); this.GitStatusShouldBeClean(NewFilesAndChangesCommitId); testFileUpdate1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate1Contents); testFileUpdate2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate2Contents); testFileUpdate3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate3Contents); testFileDelete1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete1Contents); testFileDelete2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete2Contents); testFileDelete3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete3Contents); }
public ProcessStdoutReader(GitHelpers helper, GitProcess process) { this.helper = helper; this.process = process; }
public void LockWithFullShareUpdateAndDelete() { string testFileUpdate4Contents = "Commit2LockToPreventUpdateAndDelete4 \r\n"; string testFileDelete4Contents = "PreventDelete4 \r\n"; string testFileUpdate4OldContents = "TestFileLockToPreventUpdateAndDelete4 \r\n"; string testFileUpdate4Name = "test4.txt"; string testFileDelete4Name = "test_delete4.txt"; string testFileUpdate4Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate4Name)); string testFileDelete4Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete4Name)); testFileUpdate4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate4Contents); testFileDelete4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete4Contents); if (this.CanUpdateAndDeletePlaceholdersWithOpenHandles()) { using (SafeFileHandle testFileUpdate4Handle = this.CreateFile(testFileUpdate4Path, FileShare.Read | FileShare.Delete)) using (SafeFileHandle testFileDelete4Handle = this.CreateFile(testFileDelete4Path, FileShare.Read | FileShare.Delete)) { testFileUpdate4Handle.IsInvalid.ShouldEqual(false); testFileDelete4Handle.IsInvalid.ShouldEqual(false); this.GitCheckoutCommitId(OldCommitId); this.GitStatusShouldBeClean(OldCommitId); } } else { using (SafeFileHandle testFileUpdate4Handle = this.CreateFile(testFileUpdate4Path, FileShare.Read | FileShare.Delete)) using (SafeFileHandle testFileDelete4Handle = this.CreateFile(testFileDelete4Path, FileShare.Read | FileShare.Delete)) { testFileUpdate4Handle.IsInvalid.ShouldEqual(false); testFileDelete4Handle.IsInvalid.ShouldEqual(false); ProcessResult checkoutResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + OldCommitId); checkoutResult.Errors.ShouldContain( "HEAD is now at " + OldCommitId + @"... Add test files for update UpdatePlaceholder tests", "GVFS was unable to update the following files. To recover, close all handles to the files and run these commands:", "git checkout -- Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test4.txt"); GitHelpers.CheckGitCommandAgainstGVFSRepo( this.Enlistment.RepoRoot, "status", "HEAD detached at " + OldCommitId, "modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test4.txt", "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"); } this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate4Name); this.GitStatusShouldBeClean(OldCommitId); this.SparseCheckoutShouldContain(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate4Name); } testFileUpdate4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate4OldContents); testFileDelete4Path.ShouldNotExistOnDisk(this.fileSystem); this.GitCheckoutCommitId(NewFilesAndChangesCommitId); this.GitStatusShouldBeClean(NewFilesAndChangesCommitId); testFileUpdate4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate4Contents); testFileDelete4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete4Contents); }
private bool SetConfigSettings(GitProcess git) { return(this.enlistment.TrySetCacheServerUrlConfig() && GVFSVerb.TrySetGitConfigSettings(git)); }
public GitCommandService(GitBaseConfig config) { gitConfig = config; Process = gitConfig.CreateGitProcess(); }
private void PrefetchBlobs(ITracer tracer, GVFSEnlistment enlistment, GitObjectsHttpRequestor blobRequestor, CacheServerInfo cacheServer) { FetchHelper fetchHelper = new FetchHelper( tracer, enlistment, blobRequestor, ChunkSize, SearchThreadCount, DownloadThreadCount, IndexThreadCount); string error; if (!FetchHelper.TryLoadFolderList(enlistment, this.Folders, this.FoldersListFile, fetchHelper.FolderList, out error)) { this.ReportErrorAndExit(tracer, error); } if (!FetchHelper.TryLoadFileList(enlistment, this.Files, fetchHelper.FileList, out error)) { this.ReportErrorAndExit(tracer, error); } if (fetchHelper.FolderList.Count == 0 && fetchHelper.FileList.Count == 0) { this.ReportErrorAndExit(tracer, "Did you mean to fetch all blobs? If so, specify `--files *` to confirm."); } if (this.HydrateFiles) { if (!ConsoleHelper.ShowStatusWhileRunning( () => this.Execute <StatusVerb>( this.EnlistmentRootPath, verb => verb.Output = new StreamWriter(new MemoryStream())) == ReturnCode.Success, "Checking that GVFS is mounted", this.Output, showSpinner: true, gvfsLogEnlistmentRoot: null)) { this.ReportErrorAndExit("You can only specify --hydrate if the repo is mounted. Run 'gvfs mount' and try again."); } } GitProcess gitProcess = new GitProcess(enlistment); GitProcess.Result result = gitProcess.RevParse(GVFSConstants.DotGit.HeadName); if (result.HasErrors) { tracer.RelatedError(result.Errors); this.Output.WriteLine(result.Errors); Environment.ExitCode = (int)ReturnCode.GenericError; return; } int matchedBlobCount = 0; int downloadedBlobCount = 0; int readFileCount = 0; string headCommitId = result.Output; Func <bool> doPrefetch = () => { try { fetchHelper.FastFetchWithStats( headCommitId.Trim(), isBranch: false, readFilesAfterDownload: this.HydrateFiles, matchedBlobCount: out matchedBlobCount, downloadedBlobCount: out downloadedBlobCount, readFileCount: out readFileCount); return(!fetchHelper.HasFailures); } catch (FetchHelper.FetchException e) { tracer.RelatedError(e.Message); return(false); } }; if (this.Verbose) { doPrefetch(); } else { string message = this.HydrateFiles ? "Fetching blobs and hydrating files " : "Fetching blobs "; this.ShowStatusWhileRunning(doPrefetch, message + this.GetCacheServerDisplay(cacheServer)); } if (fetchHelper.HasFailures) { Environment.ExitCode = 1; } else { Console.WriteLine(); Console.WriteLine("Stats:"); Console.WriteLine(" Matched blobs: " + matchedBlobCount); Console.WriteLine(" Already cached: " + (matchedBlobCount - downloadedBlobCount)); Console.WriteLine(" Downloaded: " + downloadedBlobCount); if (this.HydrateFiles) { Console.WriteLine(" Hydrated files: " + readFileCount); } } }
private void PostFetchJob(List <string> packIndexes) { try { using (FileBasedLock postFetchFileLock = GVFSPlatform.Instance.CreateFileBasedLock( this.context.FileSystem, this.context.Tracer, Path.Combine(this.context.Enlistment.GitObjectsRoot, PostFetchLock))) { if (!postFetchFileLock.TryAcquireLock()) { this.context.Tracer.RelatedInfo(PostFetchTelemetryKey + ": Skipping post-fetch work since another process holds the lock"); return; } if (!this.gitObjects.TryWriteMultiPackIndex(this.context.Tracer, this.context.Enlistment, this.context.FileSystem)) { this.context.Tracer.RelatedWarning( metadata: null, message: PostFetchTelemetryKey + ": Failed to generate midx for new packfiles", keywords: Keywords.Telemetry); } if (packIndexes == null || packIndexes.Count == 0) { this.context.Tracer.RelatedInfo(PostFetchTelemetryKey + ": Skipping commit-graph write due to no new packfiles"); return; } using (ITracer activity = this.context.Tracer.StartActivity("TryWriteGitCommitGraph", EventLevel.Informational, Keywords.Telemetry, metadata: null)) { GitProcess process = new GitProcess(this.context.Enlistment); GitProcess.Result result = process.WriteCommitGraph(this.context.Enlistment.GitObjectsRoot, packIndexes); if (result.HasErrors) { this.context.Tracer.RelatedWarning( metadata: null, message: PostFetchTelemetryKey + ": Failed to generate commit-graph for new packfiles:" + result.Errors, keywords: Keywords.Telemetry); return; } } } } catch (ThreadAbortException) { this.context.Tracer.RelatedInfo("Aborting post-fetch job due to ThreadAbortException"); } catch (IOException e) { this.context.Tracer.RelatedWarning( metadata: this.CreateEventMetadata(null, e), message: PostFetchTelemetryKey + ": IOException while running post-fetch job: " + e.Message, keywords: Keywords.Telemetry); } catch (Exception e) { this.context.Tracer.RelatedError( metadata: this.CreateEventMetadata(null, e), message: PostFetchTelemetryKey + ": Exception while running post-fetch job: " + e.Message, keywords: Keywords.Telemetry); Environment.Exit((int)ReturnCode.GenericError); } }
/// <summary> /// Rebuild the status cache. This will run the background status to /// generate status results, and update the serialized status cache /// file. /// </summary> private bool TryRebuildStatusCache() { this.context.FileSystem.CreateDirectory(this.context.Enlistment.GitStatusCacheFolder); // The status cache is regenerated on mount. This means that even if the write to temp file // and rename operation doesn't complete (due to a system crash), and there is a torn write, // GVFS is still protected because a new status cache file will be generated on mount. string tmpStatusFilePath = Path.Combine(this.context.Enlistment.GitStatusCacheFolder, Path.GetRandomFileName() + "_status.tmp"); GitProcess.Result statusResult = null; // Do not modify this block unless you completely understand the comments and code within { // We MUST set the state to Rebuilding _immediately before_ we call the `git status` command. That allows us to // check afterwards if anything happened during the status command that should invalidate the cache, and we // can discard its results if that happens. this.cacheState = CacheState.Rebuilding; GitProcess git = this.context.Enlistment.CreateGitProcess(); statusResult = git.SerializeStatus( allowObjectDownloads: true, serializePath: tmpStatusFilePath); } bool rebuildSucceeded = false; if (!statusResult.HasErrors) { lock (this.cacheFileLock) { // Only update the cache if our state is still Rebuilding. Otherwise, this indicates that another call // to Invalidate came in, and moved the state back to Dirty. if (this.cacheState == CacheState.Rebuilding) { rebuildSucceeded = this.MoveCacheFileToFinalLocation(tmpStatusFilePath); if (rebuildSucceeded) { // We have to check the state once again, because it could have been invalidated while we were // copying the file in the previous step. Here we do it as a CompareExchange to minimize any further races. if (Interlocked.CompareExchange(ref this.cacheState, CacheState.Clean, CacheState.Rebuilding) != CacheState.Rebuilding) { // We did not succeed in setting the state to Clean. Note that we have already overwritten the on disk cache, // but all users of the cache file first check the cacheState, and since the cacheState is not Clean, no one // should ever read it. rebuildSucceeded = false; } } if (!rebuildSucceeded) { this.cacheState = CacheState.Dirty; } } } if (!rebuildSucceeded) { try { this.context.FileSystem.DeleteFile(tmpStatusFilePath); } catch (Exception ex) when(ex is IOException || ex is UnauthorizedAccessException) { EventMetadata metadata = new EventMetadata(); metadata.Add("Area", EtwArea); metadata.Add("Exception", ex.ToString()); this.context.Tracer.RelatedError( metadata, string.Format("GitStatusCache is unable to delete temporary status cache file at {0}.", tmpStatusFilePath)); } } } else { this.statistics.RecordBackgroundStatusScanError(); this.context.Tracer.RelatedInfo("GitStatusCache.TryRebuildStatusCache: Error generating status: {0}", statusResult.Errors); } return(rebuildSucceeded); }
protected override void Execute(GVFSEnlistment enlistment, ITracer tracer = null) { this.CheckGitVersion(enlistment); this.CheckAntiVirusExclusion(enlistment); string mountExeLocation = Path.Combine(ProcessHelper.GetCurrentProcessLocation(), MountExeName); if (!File.Exists(mountExeLocation)) { this.ReportErrorAndExit("Could not find GVFS.Mount.exe. You may need to reinstall GVFS."); } // This tracer is only needed for the HttpGitObjects so we can check the GVFS version. // If we keep it around longer, it will collide with the background process tracer. using (ITracer mountTracer = tracer ?? new JsonEtwTracer(GVFSConstants.GVFSEtwProviderName, "Mount")) { HttpGitObjects gitObjects = new HttpGitObjects(mountTracer, enlistment, maxConnections: 1); this.ValidateGVFSVersion(enlistment, gitObjects, mountTracer); } // We have to parse these parameters here to make sure they are valid before // handing them to the background process which cannot tell the user when they are bad EventLevel verbosity; Keywords keywords; this.ParseEnumArgs(out verbosity, out keywords); GitProcess git = new GitProcess(enlistment); if (!git.IsValidRepo()) { this.ReportErrorAndExit("The physical git repo is missing or invalid"); } this.SetGitConfigSettings(git); const string ParamPrefix = "--"; ProcessHelper.StartBackgroundProcess( mountExeLocation, string.Join( " ", enlistment.EnlistmentRoot, ParamPrefix + MountParameters.Verbosity, this.Verbosity, ParamPrefix + MountParameters.Keywords, this.KeywordsCsv, this.ShowDebugWindow ? ParamPrefix + MountParameters.DebugWindow : string.Empty), createWindow: this.ShowDebugWindow); this.Output.WriteLine("Waiting for GVFS to mount"); using (NamedPipeClient pipeClient = new NamedPipeClient(enlistment.NamedPipeName)) { if (!pipeClient.Connect(BackgroundProcessConnectTimeoutMS)) { this.ReportErrorAndExit("Unable to mount because the background process is not responding."); } bool isMounted = false; int tryCount = 0; while (!isMounted) { try { pipeClient.SendRequest(NamedPipeMessages.GetStatus.Request); NamedPipeMessages.GetStatus.Response getStatusResponse = NamedPipeMessages.GetStatus.Response.FromJson(pipeClient.ReadRawResponse()); if (getStatusResponse.MountStatus == NamedPipeMessages.GetStatus.Ready) { this.Output.WriteLine("Virtual repo is ready."); isMounted = true; } else if (getStatusResponse.MountStatus == NamedPipeMessages.GetStatus.MountFailed) { this.ReportErrorAndExit("Failed to mount, run 'gvfs log' for details"); } else { if (tryCount % 10 == 0) { this.Output.WriteLine(getStatusResponse.MountStatus + "..."); } Thread.Sleep(500); tryCount++; } } catch (BrokenPipeException) { this.ReportErrorAndExit("Failed to mount, run 'gvfs log' for details"); } } } }
public static void StartGitGui(string workingDirectory) { GitProcess.ExecNormal(new GitInput(workingDirectory, new Command("gui"))).Dispose(); }
private Result CreateClone( ITracer tracer, GSDEnlistment enlistment, GitObjectsHttpRequestor objectRequestor, GitRefs refs, string branch) { Result initRepoResult = this.TryInitRepo(tracer, refs, enlistment); if (!initRepoResult.Success) { return(initRepoResult); } PhysicalFileSystem fileSystem = new PhysicalFileSystem(); string errorMessage; if (!this.TryCreateAlternatesFile(fileSystem, enlistment, out errorMessage)) { return(new Result("Error configuring alternate: " + errorMessage)); } GitRepo gitRepo = new GitRepo(tracer, enlistment, fileSystem); GSDContext context = new GSDContext(tracer, fileSystem, gitRepo, enlistment); GSDGitObjects gitObjects = new GSDGitObjects(context, objectRequestor); if (!this.TryDownloadCommit( refs.GetTipCommitId(branch), enlistment, objectRequestor, gitObjects, gitRepo, out errorMessage)) { return(new Result(errorMessage)); } if (!GSDVerb.TrySetRequiredGitConfigSettings(enlistment) || !GSDVerb.TrySetOptionalGitConfigSettings(enlistment)) { return(new Result("Unable to configure git repo")); } CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment); if (!cacheServerResolver.TrySaveUrlToLocalConfig(objectRequestor.CacheServer, out errorMessage)) { return(new Result("Unable to configure cache server: " + errorMessage)); } GitProcess git = new GitProcess(enlistment); string originBranchName = "origin/" + branch; GitProcess.Result createBranchResult = git.CreateBranchWithUpstream(branch, originBranchName); if (createBranchResult.ExitCodeIsFailure) { return(new Result("Unable to create branch '" + originBranchName + "': " + createBranchResult.Errors + "\r\n" + createBranchResult.Output)); } File.WriteAllText( Path.Combine(enlistment.WorkingDirectoryBackingRoot, GSDConstants.DotGit.Head), "ref: refs/heads/" + branch); if (!this.TryDownloadRootGitAttributes(enlistment, gitObjects, gitRepo, out errorMessage)) { return(new Result(errorMessage)); } this.CreateGitScript(enlistment); string installHooksError; if (!HooksInstaller.InstallHooks(context, out installHooksError)) { tracer.RelatedError(installHooksError); return(new Result(installHooksError)); } // TODO: Move this to be after the mount? GitProcess.Result forceCheckoutResult = git.ForceCheckout(branch); if (forceCheckoutResult.ExitCodeIsFailure && forceCheckoutResult.Errors.IndexOf("unable to read tree") > 0) { // It is possible to have the above TryDownloadCommit() fail because we // already have the commit and root tree we intend to check out, but // don't have a tree further down the working directory. If we fail // checkout here, its' because we don't have these trees and the // read-object hook is not available yet. Force downloading the commit // again and retry the checkout. if (!this.TryDownloadCommit( refs.GetTipCommitId(branch), enlistment, objectRequestor, gitObjects, gitRepo, out errorMessage, checkLocalObjectCache: false)) { return(new Result(errorMessage)); } forceCheckoutResult = git.ForceCheckout(branch); } if (!RepoMetadata.TryInitialize(tracer, enlistment.DotGSDRoot, out errorMessage)) { tracer.RelatedError(errorMessage); return(new Result(errorMessage)); } try { RepoMetadata.Instance.SaveCloneMetadata(tracer, enlistment); this.LogEnlistmentInfoAndSetConfigValues(tracer, git, enlistment); } catch (Exception e) { tracer.RelatedError(e.ToString()); return(new Result(e.Message)); } finally { RepoMetadata.Shutdown(); } return(new Result(true)); }
public static void StartBash(string workingDirectory) { GitProcess.ExecSh(workingDirectory, "--login -i").Dispose(); }
public ProcessStdoutReader(GitHelpers helper, GitProcess process) { _helper = helper; _process = process; }