Пример #1
0
        private static MacScalarService CreateMacService(JsonTracer tracer, string[] args)
        {
            string serviceName = args.FirstOrDefault(arg => arg.StartsWith(MacScalarService.ServiceNameArgPrefix, StringComparison.OrdinalIgnoreCase));

            if (serviceName != null)
            {
                serviceName = serviceName.Substring(MacScalarService.ServiceNameArgPrefix.Length);
            }
            else
            {
                serviceName = ScalarConstants.Service.ServiceName;
            }

            ScalarPlatform scalarPlatform = ScalarPlatform.Instance;

            string logFilePath = ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(serviceName);

            Directory.CreateDirectory(logFilePath);

            tracer.AddLogFileEventListener(
                ScalarEnlistment.GetNewScalarLogFileName(logFilePath, ScalarConstants.LogFileTypes.Service),
                EventLevel.Informational,
                Keywords.Any);

            string             repoRegistryLocation = scalarPlatform.GetCommonAppDataRootForScalarComponent(ScalarConstants.RepoRegistry.RegistryDirectoryName);
            ScalarRepoRegistry repoRegistry         = new ScalarRepoRegistry(
                tracer,
                new PhysicalFileSystem(),
                repoRegistryLocation);

            return(new MacScalarService(tracer, serviceName, repoRegistry));
        }
Пример #2
0
        private bool TryInitializeUpgrader()
        {
            OperatingSystem os_info = Environment.OSVersion;

            if (os_info.Platform == PlatformID.Win32NT)
            {
                if (this.upgrader == null)
                {
                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgrader.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);
                    this.upgrader      = new ProductUpgrader(ProcessHelper.GetCurrentProcessVersion(), this.tracer);
                }

                return(true);
            }
            else
            {
                this.ReportInfoToConsole($"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} in only supported on Microsoft Windows Operating System.");
                return(false);
            }
        }
Пример #3
0
        private bool TryInitializeUpgrader()
        {
            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                if (this.upgrader == null)
                {
                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgrader.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);
                    this.upgrader      = new ProductUpgrader(ProcessHelper.GetCurrentProcessVersion(), this.tracer);
                }

                return(true);
            }
            else
            {
                this.ReportInfoToConsole($"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.");
                return(false);
            }
        }
Пример #4
0
        private static GVFSService CreateService(JsonTracer tracer, string[] args)
        {
            string serviceName = args.FirstOrDefault(arg => arg.StartsWith(GVFSService.ServiceNameArgPrefix, StringComparison.OrdinalIgnoreCase));

            if (serviceName != null)
            {
                serviceName = serviceName.Substring(GVFSService.ServiceNameArgPrefix.Length);
            }
            else
            {
                serviceName = GVFSConstants.Service.ServiceName;
            }

            GVFSPlatform gvfsPlatform = GVFSPlatform.Instance;

            string logFilePath = Path.Combine(
                gvfsPlatform.GetDataRootForGVFSComponent(serviceName),
                GVFSConstants.Service.LogDirectory);

            Directory.CreateDirectory(logFilePath);

            tracer.AddLogFileEventListener(
                GVFSEnlistment.GetNewGVFSLogFileName(logFilePath, GVFSConstants.LogFileTypes.Service),
                EventLevel.Informational,
                Keywords.Any);

            string       serviceDataLocation = gvfsPlatform.GetDataRootForGVFSComponent(serviceName);
            RepoRegistry repoRegistry        = new RepoRegistry(
                tracer,
                new PhysicalFileSystem(),
                serviceDataLocation,
                new GVFSMountProcess(tracer));

            return(new GVFSService(tracer, serviceName, repoRegistry));
        }
Пример #5
0
        private bool TryInitializeUpgrader(out string error)
        {
            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                error = null;
                if (this.upgrader == null)
                {
                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);

                    IProductUpgrader upgrader;
                    if (ProductUpgraderFactory.TryCreateUpgrader(out upgrader, this.tracer, out error))
                    {
                        this.upgrader = upgrader;
                    }
                    else
                    {
                        error = $"ERROR: {error}";
                    }
                }

                return(this.upgrader != null);
            }
            else
            {
                error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
                return(false);
            }
        }
Пример #6
0
        public static void Main(string[] args)
        {
            ScalarPlatformLoader.Initialize();

            using (JsonTracer tracer = new JsonTracer("Microsoft.Git.GVFS.Service.UI", "Service.UI"))
            {
                string error;
                string serviceUILogDirectory = ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(ScalarConstants.Service.UIName);
                if (!ScalarPlatform.Instance.FileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(serviceUILogDirectory, out error))
                {
                    EventMetadata metadata = new EventMetadata();
                    metadata.Add(nameof(serviceUILogDirectory), serviceUILogDirectory);
                    metadata.Add(nameof(error), error);
                    tracer.RelatedWarning(
                        metadata,
                        "Failed to create service UI logs directory",
                        Keywords.Telemetry);
                }
                else
                {
                    string logFilePath = ScalarEnlistment.GetNewScalarLogFileName(
                        serviceUILogDirectory,
                        ScalarConstants.LogFileTypes.ServiceUI,
                        logId: Environment.UserName);

                    tracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);
                }

                WinToastNotifier          winToastNotifier    = new WinToastNotifier(tracer);
                ScalarToastRequestHandler toastRequestHandler = new ScalarToastRequestHandler(winToastNotifier, tracer);
                GVFSServiceUI             process             = new GVFSServiceUI(tracer, toastRequestHandler);

                process.Start(args);
            }
        }
Пример #7
0
        private JsonTracer CreateTracer(GVFSEnlistment enlistment, EventLevel verbosity, Keywords keywords)
        {
            string enlistmentId = null;
            string mountId      = null;

            GitProcess git = new GitProcess(enlistment);

            GitProcess.ConfigResult configResult = git.GetFromLocalConfig(GVFSConstants.GitConfig.EnlistmentId);
            if (configResult.TryParseAsString(out enlistmentId, out string _, defaultValue: string.Empty))
            {
                enlistmentId = enlistmentId.Trim();
            }

            configResult = git.GetFromLocalConfig(GVFSConstants.GitConfig.MountId);
            if (configResult.TryParseAsString(out mountId, out string _, defaultValue: string.Empty))
            {
                mountId = mountId.Trim();
            }

            JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "GVFSMount", enlistmentId: enlistmentId, mountId: mountId);

            tracer.AddLogFileEventListener(
                GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.MountProcess),
                verbosity,
                keywords);
            if (this.ShowDebugWindow)
            {
                tracer.AddDiagnosticConsoleEventListener(verbosity, keywords);
            }

            return(tracer);
        }
