private GSDEnlistment CreateEnlistment(string enlistmentRootPath)
        {
            string gitBinPath = GSDPlatform.Instance.GitInstallation.GetInstalledGitBinPath();

            if (string.IsNullOrWhiteSpace(gitBinPath))
            {
                this.ReportErrorAndExit("Error: " + GSDConstants.GitIsNotInstalledError);
            }

            GSDEnlistment enlistment = null;

            try
            {
                enlistment = GSDEnlistment.CreateFromDirectory(enlistmentRootPath, gitBinPath, ProcessHelper.GetCurrentProcessLocation(), authentication: null);
            }
            catch (InvalidRepoException e)
            {
                this.ReportErrorAndExit(
                    "Error: '{0}' is not a valid GSD enlistment. {1}",
                    enlistmentRootPath,
                    e.Message);
            }

            return(enlistment);
        }
        protected bool TrySetGitConfig(ITracer tracer, string enlistmentRoot, Dictionary <string, string> configSettings)
        {
            GSDEnlistment enlistment;

            try
            {
                enlistment = GSDEnlistment.CreateFromDirectory(
                    enlistmentRoot,
                    GSDPlatform.Instance.GitInstallation.GetInstalledGitBinPath(),
                    ProcessHelper.GetCurrentProcessLocation(),
                    authentication: null);
            }
            catch (InvalidRepoException e)
            {
                EventMetadata metadata = new EventMetadata();
                metadata.Add("Exception", e.ToString());
                metadata.Add(nameof(enlistmentRoot), enlistmentRoot);
                tracer.RelatedError(metadata, $"{nameof(this.TrySetGitConfig)}: Failed to create GSDEnlistment from directory");
                return(false);
            }

            GitProcess git = enlistment.CreateGitProcess();

            foreach (string key in configSettings.Keys)
            {
                GitProcess.Result result = git.SetInLocalConfig(key, configSettings[key]);
                if (result.ExitCodeIsFailure)
                {
                    tracer.RelatedError("Could not set git config setting {0}. Error: {1}", key, result.Errors);
                    return(false);
                }
            }

            return(true);
        }
        public override IssueType HasIssue(List <string> messages)
        {
            GitProcess git = new GitProcess(this.Enlistment);

            GitProcess.ConfigResult originResult = git.GetOriginUrl();
            string error;
            string originUrl;

            if (!originResult.TryParseAsString(out originUrl, out error))
            {
                if (error.Contains("--local"))
                {
                    // example error: '--local can only be used inside a git repository'
                    // Corrupting the git config does not cause git to not recognize the current folder as "not a git repository".
                    // This is a symptom of deeper issues such as missing HEAD file or refs folders.
                    messages.Add("An issue was found that may be a side-effect of other issues. Fix them with 'gvfs repair --confirm' then 'gvfs repair' again.");
                    return(IssueType.CantFix);
                }

                messages.Add("Could not read origin url: " + error);
                return(IssueType.Fixable);
            }

            if (originUrl == null)
            {
                messages.Add("Remote 'origin' is not configured for this repo. You can fix this by running 'git remote add origin <repourl>'");
                return(IssueType.CantFix);
            }

            // We've validated the repo URL, so now make sure we can authenticate
            try
            {
                GSDEnlistment enlistment = GSDEnlistment.CreateFromDirectory(
                    this.Enlistment.EnlistmentRoot,
                    this.Enlistment.GitBinPath,
                    this.Enlistment.GSDHooksRoot,
                    authentication: null);

                string authError;
                if (!enlistment.Authentication.TryInitialize(this.Tracer, enlistment, out authError))
                {
                    messages.Add("Authentication failed. Run 'gvfs log' for more info.");
                    messages.Add(".git\\config is valid and remote 'origin' is set, but may have a typo:");
                    messages.Add(originUrl.Trim());
                    return(IssueType.CantFix);
                }
            }
            catch (InvalidRepoException)
            {
                messages.Add("An issue was found that may be a side-effect of other issues. Fix them with 'gvfs repair --confirm' then 'gvfs repair' again.");
                return(IssueType.CantFix);
            }

            return(IssueType.None);
        }
