/// <summary>
        /// Finds and returns paths to Git installations in common locations.
        /// </summary>
        /// <param name="hints">(optional) List of paths the caller believes Git can be found.</param>
        /// <param name="paths">
        /// All discovered paths to the root of Git installations, ordered by 'priority' with first
        /// being the best installation to use when shelling out to Git.exe.
        /// </param>
        /// <returns><see langword="True"/> if Git was detected; <see langword="false"/> otherwise.</returns>
        public static bool FindGitInstallations(out List <GitInstallation> installations)
        {
            const string GitAppName    = @"Git";
            const string GitSubkeyName = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Git_is1";
            const string GitValueName  = "InstallLocation";

            installations = null;

            var programFiles32Path = String.Empty;
            var programFiles64Path = String.Empty;
            var appDataRoamingPath = String.Empty;
            var appDataLocalPath   = String.Empty;
            var programDataPath    = String.Empty;
            var reg32HklmPath      = String.Empty;
            var reg64HklmPath      = String.Empty;
            var reg32HkcuPath      = String.Empty;
            var reg64HkcuPath      = String.Empty;
            var shellPathValue     = String.Empty;

            RegistryKey reg32HklmKey = null;
            RegistryKey reg64HklmKey = null;
            RegistryKey reg32HkcuKey = null;
            RegistryKey reg64HkcuKey = null;

            if ((reg32HklmKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) != null)
            {
                if ((reg32HklmKey = reg32HklmKey.OpenSubKey(GitSubkeyName)) != null)
                {
                    reg32HklmPath = reg32HklmKey.GetValue(GitValueName, reg32HklmPath) as String;
                }
            }

            if ((reg32HkcuKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32)) != null)
            {
                if ((reg32HkcuKey = reg32HkcuKey.OpenSubKey(GitSubkeyName)) != null)
                {
                    reg32HkcuPath = reg32HkcuKey.GetValue(GitValueName, reg32HkcuPath) as String;
                }
            }

            if ((programFiles32Path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)) != null)
            {
                programFiles32Path = Path.Combine(programFiles32Path, GitAppName);
            }

            if (Environment.Is64BitOperatingSystem)
            {
                if ((reg64HklmKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)) != null)
                {
                    if ((reg64HklmKey = reg64HklmKey.OpenSubKey(GitSubkeyName)) != null)
                    {
                        reg64HklmPath = reg64HklmKey.GetValue(GitValueName, reg64HklmPath) as String;
                    }
                }

                if ((reg64HkcuKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)) != null)
                {
                    if ((reg64HkcuKey = reg64HkcuKey.OpenSubKey(GitSubkeyName)) != null)
                    {
                        reg64HkcuPath = reg64HkcuKey.GetValue(GitValueName, reg64HkcuPath) as String;
                    }
                }

                // since .NET returns %ProgramFiles% as %ProgramFilesX86% when the app is 32-bit
                // manual manipulation of the path is required

                if ((programFiles64Path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)) != null)
                {
                    programFiles64Path = programFiles64Path.Substring(0, programFiles64Path.Length - 6);
                    programFiles64Path = Path.Combine(programFiles64Path, GitAppName);
                }
            }

            if ((appDataRoamingPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)) != null)
            {
                appDataRoamingPath = Path.Combine(appDataRoamingPath, GitAppName);
            }

            if ((appDataLocalPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)) != null)
            {
                appDataLocalPath = Path.Combine(appDataLocalPath, GitAppName);
            }

            if ((programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)) != null)
            {
                programDataPath = Path.Combine(programDataPath, GitAppName);
            }

            List <GitInstallation> candidates = new List <GitInstallation>();

            // add candidate locations in order of preference
            if (Where.FindApp(GitAppName, out shellPathValue))
            {
                // `Where.App` returns the path to the executable, truncate to the installation root
                if (shellPathValue.EndsWith(GitInstallation.AllVersionCmdPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    shellPathValue = shellPathValue.Substring(0, shellPathValue.Length - GitInstallation.AllVersionCmdPath.Length);
                }

                candidates.Add(new GitInstallation(shellPathValue, KnownGitDistribution.GitForWindows64v2));
                candidates.Add(new GitInstallation(shellPathValue, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(shellPathValue, KnownGitDistribution.GitForWindows32v1));
            }

            if (!String.IsNullOrEmpty(reg64HklmPath))
            {
                candidates.Add(new GitInstallation(reg64HklmPath, KnownGitDistribution.GitForWindows64v2));
            }
            if (!String.IsNullOrEmpty(programFiles32Path))
            {
                candidates.Add(new GitInstallation(programFiles64Path, KnownGitDistribution.GitForWindows64v2));
            }
            if (!String.IsNullOrEmpty(reg64HkcuPath))
            {
                candidates.Add(new GitInstallation(reg64HkcuPath, KnownGitDistribution.GitForWindows64v2));
            }
            if (!String.IsNullOrEmpty(reg32HklmPath))
            {
                candidates.Add(new GitInstallation(reg32HklmPath, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(reg32HklmPath, KnownGitDistribution.GitForWindows32v1));
            }
            if (!String.IsNullOrEmpty(programFiles32Path))
            {
                candidates.Add(new GitInstallation(programFiles32Path, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(programFiles32Path, KnownGitDistribution.GitForWindows32v1));
            }
            if (!String.IsNullOrEmpty(reg32HkcuPath))
            {
                candidates.Add(new GitInstallation(reg32HkcuPath, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(reg32HkcuPath, KnownGitDistribution.GitForWindows32v1));
            }
            if (!String.IsNullOrEmpty(programDataPath))
            {
                candidates.Add(new GitInstallation(programDataPath, KnownGitDistribution.GitForWindows64v2));
                candidates.Add(new GitInstallation(programDataPath, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(programDataPath, KnownGitDistribution.GitForWindows32v1));
            }
            if (!String.IsNullOrEmpty(appDataLocalPath))
            {
                candidates.Add(new GitInstallation(appDataLocalPath, KnownGitDistribution.GitForWindows64v2));
                candidates.Add(new GitInstallation(appDataLocalPath, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(appDataLocalPath, KnownGitDistribution.GitForWindows32v1));
            }
            if (!String.IsNullOrEmpty(appDataRoamingPath))
            {
                candidates.Add(new GitInstallation(appDataRoamingPath, KnownGitDistribution.GitForWindows64v2));
                candidates.Add(new GitInstallation(appDataRoamingPath, KnownGitDistribution.GitForWindows32v2));
                candidates.Add(new GitInstallation(appDataRoamingPath, KnownGitDistribution.GitForWindows32v1));
            }

            HashSet <GitInstallation> pathSet = new HashSet <GitInstallation>();

            foreach (var candidate in candidates)
            {
                if (GitInstallation.IsValid(candidate))
                {
                    pathSet.Add(candidate);
                }
            }

            installations = pathSet.ToList();

            Git.Trace.WriteLine($"found {installations.Count} Git installation(s).");

            return(installations.Count > 0);
        }
Esempio n. 2
0
        internal static void ParseGitConfig(TextReader reader, IDictionary <string, string> destination)
        {
            Debug.Assert(reader != null, $"The `{nameof(reader)}` parameter is null.");
            Debug.Assert(destination != null, $"The `{nameof(destination)}` parameter is null.");

            Match  match   = null;
            string section = null;

            // parse each line in the config independently - Git's configs do not accept multi-line values
            string line;

            while ((line = reader.ReadLine()) != null)
            {
                // skip empty and commented lines
                if (String.IsNullOrWhiteSpace(line))
                {
                    continue;
                }
                if (CommentRegex.Value.IsMatch(line))
                {
                    continue;
                }

                // sections begin with values like [section] or [section "section name"]. All subsequent lines,
                // until a new section is encountered, are children of the section
                if ((match = SectionRegex.Value.Match(line)).Success)
                {
                    if (match.Groups.Count >= 2 && !String.IsNullOrWhiteSpace(match.Groups[1].Value))
                    {
                        section = match.Groups[1].Value.Trim();

                        // check if the section is named, if so: process the name
                        if (match.Groups.Count >= 3 && !String.IsNullOrWhiteSpace(match.Groups[2].Value))
                        {
                            string val = match.Groups[2].Value.Trim();

                            // triming off enclosing quotes makes usage easier, only trim in pairs
                            if (val.Length > 0 && val[0] == '"')
                            {
                                if (val[val.Length - 1] == '"' && val.Length > 1)
                                {
                                    val = val.Substring(1, val.Length - 2);
                                }
                                else
                                {
                                    val = val.Substring(1, val.Length - 1);
                                }
                            }

                            section += HostSplitCharacter + val;
                        }
                    }
                }
                // section children should be in the format of name = value pairs
                else if ((match = KeyValueRegex.Value.Match(line)).Success)
                {
                    if (match.Groups.Count >= 3 &&
                        !String.IsNullOrEmpty(match.Groups[1].Value) &&
                        !String.IsNullOrEmpty(match.Groups[2].Value))
                    {
                        string key = section + HostSplitCharacter + match.Groups[1].Value.Trim();
                        string val = match.Groups[2].Value.Trim();

                        // triming off enclosing quotes makes usage easier, only trim in pairs
                        if (val.Length > 0 && val[0] == '"')
                        {
                            if (val[val.Length - 1] == '"' && val.Length > 1)
                            {
                                val = val.Substring(1, val.Length - 2);
                            }
                            else
                            {
                                val = val.Substring(1, val.Length - 1);
                            }
                        }

                        // Test for and handle include directives
                        if ("include.path".Equals(key))
                        {
                            try
                            {
                                // This is an include directive, import the configuration values from the included file
                                string includePath = (val.StartsWith("~/", StringComparison.OrdinalIgnoreCase))
                                    ? Where.Home() + val.Substring(1, val.Length - 1)
                                    : val;

                                includePath = Path.GetFullPath(includePath);

                                using (var includeFile = File.Open(includePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                                    using (var includeReader = new StreamReader(includeFile))
                                    {
                                        ParseGitConfig(includeReader, destination);
                                    }
                            }
                            catch (Exception exception)
                            {
                                Trace.WriteLine($"failed to parse config file: {val}. {exception.Message}");
                            }
                        }
                        else
                        {
                            // Add or update the (key, value)
                            if (destination.ContainsKey(key))
                            {
                                destination[key] = val;
                            }
                            else
                            {
                                destination.Add(key, val);
                            }
                        }
                    }
                }
            }
        }