Пример #8
0
        private bool TryInitializeUpgrader(out string error)
        {
            if (this.DryRun && this.Confirmed)
            {
                error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible.";
                return(false);
            }

            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                error = null;
                if (this.upgrader == null)
                {
                    this.productUpgraderPlatformStrategy = GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(this.fileSystem, tracer: null);
                    if (!this.productUpgraderPlatformStrategy.TryPrepareLogDirectory(out error))
                    {
                        return(false);
                    }

                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);

                    string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
                    if (string.IsNullOrEmpty(gitBinPath))
                    {
                        error = $"nameof(this.TryInitializeUpgrader): Unable to locate git installation. Ensure git is installed and try again.";
                        return(false);
                    }

                    ICredentialStore credentialStore = new GitProcess(gitBinPath, workingDirectoryRoot: null);

                    ProductUpgrader upgrader;
                    if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, new LocalGVFSConfig(), credentialStore, this.DryRun, this.NoVerify, out upgrader, out error))
                    {
                        this.upgrader = upgrader;
                    }
                    else
                    {
                        error = $"ERROR: {error}";
                    }
                }

                return(this.upgrader != null);
            }
            else
            {
                error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
                return(false);
            }
        }
Пример #9
0
        private bool TryInitializeUpgrader(out string error)
        {
            if (this.DryRun && this.Confirmed)
            {
                error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible.";
                return(false);
            }

            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                error = null;
                if (this.upgrader == null)
                {
                    // Under normal circumstances ProductUpgraderInfo.GetLogDirectoryPath will have already been created by GVFS.Service.  If for some reason it
                    // does not (e.g. the service failed to start), we need to create ProductUpgraderInfo.GetLogDirectoryPath() explicity to ensure that
                    // it has the correct ACLs (so that both admin and non-admin users can create log files).
                    // If the logs directory does not already exist, this call could fail when running as a non-elevated user.
                    string createDirectoryError;
                    if (!this.fileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(ProductUpgraderInfo.GetLogDirectoryPath(), out createDirectoryError))
                    {
                        error  = $"ERROR: Unable to create directory `{ProductUpgraderInfo.GetLogDirectoryPath()}`";
                        error += $"\n{createDirectoryError}";
                        error += $"\n\nTry running {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} from an elevated command prompt.";
                        return(false);
                    }

                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);

                    ProductUpgrader upgrader;
                    if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, this.DryRun, this.NoVerify, out upgrader, out error))
                    {
                        this.upgrader = upgrader;
                    }
                    else
                    {
                        error = $"ERROR: {error}";
                    }
                }

                return(this.upgrader != null);
            }
            else
            {
                error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
                return(false);
            }
        }
Пример #10
0
        private static void StartLogFile(string enlistmentRoot, JsonTracer tracer)
        {
            if (!tracer.HasLogFileEventListener)
            {
                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(
                        Path.Combine(enlistmentRoot, GVFSPlatform.Instance.Constants.DotGVFSRoot, GVFSConstants.DotGVFS.LogName),
                        GVFSConstants.LogFileTypes.MountUpgrade),
                    EventLevel.Informational,
                    Keywords.Any);

                tracer.WriteStartEvent(enlistmentRoot, repoUrl: "N/A", cacheServerUrl: "N/A");
            }
        }
Пример #11
0
        private JsonTracer CreateTracer()
        {
            string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                this.logDirectory,
                GVFSConstants.LogFileTypes.UpgradeProcess);

            JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeProcess");

            jsonTracer.AddLogFileEventListener(
                logFilePath,
                DefaultEventLevel,
                Keywords.Any);

            return(jsonTracer);
        }
Пример #12
0
        private JsonTracer CreateTracer(GVFSEnlistment enlistment, EventLevel verbosity, Keywords keywords)
        {
            JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "GVFSMount", enlistment.GetEnlistmentId(), enlistment.GetMountId());

            tracer.AddLogFileEventListener(
                GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.MountProcess),
                verbosity,
                keywords);
            if (this.ShowDebugWindow)
            {
                tracer.AddDiagnosticConsoleEventListener(verbosity, keywords);
            }

            return(tracer);
        }
Пример #13
0
        public static void Main(string[] args)
        {
            GVFSPlatformLoader.Initialize();

            using (JsonTracer tracer = new JsonTracer("Microsoft.Git.GVFS.Service.UI", "Service.UI"))
            {
                string logLocation = Path.Combine(
                    Environment.GetEnvironmentVariable("LocalAppData"),
                    GVFSConstants.Service.UIName,
                    "serviceUI.log");

                tracer.AddLogFileEventListener(logLocation, EventLevel.Informational, Keywords.Any);
                GVFSServiceUI process = new GVFSServiceUI(tracer);
                process.Start(args);
            }
        }
Пример #14
0
        private JsonTracer CreateTracer()
        {
            string logFilePath = ScalarEnlistment.GetNewScalarLogFileName(
                this.logDirectory,
                ScalarConstants.LogFileTypes.UpgradeProcess,
                logId: null,
                fileSystem: this.fileSystem);

            JsonTracer jsonTracer = new JsonTracer(ScalarConstants.ScalarEtwProviderName, "UpgradeProcess");

            jsonTracer.AddLogFileEventListener(
                logFilePath,
                DefaultEventLevel,
                Keywords.Any);

            return(jsonTracer);
        }
