Skip to content

Commit

Permalink
Merge pull request #8427 from RussKie/fix_6310_sort_branches
Browse files Browse the repository at this point in the history
  • Loading branch information
RussKie committed Sep 13, 2020
2 parents ffd546c + a0f8eea commit 2d52621
Show file tree
Hide file tree
Showing 33 changed files with 793 additions and 206 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ tree.txt
*.binlog
artifacts/
.tools/vswhere/
.dotnet/
71 changes: 45 additions & 26 deletions GitCommands/Git/Commands/GitCommandHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GitCommands.Git;
using GitExtUtils;
using GitUIPluginInterfaces;
using JetBrains.Annotations;
Expand Down Expand Up @@ -81,39 +80,59 @@ public static ArgumentString AddSubmoduleCmd(string remotePath, string localPath
};
}

public static ArgumentString GetRefsCmd(bool tags, bool branches, bool noLocks)
public static ArgumentString GetRefsCmd(bool tags, bool branches, bool noLocks, GitRefsSortBy sortBy, GitRefsSortOrder sortOrder)
{
GitArgumentBuilder cmd;

if (tags)
{
cmd = new GitArgumentBuilder("show-ref", gitOptions:
noLocks && GitVersion.Current.SupportNoOptionalLocks
? (ArgumentString)"--no-optional-locks"
: default)
{
{ branches, "--dereference", "--tags" },
};
}
else if (branches)
string format;
if (!tags)
{
// branches only
cmd = new GitArgumentBuilder("for-each-ref", gitOptions:
noLocks && GitVersion.Current.SupportNoOptionalLocks
? (ArgumentString)"--no-optional-locks"
: default)
{
"--sort=-committerdate",
@"refs/heads/",
@"--format=""%(objectname) %(refname)"""
};
// If we don't need tags, it is easy.
format = @"--format=""%(objectname) %(refname)""";
}
else
{
throw new ArgumentException("GetRefs: Neither branches nor tags requested");
// ...however, if we're interested in tags, tags may be simple (in which case they are point to commits directly),
// or "dereferences" (i.e. commits that contian metadata and point to other commits, "^{}").
// Derefence commits do not contain date information, so we need to find information from the referenced commits (those with '*').
// So the following format is as follows:
// If (there is a 'authordate' information, then this is a simple tag/direct commit)
// Then
// format = %(objectname) %(refname)
// Else
// format = %(*objectname) %(*refname) // i.e. info from a referenced commit
// End
format = @"--format=""%(if)%(authordate)%(then)%(objectname) %(refname)%(else)%(*objectname) %(*refname)%(end)""";
}

GitArgumentBuilder cmd = new GitArgumentBuilder("for-each-ref",
gitOptions: noLocks && GitVersion.Current.SupportNoOptionalLocks
? (ArgumentString)"--no-optional-locks"
: default)
{
{ sortBy != GitRefsSortBy.Default, GetSortCriteria(tags, sortBy, sortOrder), string.Empty },
format,
{ branches, "refs/heads/", string.Empty },
{ branches && tags, "refs/remotes/", string.Empty },
{ tags, "refs/tags/", string.Empty },
};

return cmd;

static string GetSortCriteria(bool needTags, GitRefsSortBy sortBy, GitRefsSortOrder sortOrder)
{
if (!GitVersion.Current.SupportRefSort)
{
return string.Empty;
}

string order = sortOrder == GitRefsSortOrder.Ascending ? string.Empty : "-";
if (!needTags)
{
return $"--sort={order}{sortBy}";
}

// Sort by dereferenced data
return $"--sort={order}{sortBy} --sort={order}*{sortBy}";
}
}

