private bool IsIncludePath(GitVariableName key, string configFilePath) { // unconditional: if (key.Equals(new GitVariableName("include", "", "path"))) { return(true); } // conditional: if (GitVariableName.SectionNameComparer.Equals(key.SectionName, "includeIf") && GitVariableName.VariableNameComparer.Equals(key.VariableName, "path") && key.SubsectionName != "") { bool ignoreCase; string pattern; const string caseSensitiveGitDirPrefix = "gitdir:"; const string caseInsensitiveGitDirPrefix = "gitdir/i:"; if (key.SubsectionName.StartsWith(caseSensitiveGitDirPrefix, StringComparison.Ordinal)) { pattern = key.SubsectionName.Substring(caseSensitiveGitDirPrefix.Length); ignoreCase = false; } else if (key.SubsectionName.StartsWith(caseInsensitiveGitDirPrefix, StringComparison.Ordinal)) { pattern = key.SubsectionName.Substring(caseInsensitiveGitDirPrefix.Length); ignoreCase = true; } else { return(false); } if (pattern.Length >= 2 && pattern[0] == '.' && pattern[1] == '/') { // leading './' is substituted with the path to the directory containing the current config file. pattern = PathUtils.CombinePosixPaths(PathUtils.ToPosixPath(Path.GetDirectoryName(configFilePath) !), pattern.Substring(2)); } else if (pattern.Length >= 2 && pattern[0] == '~' && pattern[1] == '/') { // leading '~/' is substituted with HOME path pattern = PathUtils.CombinePosixPaths(PathUtils.ToPosixPath(_environment.GetHomeDirectoryForPathExpansion(pattern)), pattern.Substring(2)); } else if (!PathUtils.IsAbsolute(pattern)) { pattern = "**/" + pattern; } if (pattern[pattern.Length - 1] == '/') { pattern += "**"; } return(Glob.IsMatch(pattern, _gitDirectoryPosix, ignoreCase, matchWildCardWithDirectorySeparator: true)); } return(false); }
/// <exception cref="InvalidDataException"/> private string NormalizeRelativePath(string relativePath, string basePath, GitVariableName key) { string root; if (relativePath.Length >= 2 && relativePath[0] == '~' && PathUtils.IsDirectorySeparator(relativePath[1])) { root = _environment.GetHomeDirectoryForPathExpansion(relativePath); relativePath = relativePath.Substring(2); } else { root = Path.GetDirectoryName(basePath) ?? ""; } try { return(Path.GetFullPath(Path.Combine(root, relativePath))); } catch { throw new InvalidDataException(string.Format(Resources.ValueOfIsNotValidPath, key.ToString(), relativePath)); } }
/// <exception cref="IOException"/> /// <exception cref="InvalidDataException"/> internal void LoadVariablesFrom(string path, Dictionary <GitVariableName, List <string> > variables, int includeDepth) { // https://git-scm.com/docs/git-config#_syntax // The following is allowed: // [section][section]var = x // [section]#[section] if (includeDepth > MaxIncludeDepth) { throw new InvalidDataException(string.Format(Resources.ConfigurationFileRecursionExceededMaximumAllowedDepth, MaxIncludeDepth)); } TextReader reader; try { reader = _fileOpener(path); } catch (Exception e) when(e is FileNotFoundException || e is DirectoryNotFoundException) { return; } catch (Exception e) when(!(e is IOException)) { throw new IOException(e.Message, e); } using (reader) { string sectionName = ""; string subsectionName = ""; while (true) { SkipMultilineWhitespace(reader); int c = reader.Peek(); if (c == -1) { break; } // Comment to the end of the line: if (IsCommentStart(c)) { ReadToLineEnd(reader); continue; } if (c == '[') { ReadSectionHeader(reader, _reusableBuffer, out sectionName, out subsectionName); continue; } ReadVariableDeclaration(reader, _reusableBuffer, out var variableName, out var variableValue); // Variable declared outside of a section is allowed (has no section name prefix). var key = new GitVariableName(sectionName, subsectionName, variableName); if (!variables.TryGetValue(key, out var values)) { variables.Add(key, values = new List <string>()); } values.Add(variableValue); // Spec https://git-scm.com/docs/git-config#_includes: if (IsIncludePath(key, path)) { string includedConfigPath = NormalizeRelativePath(relativePath: variableValue, basePath: path, key); LoadVariablesFrom(includedConfigPath, variables, includeDepth + 1); } } } }