Пример #15
0
        public UpgradeOrchestrator()
        {
            string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                ProductUpgraderInfo.GetLogDirectoryPath(),
                GVFSConstants.LogFileTypes.UpgradeProcess);
            JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeProcess");

            jsonTracer.AddLogFileEventListener(
                logFilePath,
                DefaultEventLevel,
                Keywords.Any);

            this.tracer        = jsonTracer;
            this.preRunChecker = new InstallerPreRunChecker(this.tracer, GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm);
            this.output        = Console.Out;
            this.input         = Console.In;
            this.mount         = false;
            this.ExitCode      = ReturnCode.Success;
        }
        protected override void Execute(GVFSEnlistment enlistment)
        {
            if (this.List || (
                    !this.Prune &&
                    !this.Disable &&
                    string.IsNullOrEmpty(this.Add) &&
                    string.IsNullOrEmpty(this.Remove) &&
                    string.IsNullOrEmpty(this.Set) &&
                    string.IsNullOrEmpty(this.File)))
            {
                this.ListSparseFolders(enlistment.EnlistmentRoot);
                return;
            }

            this.CheckOptions();

            using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, SparseVerbName))
            {
                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Sparse),
                    EventLevel.Informational,
                    Keywords.Any);

                EventMetadata metadata = new EventMetadata();
                metadata.Add(nameof(this.Set), this.Set);
                metadata.Add(nameof(this.File), this.File);
                metadata.Add(nameof(this.Add), this.Add);
                metadata.Add(nameof(this.Remove), this.Remove);
                metadata.Add(nameof(this.Prune), this.Prune);
                metadata.Add(nameof(this.Disable), this.Disable);
                tracer.RelatedInfo(metadata, $"Running sparse");

                HashSet <string> directories;
                bool             needToChangeProjection = false;
                using (GVFSDatabase database = new GVFSDatabase(new PhysicalFileSystem(), enlistment.EnlistmentRoot, new SqliteDatabase()))
                {
                    SparseTable sparseTable = new SparseTable(database);
                    directories = sparseTable.GetAll();

                    List <string> foldersToRemove = new List <string>();
                    List <string> foldersToAdd    = new List <string>();

                    if (this.Disable)
                    {
                        if (directories.Count > 0)
                        {
                            this.WriteMessage(tracer, "Removing all folders from sparse list. When the sparse list is empty, all folders are projected.");
                            needToChangeProjection = true;
                            foldersToRemove.AddRange(directories);
                            directories.Clear();
                        }
                        else
                        {
                            return;
                        }
                    }
                    else if (!string.IsNullOrEmpty(this.Set) || !string.IsNullOrEmpty(this.File))
                    {
                        IEnumerable <string> folders = null;
                        if (!string.IsNullOrEmpty(this.Set))
                        {
                            folders = this.ParseFolderList(this.Set);
                        }
                        else if (!string.IsNullOrEmpty(this.File))
                        {
                            PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                            folders = this.ParseFolderList(fileSystem.ReadAllText(this.File), folderSeparator: Environment.NewLine);
                        }
                        else
                        {
                            this.WriteMessage(tracer, "Invalid options specified.");
                            throw new InvalidOperationException();
                        }

                        foreach (string folder in folders)
                        {
                            if (!directories.Contains(folder))
                            {
                                needToChangeProjection = true;
                                foldersToAdd.Add(folder);
                            }
                            else
                            {
                                // Remove from directories so that the only directories left in the directories collection
                                // will be the ones that will need to be removed from sparse set
                                directories.Remove(folder);
                            }
                        }

                        if (directories.Count > 0)
                        {
                            needToChangeProjection = true;
                            foldersToRemove.AddRange(directories);
                            directories.Clear();
                        }

                        // Need to add folders that will be in the projection back into directories for the status check
                        foreach (string folder in folders)
                        {
                            directories.Add(folder);
                        }
                    }
                    else
                    { // Process adds and removes
                        foreach (string folder in this.ParseFolderList(this.Remove))
                        {
                            if (directories.Contains(folder))
                            {
                                needToChangeProjection = true;
                                directories.Remove(folder);
                                foldersToRemove.Add(folder);
                            }
                        }

                        foreach (string folder in this.ParseFolderList(this.Add))
                        {
                            if (!directories.Contains(folder))
                            {
                                needToChangeProjection = true;
                                directories.Add(folder);
                                foldersToAdd.Add(folder);
                            }
                        }
                    }

                    if (needToChangeProjection || this.Prune)
                    {
                        if (directories.Count > 0)
                        {
                            // Make sure there is a clean git status before allowing sparse set to change
                            this.CheckGitStatus(tracer, enlistment, directories);
                        }

                        this.UpdateSparseFolders(tracer, sparseTable, foldersToRemove, foldersToAdd);
                    }

                    if (needToChangeProjection)
                    {
                        // Force a projection update to get the current inclusion set
                        this.ForceProjectionChange(tracer, enlistment);
                        tracer.RelatedInfo("Projection updated after adding or removing folders.");
                    }
                    else
                    {
                        this.WriteMessage(tracer, "No folders to update in sparse set.");
                    }

                    if (this.Prune && directories.Count > 0)
                    {
                        this.PruneFoldersOutsideSparse(tracer, enlistment, sparseTable);
                    }
                }
            }
        }