public static ArgumentString RevertCmd(ObjectId commitId, bool autoCommit, int parentIndex)
Expand Down
51 changes: 8 additions & 43 deletions GitCommands/Git/GitModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2895,53 +2895,18 @@ public RemoteActionResult<IReadOnlyList<IGitRef>> GetRemoteServerRefs(string rem

public IReadOnlyList<IGitRef> GetRefs(bool tags = true, bool branches = true)
{
return GetRefs(tags, branches, false);
}

public IReadOnlyList<IGitRef> GetRefs(bool tags, bool branches, bool noLocks)
{
var refList = _gitExecutable.GetOutput(GitCommandHelpers.GetRefsCmd(tags: tags, branches: branches, noLocks: noLocks));
// We do not want to lock the repo for background operations.
// The primary use of 'noLocks' is to run git-status the commit count as a background operation,
// but to run the same in a foreground for FormCommit.
//
// Assume that all GetRefs() are done in the background, which may not be correct in the future.
const bool noLocks = true;

string cmd = GitCommandHelpers.GetRefsCmd(tags: tags, branches: branches, noLocks, AppSettings.RefsSortBy, AppSettings.RefsSortOrder);
var refList = _gitExecutable.GetOutput(cmd);
return ParseRefs(refList);
}

/// <param name="option">Order by date is slower.</param>
public IReadOnlyList<IGitRef> GetTagRefs(GetTagRefsSortOrder option)
{
var list = GetRefs(true, false);

switch (option)
{
case GetTagRefsSortOrder.ByCommitDateAscending:
return list.OrderBy(GetDate).ToList();
case GetTagRefsSortOrder.ByCommitDateDescending:
return list.OrderByDescending(GetDate).ToList();
default:
return list;
}

// BUG this sorting logic has no effect as CommitDate is not set by the GitRevision constructor
DateTime GetDate(IGitRef head) => new GitRevision(head.ObjectId).CommitDate;
}

public enum GetTagRefsSortOrder
{
/// <summary>
/// default
/// </summary>
ByName,

/// <summary>
/// slower than ByName
/// </summary>
ByCommitDateAscending,

/// <summary>
/// slower than ByName
/// </summary>
ByCommitDateDescending
}

public async Task<string[]> GetMergedBranchesAsync(bool includeRemote = false, bool fullRefname = false, string commit = null)
=> (await _gitExecutable
.GetOutputAsync(GitCommandHelpers.MergedBranchesCmd(includeRemote, fullRefname, commit))
Expand Down
7 changes: 5 additions & 2 deletions GitCommands/Git/GitVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ public class GitVersion : IComparable<GitVersion>
private static readonly GitVersion v2_7_0 = new GitVersion("2.7.0");
private static readonly GitVersion v2_9_0 = new GitVersion("2.9.0");
private static readonly GitVersion v2_11_0 = new GitVersion("2.11.0");
private static readonly GitVersion v2_14_6 = new GitVersion("2.14.6");
private static readonly GitVersion v2_15_0 = new GitVersion("2.15.0");
private static readonly GitVersion v2_15_2 = new GitVersion("2.15.2");
private static readonly GitVersion v2_19_0 = new GitVersion("2.19.0");
private static readonly GitVersion v2_20_0 = new GitVersion("2.20.0");

public static readonly GitVersion LastSupportedVersion = v2_11_0;
public static readonly GitVersion LastRecommendedVersion = new GitVersion("2.25.1");
public static readonly GitVersion LastSupportedVersion = v2_19_0;
public static readonly GitVersion LastRecommendedVersion = new GitVersion("2.28.0");

private static GitVersion _current;

Expand Down Expand Up @@ -128,6 +129,8 @@ int Get(IReadOnlyList<int> values, int index)

public bool SupportRebaseMerges => this >= v2_19_0;

public bool SupportRefSort => this >= v2_14_6;

public bool SupportGuiMergeTool => this >= v2_20_0;

public bool IsUnknown => _a == 0 && _b == 0 && _c == 0 && _d == 0;
Expand Down
3 changes: 0 additions & 3 deletions GitCommands/IDiffListSortService.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GitCommands
{
Expand Down
12 changes: 12 additions & 0 deletions GitCommands/Settings/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,18 @@ public static bool UseConsoleEmulatorForCommands
set => SetBool("UseConsoleEmulatorForCommands", value);
}

public static GitRefsSortBy RefsSortBy
{
get => GetEnum("RefsSortBy", GitRefsSortBy.Default);
set => SetEnum("RefsSortBy", value);
}

public static GitRefsSortOrder RefsSortOrder
{
get => GetEnum("RefsSortOrder", GitRefsSortOrder.Descending);
set => SetEnum("RefsSortOrder", value);
}

public static DiffListSortType DiffListSorting
{
get => GetEnum("DiffListSortType", DiffListSortType.FilePath);
Expand Down
73 changes: 73 additions & 0 deletions GitUI/BranchTreePanel/ContextMenu/GitRefsSortByContextMenuItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Linq;
using System.Windows.Forms;
using GitCommands;
using GitCommands.Utils;
using GitUI.Properties;
using GitUIPluginInterfaces;

namespace GitUI.BranchTreePanel.ContextMenu
{
internal class GitRefsSortByContextMenuItem : ToolStripMenuItem
{
private readonly Action _onSortByChanged;

public GitRefsSortByContextMenuItem(Action onSortByChanged)
{
_onSortByChanged = onSortByChanged;

Image = Images.SortBy;
Text = Strings.SortBy;

foreach (var option in EnumHelper.GetValues<GitRefsSortBy>().Select(e => (Text: e.GetDescription(), Value: e)))
{
var item = new ToolStripMenuItem()
{
Text = option.Text,
Image = null,
Tag = option.Value
};

item.Click += Item_Click;
DropDownItems.Add(item);
}

DropDownOpening += (s, e) => RequerySortingMethod();
RequerySortingMethod();
}

private void RequerySortingMethod()
{
var currentSort = AppSettings.RefsSortBy;
foreach (ToolStripMenuItem item in DropDownItems)
{
item.Checked = currentSort.Equals(item.Tag);
}
}

private void Item_Click(object sender, EventArgs e)
{
if (sender is ToolStripMenuItem item)
{
var sortingType = (GitRefsSortBy)item.Tag;
AppSettings.RefsSortBy = sortingType;

_onSortByChanged?.Invoke();
}
}

internal TestAccessor GetTestAccessor() => new TestAccessor(this);

internal struct TestAccessor
{
private readonly GitRefsSortByContextMenuItem _contextMenuItem;

public TestAccessor(GitRefsSortByContextMenuItem menuitem)
{
_contextMenuItem = menuitem;
}

public void RaiseDropDownOpening() => _contextMenuItem.RequerySortingMethod();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.Linq;
using System.Windows.Forms;
using GitCommands;
using GitCommands.Utils;
using GitUI.Properties;
using GitUIPluginInterfaces;

namespace GitUI.BranchTreePanel.ContextMenu
{
internal class GitRefsSortOrderContextMenuItem : ToolStripMenuItem
{
internal const string MenuItemName = "GitRefsSortOrderContextMenuItem";
private readonly Action _onSortOrderChanged;

public GitRefsSortOrderContextMenuItem(Action onSortOrderChanged)
{
_onSortOrderChanged = onSortOrderChanged;

Image = Images.SortBy;
Text = Strings.SortOrder;
Name = MenuItemName;

foreach (var option in EnumHelper.GetValues<GitRefsSortOrder>().Select(e => (Text: e.GetDescription(), Value: e)))
{
var item = new ToolStripMenuItem()
{
Text = option.Text,
Image = null,
Tag = option.Value
};

item.Click += Item_Click;
DropDownItems.Add(item);
}

DropDownOpening += (s, e) => RequerySortingMethod();
RequerySortingMethod();
}

private void RequerySortingMethod()
{
var currentSort = AppSettings.RefsSortOrder;
foreach (ToolStripMenuItem item in DropDownItems)
{
item.Checked = currentSort.Equals(item.Tag);
}
}

private void Item_Click(object sender, EventArgs e)
{
if (sender is ToolStripMenuItem item)
{
var sortingType = (GitRefsSortOrder)item.Tag;
AppSettings.RefsSortOrder = sortingType;

_onSortOrderChanged?.Invoke();
}
}

internal TestAccessor GetTestAccessor() => new TestAccessor(this);

internal struct TestAccessor
{
private readonly GitRefsSortOrderContextMenuItem _contextMenuItem;

public TestAccessor(GitRefsSortOrderContextMenuItem menuitem)
{
_contextMenuItem = menuitem;
}

public void RaiseDropDownOpening() => _contextMenuItem.RequerySortingMethod();
}
}
}

0 comments on commit 2d52621

Please sign in to comment.