private void button1_Click(object sender, EventArgs e) { GitArgumentBuilder args = new("branch") { "-a" }; string[] references = _gitUiCommands.GitModule.GitExecutable.GetOutput(args) .Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if (references.Length == 0) { MessageBox.Show(this, "No remote branches found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); DialogResult = DialogResult.Cancel; return; } foreach (string reference in references) { try { string branchName = reference.Trim('*', ' ', '\n', '\r'); if (branchName.StartsWith("remotes/" + _NO_TRANSLATE_Remote.Text + "/")) { args = new GitArgumentBuilder("branch") { "--track", branchName.Replace($"remotes/{_NO_TRANSLATE_Remote.Text}/", ""), branchName }; _gitUiCommands.GitModule.GitExecutable.GetOutput(args); } } catch { } } MessageBox.Show(this, string.Format("{0} local tracking branches have been created/updated.", references.Length), "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); Close(); } }
private string CalculateRemoteBranchNameBasedOnBranchesText() { if (!Branches.Text.IsNullOrEmpty()) { return(Branches.Text); } string remoteBranchName = Module.GetSetting(string.Format("branch.{0}.merge", _branch)); if (!remoteBranchName.IsNullOrEmpty()) { var args = new GitArgumentBuilder("name-rev") { "--name-only", remoteBranchName.QuoteNE() }; remoteBranchName = Module.GitExecutable.GetOutput(args).Trim(); } return(remoteBranchName); }
private void StageSubmodule() { using (var form = new FormStatus(ProcessStart, string.Format(_stageFilename.Text, _filename))) { form.ShowDialogOnError(this); } void ProcessStart(FormStatus form) { form.AddMessageLine(string.Format(_stageFilename.Text, _filename)); var args = new GitArgumentBuilder("add") { "--", _filename.QuoteNE() }; string output = Module.RunGitCmd(args); form.AddMessageLine(output); form.Done(isSuccess: string.IsNullOrWhiteSpace(output)); } }
private void LoadBaseBranches() { var branchType = cbType.SelectedValue.ToString(); var manageBaseBranch = branchType != Branch.release.ToString("G"); pnlBasedOn.Visible = manageBaseBranch; if (manageBaseBranch) { cbBaseBranch.DataSource = GetLocalBranches(); } List <string> GetLocalBranches() { var args = new GitArgumentBuilder("branch"); return(_gitUiCommands.GitModule .GitExecutable.GetOutput(args) .Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(e => e.Trim('*', ' ', '\n', '\r')) .ToList()); } }
/// <summary> /// Returns the branches of a remote repository as strings; ignores git errors and warnings. /// </summary> /// 'git ls-remotes --heads "URL"' is completely independent from a local repo clone. /// Hence there is no need for a GitModule. /// <param name="gitExecutable">the git executable.</param> /// <param name="url">the repo URL; can also be a local path.</param> private static IEnumerable <string> LoadRemoteRepoBranches(IExecutable gitExecutable, string url) { if (string.IsNullOrWhiteSpace(url)) { return(Array.Empty <string>()); } var gitArguments = new GitArgumentBuilder("ls-remote") { "--heads", url.ToPosixPath().Quote() }; var heads = gitExecutable.GetOutput(gitArguments); return(heads.LazySplit('\n', StringSplitOptions.RemoveEmptyEntries) .Select(head => { int branchIndex = head.IndexOf(GitRefName.RefsHeadsPrefix); return branchIndex == -1 ? null : head.Substring(branchIndex + GitRefName.RefsHeadsPrefix.Length); }) .WhereNotNull() .ToImmutableList()); }
public async Task Validate_GetRevisionTagSignatureStatusAsync_one_tag(TagStatus tagStatus, string gitCmdReturn) { var objectId = ObjectId.Random(); var gitRef = new GitRef(_module(), objectId, "refs/tags/FirstTag^{}"); var revision = new GitRevision(objectId) { Refs = new[] { gitRef } }; var args = new GitArgumentBuilder("verify-tag") { "--raw", gitRef.LocalName }; _module().GitExecutable.GetOutput(args).Returns(gitCmdReturn); var actual = await _gpgController.GetRevisionTagSignatureStatusAsync(revision); Assert.AreEqual(tagStatus, actual); }
/// <inheritdoc /> public Encoding Read() { try { Encoding systemEncoding; // 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" var arguments = new GitArgumentBuilder("config") { "--get", controlStr }; string s = _module.GitExecutable.GetOutput(arguments, outputEncoding: 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); } catch (Exception) { // Ignore exception. If the git location itself is not configured correctly yet, we could never execute it. return(Encoding.Default); } }
private IEnumerable <string> GetObsoleteBranchNames(RefreshContext context, string curBranch) { RegexOptions options; if (context.RegexIgnoreCase) { options = RegexOptions.Compiled | RegexOptions.IgnoreCase; } else { options = RegexOptions.Compiled; } var regex = string.IsNullOrEmpty(context.RegexFilter) ? null : new Regex(context.RegexFilter, options); bool regexMustMatch = !context.RegexDoesNotMatch; var args = new GitArgumentBuilder("branch") { "--list", { context.IncludeRemotes, "-r" }, { !context.IncludeUnmerged, "--merged" }, context.ReferenceBranch }; var result = context.Commands.GitExecutable.Execute(args); if (!result.ExitedSuccessfully) { MessageBox.Show(this, result.AllOutput, $"git {args}", MessageBoxButtons.OK, MessageBoxIcon.Error); return(Array.Empty <string>()); } return(_commandOutputParser.GetBranchNames(result.AllOutput) .Where(branchName => branchName != curBranch && branchName != context.ReferenceBranch) .Where(branchName => (!context.IncludeRemotes || branchName.StartsWith(context.RemoteRepositoryName + "/")) && (regex == null || regex.IsMatch(branchName) == regexMustMatch))); }
private void LoadFileHistory() { FileChanges.Visible = true; if (string.IsNullOrEmpty(FileName)) { return; } _asyncLoader.LoadAsync( () => BuildFilter(), filter => { FileChanges.SetFilters(filter); FileChanges.Load(); }); return; (string revision, string path) BuildFilter() { var fileName = FileName; // Replace windows path separator to Linux path separator. // This is needed to keep the file history working when started from file tree in // browse dialog. fileName = fileName.ToPosixPath(); // we will need this later to look up proper casing for the file var fullFilePath = _fullPathResolver.Resolve(fileName); // The section below contains native windows (kernel32) calls // and breaks on Linux. Only use it on Windows. Casing is only // a Windows problem anyway. if (EnvUtils.RunningOnWindows() && File.Exists(fullFilePath)) { // grab the 8.3 file path var shortPath = new StringBuilder(4096); NativeMethods.GetShortPathName(fullFilePath, shortPath, shortPath.Capacity); // use 8.3 file path to get properly cased full file path var longPath = new StringBuilder(4096); NativeMethods.GetLongPathName(shortPath.ToString(), longPath, longPath.Capacity); // remove the working directory and now we have a properly cased file name. fileName = longPath.ToString().Substring(Module.WorkingDir.Length).ToPosixPath(); } if (fileName.StartsWith(Module.WorkingDir, StringComparison.InvariantCultureIgnoreCase)) { fileName = fileName.Substring(Module.WorkingDir.Length); } FileName = fileName; var res = (revision : (string)null, path : $" \"{fileName}\""); if (AppSettings.FollowRenamesInFileHistory && !Directory.Exists(fullFilePath)) { // git log --follow is not working as expected (see http://kerneltrap.org/mailarchive/git/2009/1/30/4856404/thread) // // But we can take a more complicated path to get reasonable results: // 1. use git log --follow to get all previous filenames of the file we are interested in // 2. use git log "list of files names" to get the history graph // // note: This implementation is quite a quick hack (by someone who does not speak C# fluently). // var args = new GitArgumentBuilder("log") { "--format=\"%n\"", "--name-only", "--format", GitCommandHelpers.FindRenamesAndCopiesOpts(), "--", fileName.Quote() }; var listOfFileNames = new StringBuilder(fileName.Quote()); // keep a set of the file names already seen var setOfFileNames = new HashSet <string> { fileName }; var lines = Module.GetGitOutputLines(args, GitModule.LosslessEncoding); foreach (var line in lines.Select(GitModule.ReEncodeFileNameFromLossless)) { if (!string.IsNullOrEmpty(line) && setOfFileNames.Add(line)) { listOfFileNames.Append(" \""); listOfFileNames.Append(line); listOfFileNames.Append('\"'); } } // here we need --name-only to get the previous filenames in the revision graph res.path = listOfFileNames.ToString(); res.revision += " --name-only --parents" + GitCommandHelpers.FindRenamesAndCopiesOpts(); } else if (AppSettings.FollowRenamesInFileHistory) { // history of a directory // --parents doesn't work with --follow enabled, but needed to graph a filtered log res.revision = " " + GitCommandHelpers.FindRenamesOpt() + " --follow --parents"; } else { // rename following disabled res.revision = " --parents"; } if (AppSettings.FullHistoryInFileHistory) { res.revision = string.Concat(" --full-history --simplify-merges ", res.revision); } return(res); } }
private void Delete_Click(object sender, EventArgs e) { var selectedBranches = _branches.Where(branch => branch.Delete).ToList(); if (selectedBranches.Count == 0) { MessageBox.Show(string.Format(_selectBranchesToDelete.Text, _NO_TRANSLATE_deleteDataGridViewCheckBoxColumn.HeaderText), _deleteCaption.Text); return; } if (MessageBox.Show(this, string.Format(_areYouSureToDelete.Text, selectedBranches.Count), _deleteCaption.Text, MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } var remoteName = _NO_TRANSLATE_Remote.Text; var remoteBranchPrefix = remoteName + "/"; var remoteBranchesSource = IncludeRemoteBranches.Checked ? selectedBranches.Where(branch => branch.Name.StartsWith(remoteBranchPrefix)) : Enumerable.Empty <Branch>(); var remoteBranches = remoteBranchesSource.ToList(); if (remoteBranches.Count > 0) { var message = string.Format(_dangerousAction.Text, remoteName); if (MessageBox.Show(this, message, _deleteCaption.Text, MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } } var localBranches = selectedBranches.Except(remoteBranches).ToList(); tableLayoutPanel2.Enabled = tableLayoutPanel3.Enabled = false; imgLoading.Visible = true; lblStatus.Text = _deletingBranches.Text; ThreadHelper.JoinableTaskFactory.RunAsync(async() => { await TaskScheduler.Default.SwitchTo(alwaysYield: true); foreach (var remoteBranch in remoteBranches) { // Delete branches one by one, because it is possible one fails var remoteBranchNameOffset = remoteBranchPrefix.Length; var args = new GitArgumentBuilder("push") { remoteName, $":{remoteBranch.Name.Substring(remoteBranchNameOffset)}" }; _gitCommands.GitExecutable.GetOutput(args); } foreach (var localBranch in localBranches) { var args = new GitArgumentBuilder("branch") { "-d", localBranch.Name }; _gitCommands.GitExecutable.GetOutput(args); // Delete branches one by one, because it is possible one fails _gitCommands.GitExecutable.GetOutput(args); } await this.SwitchToMainThreadAsync(); tableLayoutPanel2.Enabled = tableLayoutPanel3.Enabled = true; await RefreshObsoleteBranchesAsync().ConfigureAwait(false); }); }
private void RecreateObservable() { CancelBackgroundOperation(); int fetchInterval = _fetchInterval.ValueOrDefault(Settings); var gitModule = _currentGitUiCommands.GitModule; if (fetchInterval > 0 && gitModule.IsValidGitWorkingDir()) { _cancellationToken = Observable.Timer(TimeSpan.FromSeconds(Math.Max(5, fetchInterval))) .SelectMany(i => { // if git not running - start fetch immediately if (!gitModule.IsRunningGitProcess()) { return(Observable.Return(i)); } // in other case - every 5 seconds check if git still running return(Observable .Interval(TimeSpan.FromSeconds(5)) .SkipWhile(ii => gitModule.IsRunningGitProcess()) .FirstAsync() ); }) .Repeat() .ObserveOn(ThreadPoolScheduler.Instance) .Subscribe(i => { GitArgumentBuilder args; if (_fetchAllSubmodules.ValueOrDefault(Settings)) { args = new GitArgumentBuilder("submodule") { "foreach", "--recursive", "git", "fetch", "--all" }; _currentGitUiCommands.GitModule.GitExecutable.GetOutput(args); } var gitCmd = _gitCommand.ValueOrDefault(Settings).Trim().SplitBySpace(); args = new GitArgumentBuilder(gitCmd[0]) { gitCmd.Skip(1) }; var msg = _currentGitUiCommands.GitModule.GitExecutable.GetOutput(args); if (_autoRefresh.ValueOrDefault(Settings)) { if (gitCmd[0].Equals("fetch", StringComparison.InvariantCultureIgnoreCase)) { if (msg.Contains("From")) { _currentGitUiCommands.RepoChangedNotifier.Notify(); } } else { _currentGitUiCommands.RepoChangedNotifier.Notify(); } } }); } }
private void FindLargeFilesFunction() { try { var data = GetLargeFiles(_threshold); Dictionary <string, DateTime> revData = new Dictionary <string, DateTime>(); foreach (var d in data) { string commit = d.Commit.First(); DateTime date; if (!revData.ContainsKey(commit)) { var args = new GitArgumentBuilder("show") { "-s", commit, "--format=\"%ci\"" }; string revDate = _gitCommands.GitExecutable.GetOutput(args); DateTime.TryParse(revDate, out date); revData.Add(commit, date); } else { date = revData[commit]; } if (!_list.TryGetValue(d.SHA, out var curGitObject)) { d.LastCommitDate = date; _list.Add(d.SHA, d); ThreadHelper.JoinableTaskFactory.Run(async() => { await BranchesGrid.SwitchToMainThreadAsync(); _gitObjects.Add(d); }); } else if (!curGitObject.Commit.Contains(commit)) { if (curGitObject.LastCommitDate < date) { curGitObject.LastCommitDate = date; } ThreadHelper.JoinableTaskFactory.Run(async() => { await BranchesGrid.SwitchToMainThreadAsync(); _gitObjects.ResetItem(_gitObjects.IndexOf(curGitObject)); }); curGitObject.Commit.Add(commit); } } string objectsPackDirectory = _gitCommands.ResolveGitInternalPath("objects/pack/"); if (Directory.Exists(objectsPackDirectory)) { var packFiles = Directory.GetFiles(objectsPackDirectory, "pack-*.idx"); foreach (var pack in packFiles) { var args = new GitArgumentBuilder("verify-pack") { "-v", pack }; string[] objects = _gitCommands.GitExecutable.GetOutput(args).Split('\n'); ThreadHelper.JoinableTaskFactory.Run(async() => { await pbRevisions.SwitchToMainThreadAsync(); pbRevisions.Value = pbRevisions.Value + (int)((_revList.Length * 0.1f) / packFiles.Length); }); foreach (var gitObject in objects.Where(x => x.Contains(" blob "))) { string[] dataFields = gitObject.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (_list.TryGetValue(dataFields[0], out var curGitObject)) { if (int.TryParse(dataFields[3], out var compressedSize)) { curGitObject.CompressedSizeInBytes = compressedSize; ThreadHelper.JoinableTaskFactory.Run(async() => { await BranchesGrid.SwitchToMainThreadAsync(); _gitObjects.ResetItem(_gitObjects.IndexOf(curGitObject)); }); } } } } } ThreadHelper.JoinableTaskFactory.Run(async() => { await pbRevisions.SwitchToMainThreadAsync(); pbRevisions.Hide(); }); ThreadHelper.JoinableTaskFactory.Run(async() => { await BranchesGrid.SwitchToMainThreadAsync(); BranchesGrid.ReadOnly = false; }); } catch { } }
public void Validate_GetTagVerifyMessage(int usefulTagRefNumber, string expected) { var objectId = ObjectId.Random(); var revision = new GitRevision(objectId); switch (usefulTagRefNumber) { case 0: { // Tag but not dereference var gitRef = new GitRef(_module(), objectId, "refs/tags/TagName"); revision.Refs = new[] { gitRef }; var args = new GitArgumentBuilder("verify-tag") { gitRef.LocalName }; _module().GitExecutable.GetOutput(args).Returns(""); break; } case 1: { // One tag that's also IsDereference == true var gitRef = new GitRef(_module(), objectId, "refs/tags/TagName^{}"); revision.Refs = new[] { gitRef }; var args = new GitArgumentBuilder("verify-tag") { gitRef.LocalName }; _module().GitExecutable.GetOutput(args).Returns(gitRef.LocalName); break; } case 2: { // Two tag that's also IsDereference == true var gitRef1 = new GitRef(_module(), objectId, "refs/tags/FirstTag^{}"); var args = new GitArgumentBuilder("verify-tag") { gitRef1.LocalName }; _module().GitExecutable.GetOutput(args).Returns(gitRef1.LocalName); var gitRef2 = new GitRef(_module(), objectId, "refs/tags/SecondTag^{}"); revision.Refs = new[] { gitRef1, gitRef2 }; args = new GitArgumentBuilder("verify-tag") { gitRef2.LocalName }; _module().GitExecutable.GetOutput(args).Returns(gitRef2.LocalName); break; } } var actual = _gpgController.GetTagVerifyMessage(revision); Assert.AreEqual(expected, actual); }
private void LoadFileHistory() { FileChanges.Visible = true; if (string.IsNullOrEmpty(FileName)) { return; } _asyncLoader.LoadAsync( () => BuildFilter(), filter => { FileChanges.SetFilters(filter); FileChanges.Load(); }); return; (string revision, string path) BuildFilter() { var fileName = FileName; // Replace windows path separator to Linux path separator. // This is needed to keep the file history working when started from file tree in // browse dialog. FileName = fileName.ToPosixPath(); var res = (revision : (string)null, path : $" \"{fileName}\""); var fullFilePath = _fullPathResolver.Resolve(fileName); if (AppSettings.FollowRenamesInFileHistory && !Directory.Exists(fullFilePath)) { // git log --follow is not working as expected (see http://kerneltrap.org/mailarchive/git/2009/1/30/4856404/thread) // // But we can take a more complicated path to get reasonable results: // 1. use git log --follow to get all previous filenames of the file we are interested in // 2. use git log "list of files names" to get the history graph // // note: This implementation is quite a quick hack (by someone who does not speak C# fluently). // var args = new GitArgumentBuilder("log") { "--format=\"%n\"", "--name-only", "--follow", GitCommandHelpers.FindRenamesAndCopiesOpts(), "--", fileName.Quote() }; var listOfFileNames = new StringBuilder(fileName.Quote()); // keep a set of the file names already seen var setOfFileNames = new HashSet <string> { fileName }; var lines = Module.GitExecutable.GetOutputLines(args, outputEncoding: GitModule.LosslessEncoding); foreach (var line in lines.Select(GitModule.ReEncodeFileNameFromLossless)) { if (!string.IsNullOrEmpty(line) && setOfFileNames.Add(line)) { listOfFileNames.Append(" \""); listOfFileNames.Append(line); listOfFileNames.Append('\"'); } } // here we need --name-only to get the previous filenames in the revision graph res.path = listOfFileNames.ToString(); res.revision += " --name-only --parents" + GitCommandHelpers.FindRenamesAndCopiesOpts(); } else if (AppSettings.FollowRenamesInFileHistory) { // history of a directory // --parents doesn't work with --follow enabled, but needed to graph a filtered log res.revision = " " + GitCommandHelpers.FindRenamesOpt() + " --follow --parents"; } else { // rename following disabled res.revision = " --parents"; } if (AppSettings.FullHistoryInFileHistory) { res.revision = string.Concat(" --full-history ", AppSettings.SimplifyMergesInFileHistory ? "--simplify-merges " : string.Empty, res.revision); } return(res); } }
// This method is required to facilitate unit tests private IDictionary <string, AheadBehindData> GetData(Encoding encoding, string branchName = "") { if (branchName == null) { throw new ArgumentException(nameof(branchName)); } if (branchName == DetachedHeadParser.DetachedBranch) { return(null); } var aheadBehindGitCommand = new GitArgumentBuilder("for-each-ref") { $"--color=never", $"--format=\"{_refFormat}\"", "refs/heads/" + branchName }; var result = GetGitExecutable().GetOutput(aheadBehindGitCommand, outputEncoding: encoding); if (string.IsNullOrEmpty(result)) { return(null); } var matches = _aheadBehindRegEx.Matches(result); var aheadBehindForBranchesData = new Dictionary <string, AheadBehindData>(); foreach (Match match in matches) { var branch = match.Groups["branch"].Value; var remoteRef = (match.Groups["remote_p"].Success && !string.IsNullOrEmpty(match.Groups["remote_p"].Value)) ? match.Groups["remote_p"].Value : match.Groups["remote_u"].Value; if (string.IsNullOrEmpty(branch) || string.IsNullOrEmpty(remoteRef)) { continue; } #pragma warning disable SA1515 // Single-line comment should be preceded by blank line aheadBehindForBranchesData.Add(match.Groups["branch"].Value, new AheadBehindData { // The information is displayed in the push button, so the push info is preferred (may differ from upstream) Branch = branch, RemoteRef = remoteRef, AheadCount = // Prefer push to upstream for the count match.Groups["ahead_p"].Success // Single-line comment should be preceded by blank line ? match.Groups["ahead_p"].Value // If behind is set for push, ahead is null : match.Groups["behind_p"].Success ? string.Empty : match.Groups["ahead_u"].Success ? match.Groups["ahead_u"].Value // No information about the remote branch, it is gone : match.Groups["gone_p"].Success || match.Groups["gone_u"].Success ? AheadBehindData.Gone // If the printout is unknown (translated?), do not assume that there are "0" changes : (match.Groups["unk_p"].Success && !string.IsNullOrWhiteSpace(match.Groups["unk_p"].Value)) || (match.Groups["unk_u"].Success && !string.IsNullOrWhiteSpace(match.Groups["unk_u"].Value)) ? string.Empty // A remote exists, but "track" does not display the count if ahead/behind match : "0", // Behind do not track '0' or 'gone', only in Ahead BehindCount = match.Groups["behind_p"].Success ? match.Groups["behind_p"].Value : !match.Groups["ahead_p"].Success ? match.Groups["behind_u"].Value : string.Empty }); #pragma warning restore SA1515 } return(aheadBehindForBranchesData); }