Пример #17
0
        protected override void Execute(GVFSEnlistment enlistment)
        {
            using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "Prefetch"))
            {
                if (this.Verbose)
                {
                    tracer.AddDiagnosticConsoleEventListener(EventLevel.Informational, Keywords.Any);
                }

                string cacheServerUrl = CacheServerResolver.GetUrlFromConfig(enlistment);

                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Prefetch),
                    EventLevel.Informational,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    cacheServerUrl);

                RetryConfig retryConfig = this.GetRetryConfig(tracer, enlistment, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));

                CacheServerInfo cacheServer = this.ResolvedCacheServer;
                GVFSConfig      gvfsConfig  = this.GVFSConfig;
                if (!this.SkipVersionCheck)
                {
                    string authErrorMessage;
                    if (!this.ShowStatusWhileRunning(
                            () => enlistment.Authentication.TryRefreshCredentials(tracer, out authErrorMessage),
                            "Authenticating"))
                    {
                        this.ReportErrorAndExit(tracer, "Unable to prefetch because authentication failed");
                    }

                    if (gvfsConfig == null)
                    {
                        gvfsConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig);
                    }

                    if (cacheServer == null)
                    {
                        CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment);
                        cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServerUrl, gvfsConfig);
                    }

                    this.ValidateClientVersions(tracer, enlistment, gvfsConfig, showWarnings: false);

                    this.Output.WriteLine("Configured cache server: " + cacheServer);
                }

                this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, gvfsConfig, cacheServer);

                try
                {
                    EventMetadata metadata = new EventMetadata();
                    metadata.Add("Commits", this.Commits);
                    metadata.Add("Files", this.Files);
                    metadata.Add("Folders", this.Folders);
                    metadata.Add("FoldersListFile", this.FoldersListFile);
                    metadata.Add("HydrateFiles", this.HydrateFiles);
                    tracer.RelatedEvent(EventLevel.Informational, "PerformPrefetch", metadata);

                    GitObjectsHttpRequestor objectRequestor = new GitObjectsHttpRequestor(tracer, enlistment, cacheServer, retryConfig);

                    if (this.Commits)
                    {
                        if (!string.IsNullOrWhiteSpace(this.Files) ||
                            !string.IsNullOrWhiteSpace(this.Folders) ||
                            !string.IsNullOrWhiteSpace(this.FoldersListFile))
                        {
                            this.ReportErrorAndExit(tracer, "You cannot prefetch commits and blobs at the same time.");
                        }

                        if (this.HydrateFiles)
                        {
                            this.ReportErrorAndExit(tracer, "You can only specify --hydrate with --files or --folders");
                        }

                        PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                        using (FileBasedLock prefetchLock = new FileBasedLock(
                                   fileSystem,
                                   tracer,
                                   Path.Combine(enlistment.GitPackRoot, PrefetchCommitsAndTreesLock),
                                   enlistment.EnlistmentRoot,
                                   overwriteExistingLock: true))
                        {
                            this.WaitUntilLockIsAcquired(tracer, prefetchLock);
                            this.PrefetchCommits(tracer, enlistment, objectRequestor, cacheServer);
                        }
                    }
                    else
                    {
                        this.PrefetchBlobs(tracer, enlistment, objectRequestor, cacheServer);
                    }
                }
                catch (VerbAbortedException)
                {
                    throw;
                }
                catch (AggregateException aggregateException)
                {
                    this.Output.WriteLine(
                        "Cannot prefetch {0}. " + ConsoleHelper.GetGVFSLogMessage(enlistment.EnlistmentRoot),
                        enlistment.EnlistmentRoot);
                    foreach (Exception innerException in aggregateException.Flatten().InnerExceptions)
                    {
                        tracer.RelatedError(
                            new EventMetadata
                        {
                            { "Verb", typeof(PrefetchVerb).Name },
                            { "Exception", innerException.ToString() }
                        },
                            $"Unhandled {innerException.GetType().Name}: {innerException.Message}");
                    }

                    Environment.ExitCode = (int)ReturnCode.GenericError;
                }
                catch (Exception e)
                {
                    this.Output.WriteLine(
                        "Cannot prefetch {0}. " + ConsoleHelper.GetGVFSLogMessage(enlistment.EnlistmentRoot),
                        enlistment.EnlistmentRoot);
                    tracer.RelatedError(
                        new EventMetadata
                    {
                        { "Verb", typeof(PrefetchVerb).Name },
                        { "Exception", e.ToString() }
                    },
                        $"Unhandled {e.GetType().Name}: {e.Message}");

                    Environment.ExitCode = (int)ReturnCode.GenericError;
                }
            }
        }
Пример #18
0
        public override void Execute()
        {
            this.ValidatePathParameter(this.EnlistmentRootPathParameter);

            string hooksPath = this.GetGVFSHooksPathAndCheckVersion(tracer: null, hooksVersion: out _);

            GVFSEnlistment enlistment = GVFSEnlistment.CreateWithoutRepoUrlFromDirectory(
                this.EnlistmentRootPathParameter,
                GVFSPlatform.Instance.GitInstallation.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 that GVFS is not 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 (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "RepairVerb"))
            {
                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Repair),
                    EventLevel.Verbose,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    "N/A",
                    new EventMetadata
                {
                    { "Confirmed", this.Confirmed },
                    { "IsElevated", GVFSPlatform.Instance.IsElevated() },
                    { "NamedPipename", enlistment.NamedPipeName },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                });

                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();
                }
            }
        }
Пример #19
0
        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.ExitCodeIsFailure || 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", enlistmentId: null, mountId: null, 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 },
                });

                string error;
                if (!enlistment.Authentication.TryInitialize(tracer, enlistment, out error))
                {
                    tracer.RelatedError(error);
                    Console.WriteLine(error);
                    return(ExitFailure);
                }

                RetryConfig    retryConfig = new RetryConfig(this.MaxAttempts, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));
                BlobPrefetcher prefetcher  = this.GetFolderPrefetcher(tracer, enlistment, cacheServer, retryConfig);
                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);
            }
        }
