/GitCommands/Git/GitModule.cs
C# | 2952 lines | 2349 code | 452 blank | 151 comment | 407 complexity | 1d38ab80e40c5c5ea14e33e4463ef8de MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Net.Mail;
- using System.Security.Permissions;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using GitCommands.Config;
- using GitCommands.Settings;
- using GitCommands.Utils;
- using GitUIPluginInterfaces;
- using JetBrains.Annotations;
- using PatchApply;
- using SmartFormat;
- namespace GitCommands
- {
- public delegate void GitModuleChangedEventHandler(GitModule module);
- public enum SubmoduleStatus
- {
- Unknown,
- NewSubmodule,
- FastForward,
- Rewind,
- NewerTime,
- OlderTime,
- SameTime
- }
- /// <summary>Provides manipulation with git module.
- /// <remarks>Several instances may be created for submodules.</remarks></summary>
- [DebuggerDisplay("GitModule ( {_workingDir} )")]
- public sealed class GitModule : IGitModule
- {
- private static readonly Regex DefaultHeadPattern = new Regex("refs/remotes/[^/]+/HEAD", RegexOptions.Compiled);
- private readonly object _lock = new object();
- public GitModule(string workingdir)
- {
- _superprojectInit = false;
- _workingDir = (workingdir ?? "").EnsureTrailingPathSeparator();
- }
- #region IGitCommands
- [NotNull] private readonly string _workingDir;
- [NotNull]
- public string WorkingDir
- {
- get
- {
- return _workingDir;
- }
- }
- /// <summary>Gets the path to the git application executable.</summary>
- public string GitCommand
- {
- get
- {
- return AppSettings.GitCommand;
- }
- }
- public Version AppVersion
- {
- get
- {
- return AppSettings.AppVersion;
- }
- }
- public string GravatarCacheDir
- {
- get
- {
- return AppSettings.GravatarCachePath;
- }
- }
- #endregion
- private bool _superprojectInit;
- private GitModule _superprojectModule;
- private string _submoduleName;
- private string _submodulePath;
- public string SubmoduleName
- {
- get
- {
- InitSuperproject();
- return _submoduleName;
- }
- }
- public string SubmodulePath
- {
- get
- {
- InitSuperproject();
- return _submodulePath;
- }
- }
- public GitModule SuperprojectModule
- {
- get
- {
- InitSuperproject();
- return _superprojectModule;
- }
- }
- private void InitSuperproject()
- {
- if (!_superprojectInit)
- {
- string superprojectDir = FindGitSuperprojectPath(out _submoduleName, out _submodulePath);
- _superprojectModule = superprojectDir == null ? null : new GitModule(superprojectDir);
- _superprojectInit = true;
- }
- }
- public GitModule FindTopProjectModule()
- {
- GitModule module = SuperprojectModule;
- if (module == null)
- return null;
- do
- {
- if (module.SuperprojectModule == null)
- return module;
- module = module.SuperprojectModule;
- } while (module != null);
- return module;
- }
- private RepoDistSettings _settings;
- public RepoDistSettings Settings
- {
- get
- {
- lock (_lock)
- {
- if (_settings == null)
- _settings = RepoDistSettings.CreateEffective(this);
- }
- return _settings;
- }
- }
- private ConfigFileSettings _effectiveConfigFile;
- public ConfigFileSettings EffectiveConfigFile
- {
- get
- {
- lock (_lock)
- {
- if (_effectiveConfigFile == null)
- _effectiveConfigFile = ConfigFileSettings.CreateEffective(this);
- }
- return _effectiveConfigFile;
- }
- }
- public ConfigFileSettings LocalConfigFile
- {
- get
- {
- return new ConfigFileSettings(null, EffectiveConfigFile.SettingsCache);
- }
- }
- //encoding for files paths
- private static Encoding _systemEncoding;
- public static Encoding SystemEncoding
- {
- get
- {
- if (_systemEncoding == null)
- {
- //check whether GitExtensions works with standard msysgit or msysgit-unicode
- // invoke a git command that returns an invalid argument in its response, and
- // check if a unicode-only character is reported back. If so assume msysgit-unicode
- // git config --get with a malformed key (no section) returns:
- // "error: key does not contain a section: <key>"
- const string controlStr = "ą"; // "a caudata"
- string arguments = string.Format("config --get {0}", controlStr);
- String s = new GitModule("").RunGitCmd(arguments, Encoding.UTF8);
- if (s != null && s.IndexOf(controlStr) != -1)
- _systemEncoding = new UTF8Encoding(false);
- else
- _systemEncoding = Encoding.Default;
- Debug.WriteLine("System encoding: " + _systemEncoding.EncodingName);
- }
- return _systemEncoding;
- }
- }
- //Encoding that let us read all bytes without replacing any char
- //It is using to read output of commands, which may consist of:
- //1) commit header (message, author, ...) encoded in CommitEncoding, recoded to LogOutputEncoding or not dependent of
- // pretty parameter (pretty=raw - recoded, pretty=format - not recoded)
- //2) file content encoded in its original encoding
- //3) file path (file name is encoded in system default encoding),
- // when core.quotepath is on, every non ASCII character is escaped
- // with \ followed by its code as a three digit octal number
- //4) branch, tag name, errors, warnings, hints encoded in system default encoding
- public static readonly Encoding LosslessEncoding = Encoding.GetEncoding("ISO-8859-1");//is any better?
- public Encoding FilesEncoding
- {
- get
- {
- Encoding result = EffectiveConfigFile.FilesEncoding;
- if (result == null)
- result = new UTF8Encoding(false);
- return result;
- }
- }
- public Encoding CommitEncoding
- {
- get
- {
- Encoding result = EffectiveConfigFile.CommitEncoding;
- if (result == null)
- result = new UTF8Encoding(false);
- return result;
- }
- }
- /// <summary>
- /// Encoding for commit header (message, notes, author, commiter, emails)
- /// </summary>
- public Encoding LogOutputEncoding
- {
- get
- {
- Encoding result = EffectiveConfigFile.LogOutputEncoding;
- if (result == null)
- result = CommitEncoding;
- return result;
- }
- }
- /// <summary>"(no branch)"</summary>
- public static readonly string DetachedBranch = "(no branch)";
- private static readonly string[] DetachedPrefixes = { "(no branch", "(detached from " };
- public AppSettings.PullAction LastPullAction
- {
- get { return AppSettings.GetEnum("LastPullAction_" + WorkingDir, AppSettings.PullAction.None); }
- set { AppSettings.SetEnum("LastPullAction_" + WorkingDir, value); }
- }
- public void LastPullActionToFormPullAction()
- {
- if (LastPullAction == AppSettings.PullAction.FetchAll)
- AppSettings.FormPullAction = AppSettings.PullAction.Fetch;
- else if (LastPullAction != AppSettings.PullAction.None)
- AppSettings.FormPullAction = LastPullAction;
- }
- /// <summary>Indicates whether the <see cref="WorkingDir"/> contains a git repository.</summary>
- public bool IsValidGitWorkingDir()
- {
- return IsValidGitWorkingDir(_workingDir);
- }
- /// <summary>Indicates whether the specified directory contains a git repository.</summary>
- public static bool IsValidGitWorkingDir(string dir)
- {
- if (string.IsNullOrEmpty(dir))
- return false;
- string dirPath = dir.EnsureTrailingPathSeparator();
- string path = dirPath + ".git";
- if (Directory.Exists(path) || File.Exists(path))
- return true;
- return Directory.Exists(dirPath + "info") &&
- Directory.Exists(dirPath + "objects") &&
- Directory.Exists(dirPath + "refs");
- }
- /// <summary>Gets the ".git" directory path.</summary>
- public string GetGitDirectory()
- {
- return GetGitDirectory(_workingDir);
- }
- public static string GetGitDirectory(string repositoryPath)
- {
- var gitpath = Path.Combine(repositoryPath, ".git");
- if (File.Exists(gitpath))
- {
- var lines = File.ReadLines(gitpath);
- foreach (string line in lines)
- {
- if (line.StartsWith("gitdir:"))
- {
- string path = line.Substring(7).Trim().Replace('/', '\\');
- if (Path.IsPathRooted(path))
- return path.EnsureTrailingPathSeparator();
- else
- return
- Path.GetFullPath(Path.Combine(repositoryPath,
- path.EnsureTrailingPathSeparator()));
- }
- }
- }
- gitpath = gitpath.EnsureTrailingPathSeparator();
- if (!Directory.Exists(gitpath))
- return repositoryPath;
- return gitpath;
- }
- public bool IsBareRepository()
- {
- return WorkingDir == GetGitDirectory();
- }
- public static bool IsBareRepository(string repositoryPath)
- {
- return repositoryPath == GetGitDirectory(repositoryPath);
- }
- public bool HasSubmodules()
- {
- return GetSubmodulesLocalPathes(recursive: false).Any();
- }
- /// <summary>
- /// This is a faster function to get the names of all submodules then the
- /// GetSubmodules() function. The command @git submodule is very slow.
- /// </summary>
- public IList<string> GetSubmodulesLocalPathes(bool recursive = true)
- {
- var configFile = GetSubmoduleConfigFile();
- var submodules = configFile.ConfigSections.Select(configSection => configSection.GetPathValue("path").Trim()).ToList();
- if (recursive)
- {
- for (int i = 0; i < submodules.Count; i++)
- {
- var submodule = GetSubmodule(submodules[i]);
- var submoduleConfigFile = submodule.GetSubmoduleConfigFile();
- var subsubmodules = submoduleConfigFile.ConfigSections.Select(configSection => configSection.GetPathValue("path").Trim()).ToList();
- for (int j = 0; j < subsubmodules.Count; j++)
- subsubmodules[j] = submodules[i] + '/' + subsubmodules[j];
- submodules.InsertRange(i + 1, subsubmodules);
- i += subsubmodules.Count;
- }
- }
- return submodules;
- }
- public static string FindGitWorkingDir(string startDir)
- {
- if (string.IsNullOrEmpty(startDir))
- return "";
- var dir = startDir.Trim();
- do
- {
- if (IsValidGitWorkingDir(dir))
- return dir.EnsureTrailingPathSeparator();
- dir = PathUtil.GetDirectoryName(dir);
- }
- while (!string.IsNullOrEmpty(dir));
- return startDir;
- }
- private static Process StartProccess(string fileName, string arguments, string workingDir, bool showConsole)
- {
- GitCommandHelpers.SetEnvironmentVariable();
- string quotedCmd = fileName;
- if (quotedCmd.IndexOf(' ') != -1)
- quotedCmd = quotedCmd.Quote();
- AppSettings.GitLog.Log(quotedCmd + " " + arguments);
- var startInfo = new ProcessStartInfo
- {
- FileName = fileName,
- Arguments = arguments,
- WorkingDirectory = workingDir
- };
- if (!showConsole)
- {
- startInfo.UseShellExecute = false;
- startInfo.CreateNoWindow = true;
- }
- return Process.Start(startInfo);
- }
- /// <summary>
- /// Run command, console window is visible
- /// </summary>
- public Process RunExternalCmdDetachedShowConsole(string cmd, string arguments)
- {
- try
- {
- return StartProccess(cmd, arguments, _workingDir, showConsole: true);
- }
- catch (Exception ex)
- {
- Trace.WriteLine(ex.Message);
- }
- return null;
- }
- /// <summary>
- /// Run command, console window is visible, wait for exit
- /// </summary>
- public void RunExternalCmdShowConsole(string cmd, string arguments)
- {
- try
- {
- using (var process = StartProccess(cmd, arguments, _workingDir, showConsole: true))
- process.WaitForExit();
- }
- catch (Exception ex)
- {
- Trace.WriteLine(ex.Message);
- }
- }
- /// <summary>
- /// Run command, console window is hidden
- /// </summary>
- public static Process RunExternalCmdDetached(string fileName, string arguments, string workingDir)
- {
- try
- {
- return StartProccess(fileName, arguments, workingDir, showConsole: false);
- }
- catch (Exception ex)
- {
- Trace.WriteLine(ex.Message);
- }
- return null;
- }
- /// <summary>
- /// Run command, console window is hidden
- /// </summary>
- public Process RunExternalCmdDetached(string cmd, string arguments)
- {
- return RunExternalCmdDetached(cmd, arguments, _workingDir);
- }
- /// <summary>
- /// Run git command, console window is hidden, redirect output
- /// </summary>
- public Process RunGitCmdDetached(string arguments, Encoding encoding = null)
- {
- if (encoding == null)
- encoding = SystemEncoding;
- return GitCommandHelpers.StartProcess(AppSettings.GitCommand, arguments, _workingDir, encoding);
- }
- /// <summary>
- /// Run command, cache results, console window is hidden, wait for exit, redirect output
- /// </summary>
- [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
- public string RunCacheableCmd(string cmd, string arguments = "", Encoding encoding = null)
- {
- if (encoding == null)
- encoding = SystemEncoding;
- byte[] cmdout, cmderr;
- if (GitCommandCache.TryGet(arguments, out cmdout, out cmderr))
- return EncodingHelper.DecodeString(cmdout, cmderr, ref encoding);
- GitCommandHelpers.RunCmdByte(cmd, arguments, _workingDir, null, out cmdout, out cmderr);
- GitCommandCache.Add(arguments, cmdout, cmderr);
- return EncodingHelper.DecodeString(cmdout, cmderr, ref encoding);
- }
- /// <summary>
- /// Run command, console window is hidden, wait for exit, redirect output
- /// </summary>
- [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
- public CmdResult RunCmdResult(string cmd, string arguments, Encoding encoding = null, byte[] stdInput = null)
- {
- byte[] output, error;
- int exitCode = GitCommandHelpers.RunCmdByte(cmd, arguments, _workingDir, stdInput, out output, out error);
- if (encoding == null)
- encoding = SystemEncoding;
- return new CmdResult
- {
- StdOutput = output == null ? string.Empty : encoding.GetString(output),
- StdError = error == null ? string.Empty : encoding.GetString(error),
- ExitCode = exitCode
- };
- }
- /// <summary>
- /// Run command, console window is hidden, wait for exit, redirect output
- /// </summary>
- [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
- public string RunCmd(string cmd, string arguments, Encoding encoding = null, byte[] stdInput = null)
- {
- return RunCmdResult(cmd, arguments, encoding, stdInput).GetString();
- }
- /// <summary>
- /// Run git command, console window is hidden, wait for exit, redirect output
- /// </summary>
- public string RunGitCmd(string arguments, Encoding encoding = null, byte[] stdInput = null)
- {
- return RunCmd(AppSettings.GitCommand, arguments, encoding, stdInput);
- }
- /// <summary>
- /// Run git command, console window is hidden, wait for exit, redirect output
- /// </summary>
- public CmdResult RunGitCmdResult(string arguments, Encoding encoding = null, byte[] stdInput = null)
- {
- return RunCmdResult(AppSettings.GitCommand, arguments, encoding, stdInput);
- }
- /// <summary>
- /// Run command, console window is hidden, wait for exit, redirect output
- /// </summary>
- [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
- private IEnumerable<string> ReadCmdOutputLines(string cmd, string arguments, string stdInput)
- {
- return GitCommandHelpers.ReadCmdOutputLines(cmd, arguments, _workingDir, stdInput);
- }
- /// <summary>
- /// Run git command, console window is hidden, wait for exit, redirect output
- /// </summary>
- public IEnumerable<string> ReadGitOutputLines(string arguments)
- {
- return ReadCmdOutputLines(AppSettings.GitCommand, arguments, null);
- }
- /// <summary>
- /// Run batch file, console window is hidden, wait for exit, redirect output
- /// </summary>
- public string RunBatchFile(string batchFile)
- {
- string tempFileName = Path.ChangeExtension(Path.GetTempFileName(), ".cmd");
- using (var writer = new StreamWriter(tempFileName))
- {
- writer.WriteLine("@prompt $G");
- writer.Write(batchFile);
- }
- string result = RunCmd("cmd.exe", "/C \"" + tempFileName + "\"");
- File.Delete(tempFileName);
- return result;
- }
- public void EditNotes(string revision)
- {
- string editor = GetEffectivePathSetting("core.editor").ToLower();
- if (editor.Contains("gitextensions") || editor.Contains("notepad") ||
- editor.Contains("notepad++"))
- {
- RunGitCmd("notes edit " + revision);
- }
- else
- {
- RunExternalCmdShowConsole(AppSettings.GitCommand, "notes edit " + revision);
- }
- }
- public bool InTheMiddleOfConflictedMerge()
- {
- return !string.IsNullOrEmpty(RunGitCmd("ls-files -z --unmerged"));
- }
- public IList<GitItem> GetConflictedFiles()
- {
- var unmergedFiles = new List<GitItem>();
- var fileName = "";
- foreach (var file in GetUnmergedFileListing())
- {
- if (file.IndexOf('\t') <= 0)
- continue;
- if (file.Substring(file.IndexOf('\t') + 1) == fileName)
- continue;
- fileName = file.Substring(file.IndexOf('\t') + 1);
- unmergedFiles.Add(new GitItem(this) { FileName = fileName });
- }
- return unmergedFiles;
- }
- private IEnumerable<string> GetUnmergedFileListing()
- {
- return RunGitCmd("ls-files -z --unmerged").Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
- }
- public bool HandleConflictSelectSide(string fileName, string side)
- {
- Directory.SetCurrentDirectory(_workingDir);
- fileName = fileName.ToPosixPath();
- side = GetSide(side);
- string result = RunGitCmd(String.Format("checkout-index -f --stage={0} -- \"{1}\"", side, fileName));
- if (!result.IsNullOrEmpty())
- {
- return false;
- }
- result = RunGitCmd(String.Format("add -- \"{0}\"", fileName));
- return result.IsNullOrEmpty();
- }
- public bool HandleConflictsSaveSide(string fileName, string saveAsFileName, string side)
- {
- Directory.SetCurrentDirectory(_workingDir);
- fileName = fileName.ToPosixPath();
- side = GetSide(side);
- var result = RunGitCmd(String.Format("checkout-index --stage={0} --temp -- \"{1}\"", side, fileName));
- if (result.IsNullOrEmpty())
- {
- return false;
- }
- if (!result.StartsWith(".merge_file_"))
- {
- return false;
- }
- // Parse temporary file name from command line result
- var splitResult = result.Split(new[] { "\t", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
- if (splitResult.Length != 2)
- {
- return false;
- }
- var temporaryFileName = splitResult[0].Trim();
- if (!File.Exists(temporaryFileName))
- {
- return false;
- }
- var retValue = false;
- try
- {
- if (File.Exists(saveAsFileName))
- {
- File.Delete(saveAsFileName);
- }
- File.Move(temporaryFileName, saveAsFileName);
- retValue = true;
- }
- catch
- {
- }
- finally
- {
- if (File.Exists(temporaryFileName))
- {
- File.Delete(temporaryFileName);
- }
- }
- return retValue;
- }
- public void SaveBlobAs(string saveAs, string blob)
- {
- using (var ms = (MemoryStream)GetFileStream(blob)) //Ugly, has implementation info.
- {
- byte[] buf = ms.ToArray();
- if (EffectiveConfigFile.core.autocrlf.Value == AutoCRLFType.True)
- {
- if (!FileHelper.IsBinaryFile(this, saveAs) && !FileHelper.IsBinaryFileAccordingToContent(buf))
- {
- buf = GitConvert.ConvertCrLfToWorktree(buf);
- }
- }
- using (FileStream fileOut = File.Create(saveAs))
- {
- fileOut.Write(buf, 0, buf.Length);
- }
- }
- }
- private static string GetSide(string side)
- {
- if (side.Equals("REMOTE", StringComparison.CurrentCultureIgnoreCase))
- side = "3";
- if (side.Equals("LOCAL", StringComparison.CurrentCultureIgnoreCase))
- side = "2";
- if (side.Equals("BASE", StringComparison.CurrentCultureIgnoreCase))
- side = "1";
- return side;
- }
- public string[] GetConflictedFiles(string filename)
- {
- Directory.SetCurrentDirectory(_workingDir);
- filename = filename.ToPosixPath();
- string[] fileNames =
- {
- filename + ".BASE",
- filename + ".LOCAL",
- filename + ".REMOTE"
- };
- var unmerged = RunGitCmd("ls-files -z --unmerged \"" + filename + "\"").Split(new char[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
- foreach (var file in unmerged)
- {
- string fileStage = null;
- int findSecondWhitespace = file.IndexOfAny(new[] { ' ', '\t' });
- if (findSecondWhitespace >= 0) fileStage = file.Substring(findSecondWhitespace).Trim();
- findSecondWhitespace = fileStage.IndexOfAny(new[] { ' ', '\t' });
- if (findSecondWhitespace >= 0) fileStage = fileStage.Substring(findSecondWhitespace).Trim();
- if (string.IsNullOrEmpty(fileStage))
- continue;
- int stage;
- if (!Int32.TryParse(fileStage.Trim()[0].ToString(), out stage))
- continue;
- var tempFile = RunGitCmd("checkout-index --temp --stage=" + stage + " -- " + "\"" + filename + "\"");
- tempFile = tempFile.Split('\t')[0];
- tempFile = Path.Combine(_workingDir, tempFile);
- var newFileName = Path.Combine(_workingDir, fileNames[stage - 1]);
- try
- {
- fileNames[stage - 1] = newFileName;
- var index = 1;
- while (File.Exists(fileNames[stage - 1]) && index < 50)
- {
- fileNames[stage - 1] = newFileName + index;
- index++;
- }
- File.Move(tempFile, fileNames[stage - 1]);
- }
- catch (Exception ex)
- {
- Trace.WriteLine(ex);
- }
- }
- if (!File.Exists(fileNames[0])) fileNames[0] = null;
- if (!File.Exists(fileNames[1])) fileNames[1] = null;
- if (!File.Exists(fileNames[2])) fileNames[2] = null;
- return fileNames;
- }
- public string[] GetConflictedFileNames(string filename)
- {
- filename = filename.ToPosixPath();
- var fileNames = new string[3];
- var unmerged = RunGitCmd("ls-files -z --unmerged \"" + filename + "\"").Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
- foreach (var line in unmerged)
- {
- int findSecondWhitespace = line.IndexOfAny(new[] { ' ', '\t' });
- string fileStage = findSecondWhitespace >= 0 ? line.Substring(findSecondWhitespace).Trim() : "";
- findSecondWhitespace = fileStage.IndexOfAny(new[] { ' ', '\t' });
- fileStage = findSecondWhitespace >= 0 ? fileStage.Substring(findSecondWhitespace).Trim() : "";
- int stage;
- if (fileStage.Length > 2 && Int32.TryParse(fileStage[0].ToString(), out stage) && stage >= 1 && stage <= 3)
- {
- fileNames[stage - 1] = fileStage.Substring(2);
- }
- }
- return fileNames;
- }
- public string[] GetConflictedSubmoduleHashes(string filename)
- {
- filename = filename.ToPosixPath();
- var hashes = new string[3];
- var unmerged = RunGitCmd("ls-files -z --unmerged \"" + filename + "\"").Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);
- foreach (var line in unmerged)
- {
- int findSecondWhitespace = line.IndexOfAny(new[] { ' ', '\t' });
- string fileStage = findSecondWhitespace >= 0 ? line.Substring(findSecondWhitespace).Trim() : "";
- findSecondWhitespace = fileStage.IndexOfAny(new[] { ' ', '\t' });
- string hash = findSecondWhitespace >= 0 ? fileStage.Substring(0, findSecondWhitespace).Trim() : "";
- fileStage = findSecondWhitespace >= 0 ? fileStage.Substring(findSecondWhitespace).Trim() : "";
- int stage;
- if (fileStage.Length > 2 && Int32.TryParse(fileStage[0].ToString(), out stage) && stage >= 1 && stage <= 3)
- {
- hashes[stage - 1] = hash;
- }
- }
- return hashes;
- }
- public Dictionary<GitRef, GitItem> GetSubmoduleItemsForEachRef(string filename, Func<GitRef, bool> showRemoteRef)
- {
- string command = GetShowRefCommand();
- if (command == null)
- return new Dictionary<GitRef, GitItem>();
- filename = filename.ToPosixPath();
- var tree = RunGitCmd(command, SystemEncoding);
- var refs = GetTreeRefs(tree);
- return refs.Where(showRemoteRef).ToDictionary(r => r, r => GetSubmoduleGuid(filename, r.Name));
- }
- private string GetShowRefCommand()
- {
- if (AppSettings.ShowSuperprojectRemoteBranches)
- return "show-ref --dereference";
- if (AppSettings.ShowSuperprojectBranches || AppSettings.ShowSuperprojectTags)
- return "show-ref --dereference"
- + (AppSettings.ShowSuperprojectBranches ? " --heads" : null)
- + (AppSettings.ShowSuperprojectTags ? " --tags" : null);
- return null;
- }
- private GitItem GetSubmoduleGuid(string filename, string refName)
- {
- string str = RunGitCmd("ls-tree " + refName + " \"" + filename + "\"");
- return GitItem.CreateGitItemFromString(this, str);
- }
- public int? GetCommitCount(string parentHash, string childHash)
- {
- string result = RunGitCmd("rev-list " + parentHash + " ^" + childHash + " --count");
- int commitCount;
- if (int.TryParse(result, out commitCount))
- return commitCount;
- return null;
- }
- public string GetCommitCountString(string from, string to)
- {
- int? removed = GetCommitCount(from, to);
- int? added = GetCommitCount(to, from);
- if (removed == null || added == null)
- return "";
- if (removed == 0 && added == 0)
- return "=";
- return
- (removed > 0 ? ("-" + removed) : "") +
- (added > 0 ? ("+" + added) : "");
- }
- public string GetMergeMessage()
- {
- var file = GetGitDirectory() + "MERGE_MSG";
- return
- File.Exists(file)
- ? File.ReadAllText(file)
- : "";
- }
- public void RunGitK()
- {
- if (EnvUtils.RunningOnUnix())
- {
- RunExternalCmdDetachedShowConsole("gitk", "");
- }
- else
- {
- RunExternalCmdDetached("cmd.exe", "/c \"\"" + AppSettings.GitCommand.Replace("git.cmd", "gitk.cmd")
- .Replace("bin\\git.exe", "cmd\\gitk.cmd")
- .Replace("bin/git.exe", "cmd/gitk.cmd") + "\" --branches --tags --remotes\"");
- }
- }
- public void RunGui()
- {
- if (EnvUtils.RunningOnUnix())
- {
- RunExternalCmdDetachedShowConsole(AppSettings.GitCommand, "gui");
- }
- else
- {
- RunExternalCmdDetached("cmd.exe", "/c \"\"" + AppSettings.GitCommand + "\" gui\"");
- }
- }
- /// <summary>Runs a bash or shell command.</summary>
- public Process RunBash(string bashCommand = null)
- {
- if (EnvUtils.RunningOnUnix())
- {
- string[] termEmuCmds =
- {
- "gnome-terminal",
- "konsole",
- "Terminal",
- "xterm"
- };
- string args = "";
- string cmd = termEmuCmds.FirstOrDefault(termEmuCmd => !string.IsNullOrEmpty(RunCmd("which", termEmuCmd)));
- if (string.IsNullOrEmpty(cmd))
- {
- cmd = "bash";
- args = "--login -i";
- }
- return RunExternalCmdDetachedShowConsole(cmd, args);
- }
- else
- {
- string args;
- if (string.IsNullOrWhiteSpace(bashCommand))
- {
- args = "--login -i\"";
- }
- else
- {
- args = "--login -i -c \"" + bashCommand.Replace("\"", "\\\"") + "\"";
- }
- string termCmd = File.Exists(AppSettings.GitBinDir + "bash.exe") ? "bash" : "sh";
- return RunExternalCmdDetachedShowConsole("cmd.exe",
- string.Format("/c \"\"{0}{1}\" {2}", AppSettings.GitBinDir, termCmd, args));
- }
- }
- public string Init(bool bare, bool shared)
- {
- return RunGitCmd(Smart.Format("init{0: --bare|}{1: --shared=all|}", bare, shared));
- }
- public bool IsMerge(string commit)
- {
- string[] parents = GetParents(commit);
- return parents.Length > 1;
- }
- private static string ProccessDiffNotes(int startIndex, string[] lines)
- {
- int endIndex = lines.Length - 1;
- if (lines[endIndex] == "Notes:")
- endIndex--;
- var message = new StringBuilder();
- bool bNotesStart = false;
- for (int i = startIndex; i <= endIndex; i++)
- {
- string line = lines[i];
- if (bNotesStart)
- line = " " + line;
- message.AppendLine(line);
- if (lines[i] == "Notes:")
- bNotesStart = true;
- }
- return message.ToString();
- }
- public GitRevision GetRevision(string commit, bool shortFormat = false)
- {
- const string formatString =
- /* Hash */ "%H%n" +
- /* Tree */ "%T%n" +
- /* Parents */ "%P%n" +
- /* Author Name */ "%aN%n" +
- /* Author EMail */ "%aE%n" +
- /* Author Date */ "%at%n" +
- /* Committer Name */ "%cN%n" +
- /* Committer EMail*/ "%cE%n" +
- /* Committer Date */ "%ct%n";
- const string messageFormat = "%e%n%B%nNotes:%n%-N";
- string cmd = "log -n1 --format=format:" + formatString + (shortFormat ? "%e%n%s" : messageFormat) + " " + commit;
- var revInfo = RunCacheableCmd(AppSettings.GitCommand, cmd, LosslessEncoding);
- string[] lines = revInfo.Split('\n');
- var revision = new GitRevision(this, lines[0])
- {
- TreeGuid = lines[1],
- ParentGuids = lines[2].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries),
- Author = ReEncodeStringFromLossless(lines[3]),
- AuthorEmail = ReEncodeStringFromLossless(lines[4]),
- Committer = ReEncodeStringFromLossless(lines[6]),
- CommitterEmail = ReEncodeStringFromLossless(lines[7])
- };
- revision.AuthorDate = DateTimeUtils.ParseUnixTime(lines[5]);
- revision.CommitDate = DateTimeUtils.ParseUnixTime(lines[8]);
- revision.MessageEncoding = lines[9];
- if (shortFormat)
- {
- revision.Message = ReEncodeCommitMessage(lines[10], revision.MessageEncoding);
- }
- else
- {
- string message = ProccessDiffNotes(10, lines);
- //commit message is not reencoded by git when format is given
- revision.Body = ReEncodeCommitMessage(message, revision.MessageEncoding);
- revision.Message = revision.Body.Substring(0, revision.Body.IndexOfAny(new[] { '\r', '\n' }));
- }
- return revision;
- }
- public string[] GetParents(string commit)
- {
- string output = RunGitCmd("log -n 1 --format=format:%P \"" + commit + "\"");
- return output.Split(' ');
- }
- public GitRevision[] GetParentsRevisions(string commit)
- {
- string[] parents = GetParents(commit);
- var parentsRevisions = new GitRevision[parents.Length];
- for (int i = 0; i < parents.Length; i++)
- parentsRevisions[i] = GetRevision(parents[i], true);
- return parentsRevisions;
- }
- public string ShowSha1(string sha1)
- {
- return ReEncodeShowString(RunCacheableCmd(AppSettings.GitCommand, "show " + sha1, LosslessEncoding));
- }
- public string DeleteTag(string tagName)
- {
- return RunGitCmd(GitCommandHelpers.DeleteTagCmd(tagName));
- }
- public string GetCurrentCheckout()
- {
- return RunGitCmd("rev-parse HEAD").TrimEnd();
- }
- public KeyValuePair<char, string> GetSuperprojectCurrentCheckout()
- {
- if (SuperprojectModule == null)
- return new KeyValuePair<char, string>(' ', "");
- var lines = SuperprojectModule.RunGitCmd("submodule status --cached " + _submodulePath).Split('\n');
- if (lines.Length == 0)
- return new KeyValuePair<char, string>(' ', "");
- string submodule = lines[0];
- if (submodule.Length < 43)
- return new KeyValuePair<char, string>(' ', "");
- var currentCommitGuid = submodule.Substring(1, 40).Trim();
- return new KeyValuePair<char, string>(submodule[0], currentCommitGuid);
- }
- public bool ExistsMergeCommit(string startRev, string endRev)
- {
- if (startRev.IsNullOrEmpty() || endRev.IsNullOrEmpty())
- return false;
- string revisions = RunGitCmd("rev-list --parents --no-walk " + startRev + ".." + endRev);
- string[] revisionsTab = revisions.Split('\n');
- Func<string, bool> ex = (string parents) =>
- {
- string[] tab = parents.Split(' ');
- return tab.Length > 2 && tab.All(parent => GitRevision.Sha1HashRegex.IsMatch(parent));
- };
- return revisionsTab.Any(ex);
- }
- public ConfigFile GetSubmoduleConfigFile()
- {
- return new ConfigFile(_workingDir + ".gitmodules", true);
- }
- public string GetCurrentSubmoduleLocalPath()
- {
- if (SuperprojectModule == null)
- return null;
- string submodulePath = WorkingDir.Substring(SuperprojectModule.WorkingDir.Length);
- submodulePath = PathUtil.GetDirectoryName(submodulePath.ToPosixPath());
- return submodulePath;
- }
- public string GetSubmoduleNameByPath(string localPath)
- {
- var configFile = GetSubmoduleConfigFile();
- var submodule = configFile.ConfigSections.FirstOrDefault(configSection => configSection.GetPathValue("path").Trim() == localPath);
- if (submodule != null)
- return submodule.SubSection.Trim();
- return null;
- }
- public string GetSubmoduleRemotePath(string name)
- {
- var configFile = GetSubmoduleConfigFile();
- return configFile.GetPathValue(string.Format("submodule.{0}.url", name)).Trim();
- }
- public string GetSubmoduleFullPath(string localPath)
- {
- string dir = Path.Combine(_workingDir, localPath.EnsureTrailingPathSeparator());
- return Path.GetFullPath(dir); // fix slashes
- }
- public GitModule GetSubmodule(string localPath)
- {
- return new GitModule(GetSubmoduleFullPath(localPath));
- }
- IGitModule IGitModule.GetSubmodule(string submoduleName)
- {
- return GetSubmodule(submoduleName);
- }
- private GitSubmoduleInfo GetSubmoduleInfo(string submodule)
- {
- var gitSubmodule =
- new GitSubmoduleInfo(this)
- {
- Initialized = submodule[0] != '-',
- UpToDate = submodule[0] != '+',
- CurrentCommitGuid = submodule.Substring(1, 40).Trim()
- };
- var localPath = submodule.Substring(42).Trim();
- if (localPath.Contains("("))
- {
- gitSubmodule.LocalPath = localPath.Substring(0, localPath.IndexOf("(")).TrimEnd();
- gitSubmodule.Branch = localPath.Substring(localPath.IndexOf("(")).Trim(new[] { '(', ')', ' ' });
- }
- else
- gitSubmodule.LocalPath = localPath;
- return gitSubmodule;
- }
- public IEnumerable<IGitSubmoduleInfo> GetSubmodulesInfo()
- {
- var submodules = ReadGitOutputLines("submodule status");
- string lastLine = null;
- foreach (var submodule in submodules)
- {
- if (submodule.Length < 43)
- continue;
- if (submodule.Equals(lastLine))
- continue;
- lastLine = submodule;
- yield return GetSubmoduleInfo(submodule);
- }
- }
- public string FindGitSuperprojectPath(out string submoduleName, out string submodulePath)
- {
- submoduleName = null;
- submodulePath = null;
- if (!IsValidGitWorkingDir())
- return null;
- string superprojectPath = null;
- string currentPath = Path.GetDirectoryName(_workingDir); // remove last slash
- if (!string.IsNullOrEmpty(currentPath))
- {
- string path = Path.GetDirectoryName(currentPath);
- for (int i = 0; i < 5; i++)
- {
- if (string.IsNullOrEmpty(path))
- break;
- if (File.Exists(Path.Combine(path, ".gitmodules")) &&
- IsValidGitWorkingDir(path))
- {
- superprojectPath = path.EnsureTrailingPathSeparator();
- break;
- }
- // Check upper directory
- path = Path.GetDirectoryName(path);
- }
- }
- if (File.Exists(_workingDir + ".git") &&
- superprojectPath == null)
- {
- var lines = File.ReadLines(_workingDir + ".git");
- foreach (string line in lines)
- {
- if (line.StartsWith("gitdir:"))
- {
- string gitpath = line.Substring(7).Trim();
- int pos = gitpath.IndexOf("/.git/");
- if (pos != -1)
- {
- gitpath = gitpath.Substring(0, pos + 1).Replace('/', '\\');
- gitpath = Path.GetFullPath(Path.Combine(_workingDir, gitpath));
- if (File.Exists(gitpath + ".gitmodules") && IsValidGitWorkingDir(gitpath))
- superprojectPath = gitpath;
- }
- }
- }
- }
- if (!string.IsNullOrEmpty(superprojectPath))
- {
- submodulePath = currentPath.Substring(superprojectPath.Length).ToPosixPath();
- var configFile = new ConfigFile(superprojectPath + ".gitmodules", true);
- foreach (ConfigSection configSection in configFile.ConfigSections)
- {
- if (configSection.GetPathValue("path") == submodulePath.ToPosixPath())
- {
- submoduleName = configSection.SubSection;
- return superprojectPath;
- }
- }
- }
- return null;
- }
- public string GetSubmoduleSummary(string submodule)
- {
- var arguments = string.Format("submodule summary {0}", submodule);
- return RunGitCmd(arguments);
- }
- public string ResetSoft(string commit)
- {
- return ResetSoft(commit, "");
- }
- public string ResetMixed(string commit)
- {
- return ResetMixed(commit, "");
- }
- public string ResetHard(string commit)
- {
- return ResetHard(commit, "");
- }
- public string ResetSoft(string commit, string file)
- {
- var args = "reset --soft";
- if (!string.IsNullOrEmpty(commit))
- args += " \"" + commit + "\"";
- if (!string.IsNullOrEmpty(file))
- args += " -- \"" + file + "\"";
- return RunGitCmd(args);
- }
- public string ResetMixed(string commit, string file)
- {
- var args = "reset --mixed";
- if (!string.IsNullOrEmpty(commit))
- args += " \"" + commit + "\"";
- if (!string.IsNullOrEmpty(file))
- args += " -- \"" + file + "\"";
- return RunGitCmd(args);
- }
- public string ResetHard(string commit, string file)
- {
- var args = "reset --hard";
- if (!string.IsNullOrEmpty(commit))
- args += " \"" + commit + "\"";
- if (!string.IsNullOrEmpty(file))
- args += " -- \"" + file + "\"";
- return RunGitCmd(args);
- }
- public string ResetFile(string file)
- {
- file = file.ToPosixPath();
- return RunGitCmd("checkout-index --index --force -- \"" + file + "\"");
- }
- public string FormatPatch(string from, string to, string output, int start)
- {
- output = output.ToPosixPath();
- var result = RunGitCmd("format-patch -M -C -B --start-number " + start + " \"" + from + "\"..\"" + to +
- "\" -o \"" + output + "\"");
- return result;
- }
- public string FormatPatch(string from, string to, string output)
- {
- output = output.ToPosixPath();
- var result = RunGitCmd("format-patch -M -C -B \"" + from + "\"..\"" + to + "\" -o \"" + output + "\"");
- return result;
- }
- public string Tag(string tagName, string revision, bool annotation, bool force)
- {
- if (annotation)
- return RunGitCmd(string.Format("tag \"{0}\" -a {1} -F \"{2}\\TAGMESSAGE\" -- \"{3}\"", tagName.Trim(), (force ? "-f" : ""), GetGitDirectory(), revision));
- return RunGitCmd(string.Format("tag {0} \"{1}\" \"{2}\"", (force ? "-f" : ""), tagName.Trim(), revision));
- }
- public string CheckoutFiles(IEnumerable<string> fileList, string revision, bool force)
- {
- string files = fileList.Select(s => s.Quote()).Join(" ");
- return RunGitCmd("checkout " + force.AsForce() + revision.Quote() + " -- " + files);
- }
- /// <summary>Tries to start Pageant for the specified remote repo (using the remote's PuTTY key file).</summary>
- /// <returns>true if the remote has a PuTTY key file; otherwise, false.</returns>
- public bool StartPageantForRemote(string remote)
- {
- var sshKeyFile = GetPuttyKeyFileForRemote(remote);
- if (string.IsNullOrEmpty(sshKeyFile) || !File.Exists(sshKeyFile))
- return false;
- StartPageantWithKey(sshKeyFile);
- return true;
- }
- public static void StartPageantWithKey(string sshKeyFile)
- {
- RunExternalCmdDetached(AppSettings.Pageant, "\"" + sshKeyFile + "\"", "");
- }
- public string GetPuttyKeyFileForRemote(string remote)
- {
- if (string.IsNullOrEmpty(remote) ||
- string.IsNullOrEmpty(AppSettings.Pageant) ||
- !AppSettings.AutoStartPageant ||
- !GitCommandHelpers.Plink())
- return "";
- return GetPathSetting(string.Format("remote.{0}.puttykeyfile", remote));
- }
- public static bool PathIsUrl(string path)
- {
- return path.Contains(Path.DirectorySeparatorChar) || path.Contains(AppSettings.PosixPathSeparator.ToString());
- }
- public string FetchCmd(string remote, string remoteBranch, string localBranch, bool? fetchTags = false)
- {
- var progressOption = "";
- if (GitCommandHelpers.VersionInUse.FetchCanAskForProgress)
- progressOption = "--progress ";
- if (string.IsNullOrEmpty(remote) && string.IsNullOrEmpty(remoteBranch) && string.IsNullOrEmpty(localBranch))
- return "fetch " + progressOp…
Large files files are truncated, but you can click here to view the full file