Exemple #4
0
            private GSDEnlistment CreateEnlistment(string enlistmentRootPath, GitAuthentication authentication)
            {
                string gitBinPath = GSDPlatform.Instance.GitInstallation.GetInstalledGitBinPath();

                if (string.IsNullOrWhiteSpace(gitBinPath))
                {
                    this.ReportErrorAndExit("Error: " + GSDConstants.GitIsNotInstalledError);
                }

                string hooksPath = null;

                if (GSDPlatform.Instance.UnderConstruction.RequiresDeprecatedGitHooksLoader)
                {
                    // On Windows, the soon-to-be deprecated GitHooksLoader tries to call out to the hooks process without
                    // its full path, so we have to pass the path along to our background git processes via the PATH
                    // environment variable. On Mac this is not needed because we just copy our own hook directly into
                    // the .git/hooks folder, and once Windows does the same, this hooksPath can be removed (from here
                    // and all the classes that handle it on the way to GitProcess)

                    hooksPath = ProcessHelper.GetProgramLocation(GSDPlatform.Instance.Constants.ProgramLocaterCommand, GSDPlatform.Instance.Constants.GSDHooksExecutableName);
                    if (hooksPath == null)
                    {
                        this.ReportErrorAndExit("Could not find " + GSDPlatform.Instance.Constants.GSDHooksExecutableName);
                    }
                }

                GSDEnlistment enlistment = null;

                try
                {
                    enlistment = GSDEnlistment.CreateFromDirectory(
                        enlistmentRootPath,
                        gitBinPath,
                        hooksPath,
                        authentication,
                        createWithoutRepoURL: !this.validateOriginURL);
                }
                catch (InvalidRepoException e)
                {
                    this.ReportErrorAndExit(
                        "Error: '{0}' is not a valid GSD enlistment. {1}",
                        enlistmentRootPath,
                        e.Message);
                }

                return(enlistment);
            }
        private bool IsValidRepo(string repoRoot)
        {
            string gitBinPath = GSDPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
            string hooksPath  = null;

            string hooksVersion = null;
            string error        = null;

            if (GSDPlatform.Instance.TryGetGSDHooksPathAndVersion(out hooksPath, out hooksVersion, out error))
            {
                try
                {
                    GSDEnlistment enlistment = GSDEnlistment.CreateFromDirectory(
                        repoRoot,
                        gitBinPath,
                        hooksPath,
                        authentication: null);
                }
                catch (InvalidRepoException e)
                {
                    EventMetadata metadata = new EventMetadata();
                    metadata.Add(nameof(repoRoot), repoRoot);
                    metadata.Add(nameof(gitBinPath), gitBinPath);
                    metadata.Add(nameof(hooksPath), hooksPath);
                    metadata.Add("Exception", e.ToString());
                    this.tracer.RelatedInfo(metadata, $"{nameof(this.IsValidRepo)}: Found invalid repo");

                    return(false);
                }
            }
            else
            {
                this.tracer.RelatedError($"{nameof(this.IsValidRepo)}: {nameof(GSDPlatform.Instance.TryGetGSDHooksPathAndVersion)} failed. {error}");
                return(false);
            }

            return(true);
        }
        public override void Execute()
        {
            this.ValidatePathParameter(this.EnlistmentRootPathParameter);

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

            if (!Directory.Exists(this.EnlistmentRootPathParameter))
            {
                this.ReportErrorAndExit($"Path '{this.EnlistmentRootPathParameter}' does not exist");
            }

            string errorMessage;
            string enlistmentRoot;

            if (!GSDPlatform.Instance.TryGetGSDEnlistmentRoot(this.EnlistmentRootPathParameter, out enlistmentRoot, out errorMessage))
            {
                this.ReportErrorAndExit("'gvfs repair' must be run within a GSD enlistment");
            }

            GSDEnlistment enlistment = null;

            try
            {
                enlistment = GSDEnlistment.CreateFromDirectory(
                    this.EnlistmentRootPathParameter,
                    GSDPlatform.Instance.GitInstallation.GetInstalledGitBinPath(),
                    hooksPath,
                    authentication: null,
                    createWithoutRepoURL: true);
            }
            catch (InvalidRepoException e)
            {
                this.ReportErrorAndExit($"Failed to initialize enlistment, error: {e.Message}");
            }

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

This command detects and repairs issues that prevent a GSD 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 GSD is not mounted",
                    this.Output,
                    showSpinner: true,
                    gvfsLogEnlistmentRoot: null))
            {
                this.ReportErrorAndExit("You can only run 'gvfs repair' if GSD is not mounted. Run 'gvfs unmount' and try again.");
            }

            this.Output.WriteLine();

            using (JsonTracer tracer = new JsonTracer(GSDConstants.GSDEtwProviderName, "RepairVerb", enlistment.GetEnlistmentId(), mountId: null))
            {
                tracer.AddLogFileEventListener(
                    GSDEnlistment.GetNewGSDLogFileName(enlistment.GSDLogsRoot, GSDConstants.LogFileTypes.Repair),
                    EventLevel.Verbose,
                    Keywords.Any);
                tracer.WriteStartEvent(
                    enlistment.EnlistmentRoot,
                    enlistment.RepoUrl,
                    "N/A",
                    new EventMetadata
                {
                    { "Confirmed", this.Confirmed },
                    { "IsElevated", GSDPlatform.Instance.IsElevated() },
                    { "NamedPipename", enlistment.NamedPipeName },
                    { nameof(this.EnlistmentRootPathParameter), this.EnlistmentRootPathParameter },
                });

                List <RepairJob> jobs = new List <RepairJob>();

                // Repair databases
                jobs.Add(new RepoMetadataDatabaseRepairJob(tracer, this.Output, enlistment));

                // Repair .git folder files
                jobs.Add(new GitHeadRepairJob(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.GetGSDLogMessage(enlistment.EnlistmentRoot));
                            break;
                        }

                        this.WriteMessages(tracer, repairMessages);
                    }
                    else
                    {
                        this.WriteMessage(tracer, "Run 'gvfs repair --confirm' to attempt a repair");
                    }

                    this.Output.WriteLine();
                }
            }
        }