Пример #20
0
        protected override void Execute(ScalarEnlistment enlistment)
        {
            using (JsonTracer tracer = new JsonTracer(ScalarConstants.ScalarEtwProviderName, RunVerbName))
            {
                string cacheServerUrl = CacheServerResolver.GetUrlFromConfig(enlistment);

                string logFileName = ScalarEnlistment.GetNewScalarLogFileName(
                    enlistment.ScalarLogsRoot,
                    ScalarConstants.LogFileTypes.Maintenance,
                    logId: this.StartedByService ? "service" : null);

                List <GitMaintenanceStep> steps = new List <GitMaintenanceStep>();

                tracer.AddLogFileEventListener(
                    logFileName,
                    EventLevel.Informational,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    cacheServerUrl,
                    this.AddVerbDataToMetadata(
                        new EventMetadata
                {
                    { nameof(this.MaintenanceTask), this.MaintenanceTask },
                    { nameof(this.PackfileMaintenanceBatchSize), this.PackfileMaintenanceBatchSize },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                    { nameof(this.StartedByService), this.StartedByService },
                }));

                this.InitializeCachePaths(tracer, enlistment);
                PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                using (ScalarContext context = new ScalarContext(tracer, fileSystem, enlistment))
                {
                    try
                    {
                        GitObjectsHttpRequestor objectRequestor = null;
                        CacheServerInfo         cacheServer;
                        GitObjects gitObjects;

                        switch (this.MaintenanceTask)
                        {
                        case ScalarConstants.VerbParameters.Maintenance.AllTasksName:
                            steps.Add(new ConfigStep(context));
                            this.InitializeServerConnection(tracer, enlistment, cacheServerUrl, out objectRequestor, out cacheServer);
                            gitObjects = new GitObjects(tracer, enlistment, objectRequestor, fileSystem);
                            steps.Add(new FetchStep(context, gitObjects, requireCacheLock: false, forceRun: !this.StartedByService));
                            steps.Add(new CommitGraphStep(context, requireObjectCacheLock: false));
                            steps.Add(new LooseObjectsStep(context, forceRun: !this.StartedByService));
                            steps.Add(new PackfileMaintenanceStep(
                                          context,
                                          forceRun: !this.StartedByService,
                                          batchSize: string.IsNullOrWhiteSpace(this.PackfileMaintenanceBatchSize) ?
                                          PackfileMaintenanceStep.DefaultBatchSizeBytes.ToString() :
                                          this.PackfileMaintenanceBatchSize));
                            break;

                        case ScalarConstants.VerbParameters.Maintenance.LooseObjectsTaskName:
                            this.FailIfBatchSizeSet(tracer);
                            steps.Add(new LooseObjectsStep(context, forceRun: !this.StartedByService));
                            break;

                        case ScalarConstants.VerbParameters.Maintenance.PackFilesTaskName:
                            steps.Add(new PackfileMaintenanceStep(
                                          context,
                                          forceRun: !this.StartedByService,
                                          batchSize: string.IsNullOrWhiteSpace(this.PackfileMaintenanceBatchSize) ?
                                          PackfileMaintenanceStep.DefaultBatchSizeBytes.ToString() :
                                          this.PackfileMaintenanceBatchSize));
                            break;

                        case ScalarConstants.VerbParameters.Maintenance.FetchTaskName:
                            this.FailIfBatchSizeSet(tracer);
                            this.InitializeServerConnection(tracer, enlistment, cacheServerUrl, out objectRequestor, out cacheServer);
                            gitObjects = new GitObjects(tracer, enlistment, objectRequestor, fileSystem);
                            steps.Add(new FetchStep(context, gitObjects, requireCacheLock: false, forceRun: !this.StartedByService));
                            break;

                        case ScalarConstants.VerbParameters.Maintenance.CommitGraphTaskName:
                            this.FailIfBatchSizeSet(tracer);
                            steps.Add(new CommitGraphStep(context, requireObjectCacheLock: false));
                            break;

                        case ScalarConstants.VerbParameters.Maintenance.ConfigTaskName:
                            this.FailIfBatchSizeSet(tracer);
                            steps.Add(new ConfigStep(context));
                            break;

                        default:
                            this.ReportErrorAndExit($"Unknown maintenance task requested: '{this.MaintenanceTask}'");
                            break;
                        }

                        foreach (GitMaintenanceStep step in steps)
                        {
                            this.ShowStatusWhileRunning(() => { step.Execute(); return(true); }, step.ProgressMessage);
                        }
                    }
                    catch (VerbAbortedException)
                    {
                        throw;
                    }
                    catch (AggregateException aggregateException)
                    {
                        string error = $"AggregateException thrown while running '{this.MaintenanceTask}' task: {aggregateException.Message}";
                        tracer.RelatedError(this.CreateEventMetadata(aggregateException), error);
                        foreach (Exception innerException in aggregateException.Flatten().InnerExceptions)
                        {
                            tracer.RelatedError(
                                this.CreateEventMetadata(innerException),
                                $"Unhandled {innerException.GetType().Name}: {innerException.Message}");
                        }

                        this.ReportErrorAndExit(tracer, ReturnCode.GenericError, error);
                    }
                    catch (Exception e)
                    {
                        string error = $"Exception thrown while running '{this.MaintenanceTask}' task: {e.Message}";
                        tracer.RelatedError(this.CreateEventMetadata(e), error);
                        this.ReportErrorAndExit(tracer, ReturnCode.GenericError, error);
                    }
                }
            }
        }
Пример #21
0
        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))
                {
                    if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSService)
                    {
                        tracer.RelatedEvent(
                            EventLevel.Informational,
                            $"{nameof(MountVerb)}_{nameof(this.Execute)}_EnablingKernelDriverViaService",
                            new EventMetadata
                        {
                            { "KernelDriver.IsReady_Error", errorMessage },
                            { TracingConstants.MessageKey.InfoMessage, "Service will retry" }
                        });

                        if (!this.ShowStatusWhileRunning(
                                () => { return(this.TryEnableAndAttachPrjFltThroughService(enlistment.EnlistmentRoot, out errorMessage)); },
                                $"Attaching ProjFS to volume"))
                        {
                            this.ReportErrorAndExit(tracer, ReturnCode.FilterError, errorMessage);
                        }
                    }
                    else
                    {
                        tracer.RelatedEvent(
                            EventLevel.Informational,
                            $"{nameof(MountVerb)}_{nameof(this.Execute)}",
                            new EventMetadata
                        {
                            { "KernelDriver.IsReady_Error", errorMessage },
                        });

                        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(tracer, enlistment, mountExecutableLocation, out errorMessage)); },
                        "Mounting"))
                {
                    this.ReportErrorAndExit(errorMessage);
                }
            }

            if (!this.Unattended &&
                GVFSPlatform.Instance.UnderConstruction.SupportsGVFSService)
            {
                if (!this.ShowStatusWhileRunning(
                        () => { return(this.RegisterMount(enlistment, out errorMessage)); },
                        "Registering for automount"))
                {
                    this.Output.WriteLine("    WARNING: " + errorMessage);
                }
            }
        }
Пример #22
0
        protected override void Execute(GVFSEnlistment enlistment)
        {
            using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "Dehydrate"))
            {
                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Dehydrate),
                    EventLevel.Informational,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    CacheServerResolver.GetUrlFromConfig(enlistment),
                    new EventMetadata
                {
                    { "Confirmed", this.Confirmed },
                    { "NoStatus", this.NoStatus },
                    { "NamedPipeName", enlistment.NamedPipeName },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                });

                if (!this.Confirmed)
                {
                    this.Output.WriteLine(
                        @"WARNING: THIS IS AN EXPERIMENTAL FEATURE

Dehydrate will back up your src folder, and then create a new, empty src folder 
with a fresh virtualization of the repo. All of your downloaded objects, branches, 
and siblings of the src folder will be preserved. Your modified working directory 
files will be moved to the backup, and your new working directory will not have 
any of your uncommitted changes.

Before you dehydrate, make sure you have committed any working directory changes 
you want to keep. If you choose not to, you can still find your uncommitted changes 
in the backup folder, but it will be harder to find them because 'git status' 
will not work in the backup.

To actually execute the dehydrate, run 'gvfs dehydrate --confirm' from the parent 
of your enlistment's src folder.
");

                    return;
                }

                this.CheckGitStatus(tracer, enlistment);

                string backupRoot = Path.GetFullPath(Path.Combine(enlistment.EnlistmentRoot, "dehydrate_backup", DateTime.Now.ToString("yyyyMMdd_HHmmss")));
                this.Output.WriteLine();
                this.WriteMessage(tracer, "Starting dehydration. All of your existing files will be backed up in " + backupRoot);
                this.WriteMessage(tracer, "WARNING: If you abort the dehydrate after this point, the repo may become corrupt");
                this.Output.WriteLine();

                this.Unmount(tracer);

                string error;
                if (!DiskLayoutUpgrade.TryCheckDiskLayoutVersion(tracer, enlistment.EnlistmentRoot, out error))
                {
                    this.ReportErrorAndExit(tracer, error);
                }

                RetryConfig retryConfig;
                if (!RetryConfig.TryLoadFromGitConfig(tracer, enlistment, out retryConfig, out error))
                {
                    this.ReportErrorAndExit(tracer, "Failed to determine GVFS timeout and max retries: " + error);
                }

                // Local cache and objects paths are required for TryDownloadGitObjects
                this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGVFSConfig: null, cacheServer: null);

                if (this.TryBackupFiles(tracer, enlistment, backupRoot))
                {
                    if (this.TryDownloadGitObjects(tracer, enlistment, retryConfig) &&
                        this.TryRecreateIndex(tracer, enlistment))
                    {
                        // Converting the src folder to partial must be the final step before mount
                        this.PrepareSrcFolder(tracer, enlistment);
                        this.Mount(tracer);

                        this.Output.WriteLine();
                        this.WriteMessage(tracer, "The repo was successfully dehydrated and remounted");
                    }
                }
                else
                {
                    this.Output.WriteLine();
                    this.WriteMessage(tracer, "ERROR: Backup failed. We will attempt to mount, but you may need to reclone if that fails");

                    this.Mount(tracer);
                    this.WriteMessage(tracer, "Dehydrate failed, but remounting succeeded");
                }
            }
        }
Пример #23
0
        protected override void Execute(GVFSEnlistment enlistment)
        {
            using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, SparseVerbName))
            {
                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Sparse),
                    EventLevel.Informational,
                    Keywords.Any);

                bool needToChangeProjection = false;
                using (GVFSDatabase database = new GVFSDatabase(new PhysicalFileSystem(), enlistment.EnlistmentRoot, new SqliteDatabase()))
                {
                    SparseTable      sparseTable = new SparseTable(database);
                    HashSet <string> directories = sparseTable.GetAll();

                    string[] foldersToRemove = this.ParseFolderList(this.Remove);
                    string[] foldersToAdd    = this.ParseFolderList(this.Add);

                    if (this.List || (foldersToAdd.Length == 0 && foldersToRemove.Length == 0))
                    {
                        if (directories.Count == 0)
                        {
                            this.Output.WriteLine("No folders in sparse list. When the sparse list is empty, all folders are projected.");
                        }
                        else
                        {
                            foreach (string directory in directories)
                            {
                                this.Output.WriteLine(directory);
                            }
                        }

                        return;
                    }

                    foreach (string folder in foldersToRemove)
                    {
                        if (directories.Contains(folder))
                        {
                            needToChangeProjection = true;
                            break;
                        }
                    }

                    if (!needToChangeProjection)
                    {
                        foreach (string folder in foldersToAdd)
                        {
                            if (!directories.Contains(folder))
                            {
                                needToChangeProjection = true;
                                break;
                            }
                        }
                    }

                    if (needToChangeProjection)
                    {
                        // Make sure there is a clean git status before allowing sparse set to change
                        this.CheckGitStatus(tracer, enlistment);
                        if (!this.ShowStatusWhileRunning(
                                () =>
                        {
                            foreach (string directoryPath in foldersToRemove)
                            {
                                tracer.RelatedInfo($"Removing '{directoryPath}' from sparse folders.");
                                sparseTable.Remove(directoryPath);
                            }

                            foreach (string directoryPath in foldersToAdd)
                            {
                                tracer.RelatedInfo($"Adding '{directoryPath}' to sparse folders.");
                                sparseTable.Add(directoryPath);
                            }

                            return(true);
                        },
                                "Updating sparse folder set",
                                suppressGvfsLogMessage: true))
                        {
                            this.ReportErrorAndExit(tracer, "Failed to update sparse folder set.");
                        }
                    }
                }

                if (needToChangeProjection)
                {
                    // Force a projection update to get the current inclusion set
                    this.ForceProjectionChange(tracer, enlistment);
                    tracer.RelatedInfo("Projection updated after adding or removing folders.");
                }
                else
                {
                    this.WriteMessage(tracer, "No folders to update in sparse set.");
                }
            }
        }
Пример #24
0
        protected override void Execute(GSDEnlistment enlistment)
        {
            string errorMessage            = null;
            string mountExecutableLocation = null;

            using (JsonTracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "ExecuteMount"))
            {
                PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                GitRepo            gitRepo    = new GitRepo(tracer, enlistment, fileSystem);
                GSDContext         context    = new GSDContext(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(
                    GSDEnlistment.GetNewGSDLogFileName(enlistment.GSDLogsRoot, GSDConstants.LogFileTypes.MountVerb),
                    EventLevel.Verbose,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    cacheServer.Url,
                    new EventMetadata
                {
                    { "Unattended", this.Unattended },
                    { "IsElevated", GSDPlatform.Instance.IsElevated() },
                    { "NamedPipeName", enlistment.NamedPipeName },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                });

                RetryConfig     retryConfig     = null;
                ServerGSDConfig serverGSDConfig = this.DownloadedGSDConfig;
                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 GSD can authenticate.");
                    }

                    if (serverGSDConfig == null)
                    {
                        if (retryConfig == null)
                        {
                            retryConfig = this.GetRetryConfig(tracer, enlistment);
                        }

                        serverGSDConfig = this.QueryGSDConfig(tracer, enlistment, retryConfig);
                    }

                    this.ValidateClientVersions(tracer, enlistment, serverGSDConfig, showWarnings: true);

                    CacheServerResolver cacheServerResolver = new CacheServerResolver(tracer, enlistment);
                    cacheServer = cacheServerResolver.ResolveNameFromRemote(cacheServer.Url, serverGSDConfig);
                    this.Output.WriteLine("Configured cache server: " + cacheServer);
                }

                this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGSDConfig, 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.DotGSDRoot, 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(tracer, enlistment, mountExecutableLocation, out errorMessage)); },
                        "Mounting"))
                {
                    this.ReportErrorAndExit(tracer, errorMessage);
                }

                if (!this.Unattended)
                {
                    tracer.RelatedInfo($"{nameof(this.Execute)}: Registering for automount");

                    if (this.ShowStatusWhileRunning(
                            () => { return(this.RegisterMount(enlistment, out errorMessage)); },
                            "Registering for automount"))
                    {
                        tracer.RelatedInfo($"{nameof(this.Execute)}: Registered for automount");
                    }
                    else
                    {
                        this.Output.WriteLine("    WARNING: " + errorMessage);
                        tracer.RelatedInfo($"{nameof(this.Execute)}: Failed to register for automount");
                    }
                }
            }
        }
Пример #25
0
        protected override void Execute(GVFSEnlistment enlistment)
        {
            using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "Prefetch"))
            {
                if (this.Verbose)
                {
                    tracer.AddDiagnosticConsoleEventListener(EventLevel.Informational, Keywords.Any);
                }

                string cacheServerUrl = CacheServerResolver.GetUrlFromConfig(enlistment);

                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Prefetch),
                    EventLevel.Informational,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    cacheServerUrl);

                try
                {
                    EventMetadata metadata = new EventMetadata();
                    metadata.Add("Commits", this.Commits);
                    metadata.Add("Files", this.Files);
                    metadata.Add("Folders", this.Folders);
                    metadata.Add("FileListFile", this.FilesListFile);
                    metadata.Add("FoldersListFile", this.FoldersListFile);
                    metadata.Add("FilesFromStdIn", this.FilesFromStdIn);
                    metadata.Add("FoldersFromStdIn", this.FoldersFromStdIn);
                    metadata.Add("HydrateFiles", this.HydrateFiles);
                    tracer.RelatedEvent(EventLevel.Informational, "PerformPrefetch", metadata);

                    if (this.Commits)
                    {
                        if (!string.IsNullOrWhiteSpace(this.Files) ||
                            !string.IsNullOrWhiteSpace(this.Folders) ||
                            !string.IsNullOrWhiteSpace(this.FoldersListFile) ||
                            !string.IsNullOrWhiteSpace(this.FilesListFile) ||
                            this.FilesFromStdIn ||
                            this.FoldersFromStdIn)
                        {
                            this.ReportErrorAndExit(tracer, "You cannot prefetch commits and blobs at the same time.");
                        }

                        if (this.HydrateFiles)
                        {
                            this.ReportErrorAndExit(tracer, "You can only specify --hydrate with --files or --folders");
                        }

                        GitObjectsHttpRequestor objectRequestor;
                        CacheServerInfo         cacheServer;
                        this.InitializeServerConnection(
                            tracer,
                            enlistment,
                            cacheServerUrl,
                            out objectRequestor,
                            out cacheServer);
                        this.PrefetchCommits(tracer, enlistment, objectRequestor, cacheServer);
                    }
                    else
                    {
                        string        headCommitId;
                        List <string> filesList;
                        List <string> foldersList;
                        FileBasedDictionary <string, string> lastPrefetchArgs;

                        this.LoadBlobPrefetchArgs(tracer, enlistment, out headCommitId, out filesList, out foldersList, out lastPrefetchArgs);

                        if (BlobPrefetcher.IsNoopPrefetch(tracer, lastPrefetchArgs, headCommitId, filesList, foldersList, this.HydrateFiles))
                        {
                            Console.WriteLine("All requested files are already available. Nothing new to prefetch.");
                        }
                        else
                        {
                            GitObjectsHttpRequestor objectRequestor;
                            CacheServerInfo         cacheServer;
                            this.InitializeServerConnection(
                                tracer,
                                enlistment,
                                cacheServerUrl,
                                out objectRequestor,
                                out cacheServer);
                            this.PrefetchBlobs(tracer, enlistment, headCommitId, filesList, foldersList, lastPrefetchArgs, objectRequestor, cacheServer);
                        }
                    }
                }
                catch (VerbAbortedException)
                {
                    throw;
                }
                catch (AggregateException aggregateException)
                {
                    this.Output.WriteLine(
                        "Cannot prefetch {0}. " + ConsoleHelper.GetGVFSLogMessage(enlistment.EnlistmentRoot),
                        enlistment.EnlistmentRoot);
                    foreach (Exception innerException in aggregateException.Flatten().InnerExceptions)
                    {
                        tracer.RelatedError(
                            new EventMetadata
                        {
                            { "Verb", typeof(PrefetchVerb).Name },
                            { "Exception", innerException.ToString() }
                        },
                            $"Unhandled {innerException.GetType().Name}: {innerException.Message}");
                    }

                    Environment.ExitCode = (int)ReturnCode.GenericError;
                }
                catch (Exception e)
                {
                    this.Output.WriteLine(
                        "Cannot prefetch {0}. " + ConsoleHelper.GetGVFSLogMessage(enlistment.EnlistmentRoot),
                        enlistment.EnlistmentRoot);
                    tracer.RelatedError(
                        new EventMetadata
                    {
                        { "Verb", typeof(PrefetchVerb).Name },
                        { "Exception", e.ToString() }
                    },
                        $"Unhandled {e.GetType().Name}: {e.Message}");

                    Environment.ExitCode = (int)ReturnCode.GenericError;
                }
            }
        }
        protected override void Execute(GVFSEnlistment enlistment)
        {
            using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "Dehydrate"))
            {
                tracer.AddLogFileEventListener(
                    GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.LogFileTypes.Dehydrate),
                    EventLevel.Informational,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    CacheServerResolver.GetUrlFromConfig(enlistment),
                    new EventMetadata
                {
                    { "Confirmed", this.Confirmed },
                    { "NoStatus", this.NoStatus },
                    { "NamedPipeName", enlistment.NamedPipeName },
                    { "Folders", this.Folders },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                });

                // This is only intended to be run by functional tests
                if (this.MaintenanceJob != null)
                {
                    this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig: null, serverGVFSConfig: null, cacheServer: null);
                    PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                    using (GitRepo gitRepo = new GitRepo(tracer, enlistment, fileSystem))
                        using (GVFSContext context = new GVFSContext(tracer, fileSystem, gitRepo, enlistment))
                        {
                            switch (this.MaintenanceJob)
                            {
                            case "LooseObjects":
                                (new LooseObjectsStep(context, forceRun: true)).Execute();
                                return;

                            case "PackfileMaintenance":
                                (new PackfileMaintenanceStep(
                                     context,
                                     forceRun: true,
                                     batchSize: this.PackfileMaintenanceBatchSize ?? PackfileMaintenanceStep.DefaultBatchSize)).Execute();
                                return;

                            case "PostFetch":
                                (new PostFetchStep(context, new System.Collections.Generic.List <string>(), requireObjectCacheLock: false)).Execute();
                                return;

                            default:
                                this.ReportErrorAndExit($"Unknown maintenance job requested: {this.MaintenanceJob}");
                                break;
                            }
                        }
                }

                bool fullDehydrate = string.IsNullOrEmpty(this.Folders);

                if (!this.Confirmed && fullDehydrate)
                {
                    this.Output.WriteLine(
                        @"WARNING: THIS IS AN EXPERIMENTAL FEATURE

Dehydrate will back up your src folder, and then create a new, empty src folder 
with a fresh virtualization of the repo. All of your downloaded objects, branches, 
and siblings of the src folder will be preserved. Your modified working directory 
files will be moved to the backup, and your new working directory will not have 
any of your uncommitted changes.

Before you dehydrate, make sure you have committed any working directory changes 
you want to keep. If you choose not to, you can still find your uncommitted changes 
in the backup folder, but it will be harder to find them because 'git status' 
will not work in the backup.

To actually execute the dehydrate, run 'gvfs dehydrate --confirm' from the parent 
of your enlistment's src folder.
");

                    return;
                }
                else if (!this.Confirmed)
                {
                    this.Output.WriteLine(
                        @"WARNING: THIS IS AN EXPERIMENTAL FEATURE

All of your downloaded objects, branches, and siblings of the src folder
will be preserved.  This will remove the folders specified and any working directory
files and folders even if ignored by git similar to 'git clean -xdf <path>'.

Before you dehydrate, you will have to commit any working directory changes 
you want to keep and have a clean 'git status'.

To actually execute the dehydrate, run 'gvfs dehydrate --confirm --folders <folder list>'
from a parent of the folders list.
");

                    return;
                }

                if (this.NoStatus && !fullDehydrate)
                {
                    this.ReportErrorAndExit(tracer, "Dehydrate --no-status not valid with --folders");
                    return;
                }

                bool cleanStatus = this.CheckGitStatus(tracer, enlistment, fullDehydrate);

                string backupRoot = Path.GetFullPath(Path.Combine(enlistment.EnlistmentRoot, "dehydrate_backup", DateTime.Now.ToString("yyyyMMdd_HHmmss")));
                this.Output.WriteLine();

                if (fullDehydrate)
                {
                    this.WriteMessage(tracer, $"Starting {this.RunningVerbName}. All of your existing files will be backed up in " + backupRoot);
                }

                this.WriteMessage(tracer, $"WARNING: If you abort the {this.RunningVerbName} after this point, the repo may become corrupt");

                this.Output.WriteLine();

                this.Unmount(tracer);

                string error;
                if (!DiskLayoutUpgrade.TryCheckDiskLayoutVersion(tracer, enlistment.EnlistmentRoot, out error))
                {
                    this.ReportErrorAndExit(tracer, error);
                }

                if (fullDehydrate)
                {
                    RetryConfig retryConfig;
                    if (!RetryConfig.TryLoadFromGitConfig(tracer, enlistment, out retryConfig, out error))
                    {
                        this.ReportErrorAndExit(tracer, "Failed to determine GVFS timeout and max retries: " + error);
                    }

                    string errorMessage;
                    if (!this.TryAuthenticate(tracer, enlistment, out errorMessage))
                    {
                        this.ReportErrorAndExit(tracer, errorMessage);
                    }

                    // Local cache and objects paths are required for TryDownloadGitObjects
                    this.InitializeLocalCacheAndObjectsPaths(tracer, enlistment, retryConfig, serverGVFSConfig: null, cacheServer: null);

                    this.RunFullDehydrate(tracer, enlistment, backupRoot, retryConfig);
                }
                else
                {
                    string[] folders = this.Folders.Split(new[] { FolderListSeparator }, StringSplitOptions.RemoveEmptyEntries);

                    if (folders.Length > 0)
                    {
                        if (cleanStatus)
                        {
                            this.DehydrateFolders(tracer, enlistment, folders);
                        }
                        else
                        {
                            this.ReportErrorAndExit($"Cannot {this.ActionName}: must have a clean git status.");
                        }
                    }
                    else
                    {
                        this.ReportErrorAndExit($"No folders to {this.ActionName}.");
                    }
                }
            }
        }
Пример #27
0
        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 (!GVFSPlatform.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, GVFSConstants.WorkingDirectoryRootName),
                        StringComparison.OrdinalIgnoreCase))
                {
                    this.ReportErrorAndExit("'--local-cache-path' cannot be inside the src folder");
                }
            }

            this.CheckKernelDriverSupported(normalizedEnlistmentRootPath);
            this.CheckNotInsideExistingRepo(normalizedEnlistmentRootPath);
            this.BlockEmptyCacheServerUrl(this.CacheServerUrl);

            try
            {
                GVFSEnlistment enlistment;
                Result         cloneResult = new Result(false);

                CacheServerInfo  cacheServer      = null;
                ServerGVFSConfig serverGVFSConfig = null;

                using (JsonTracer tracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "GVFSClone"))
                {
                    cloneResult = this.TryCreateEnlistment(fullEnlistmentRootPathParameter, normalizedEnlistmentRootPath, out enlistment);
                    if (cloneResult.Success)
                    {
                        tracer.AddLogFileEventListener(
                            GVFSEnlistment.GetNewGVFSLogFileName(enlistment.GVFSLogsRoot, GVFSConstants.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", GVFSPlatform.Instance.IsElevated() },
                            { "NamedPipeName", enlistment.NamedPipeName },
                            { 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 GVFS 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));
                        serverGVFSConfig = this.QueryGVFSConfig(tracer, enlistment, retryConfig);

                        cacheServer = this.ResolveCacheServer(tracer, cacheServer, cacheServerResolver, serverGVFSConfig);

                        this.ValidateClientVersions(tracer, enlistment, serverGVFSConfig, showWarnings: true);

                        this.ShowStatusWhileRunning(
                            () =>
                        {
                            cloneResult = this.TryClone(tracer, enlistment, cacheServer, retryConfig, serverGVFSConfig, 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.ServerGVFSConfig    = serverGVFSConfig;
                        });

                        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.DownloadedGVFSConfig = serverGVFSConfig;
                        });
                    }
                }
                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);
        }