Skip to content

Commit

Permalink
gitextensions#5125 RevisionGrid Graph: Nearest branch in tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
mstv committed Nov 14, 2018
1 parent f37989c commit debef3a
Show file tree
Hide file tree
Showing 3 changed files with 428 additions and 9 deletions.
122 changes: 122 additions & 0 deletions GitUI/UserControls/RevisionGrid/Graph/LaneInfoProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ namespace GitUI.UserControls.RevisionGrid.Graph
internal sealed class LaneInfoProvider
{
private static readonly TranslationString NoInfoText = new TranslationString("Sorry, this commit seems to be not loaded.");
private static readonly TranslationString MergedWithText = new TranslationString("merged with");
internal static readonly TranslationString ByPullRequestText = new TranslationString("by pull request");
private readonly ILaneNodeLocator _nodeLocator;

public LaneInfoProvider(ILaneNodeLocator nodeLocator)
Expand Down Expand Up @@ -40,6 +42,17 @@ public string GetLaneInfo(int rowIndex, int lane)
}

laneInfoText.AppendLine(node.GitRevision.Guid);

var branch = new BranchFinder(node);
if (branch.CommittedTo.IsNotNullOrWhitespace())
{
laneInfoText.AppendFormat("\n{0}: {1}", Strings.Branch, branch.CommittedTo);
if (branch.MergedWith.IsNotNullOrWhitespace())
{
laneInfoText.AppendFormat(" ({0} {1})", MergedWithText.Text, branch.MergedWith);
}
}

laneInfoText.AppendLine();
}

Expand All @@ -62,6 +75,115 @@ public string GetLaneInfo(int rowIndex, int lane)
internal readonly struct TestAccessor
{
internal static TranslationString NoInfoText => LaneInfoProvider.NoInfoText;
internal static TranslationString MergedWithText => LaneInfoProvider.MergedWithText;
internal static TranslationString ByPullRequestText => LaneInfoProvider.ByPullRequestText;
}
}

internal class BranchFinder
{
private static readonly Regex MergeRegex = new Regex("(?i)^merged? (pull request (.*) from )?(.*branch |tag )?'?([^ ']*[^ '.])'?( of [^ ]*[^ .])?( into (.*[^.]))?\\.?$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
private readonly HashSet<RevisionGraphRevision> _visitedMergeNodes = new HashSet<RevisionGraphRevision>();

internal BranchFinder([NotNull] RevisionGraphRevision node)
{
FindBranchRecursively(node, parent: null);
}

internal string CommittedTo { get; private set; }
internal string MergedWith { get; private set; }

private bool FindBranchRecursively([NotNull] RevisionGraphRevision node, [CanBeNull] RevisionGraphRevision parent)
{
if (node.Parents.Count > 1 && !_visitedMergeNodes.Add(node))
{
return false;
}

if (CheckForMerge(node, parent) || FindBranch(node))
{
return true;
}

foreach (var child in node.Children)
{
// handle the child and its children
if (FindBranchRecursively(child, parent: node))
{
return true;
}
}

return false;
}

private bool FindBranch([NotNull] RevisionGraphRevision node)
{
foreach (var gitReference in node.GitRevision.Refs)
{
if (gitReference.IsHead || gitReference.IsRemote || gitReference.IsStash)
{
CommittedTo = gitReference.Name;
return true;
}
}

return false;
}

/// <summary>
/// Checks whether the commit message is a merge message
/// and then if its a merge message, sets CommittedTo and MergedWith.
///
/// MergedWith is set if it is the current node, i.e. on the first call.
/// MergedWith is set to string.Empty if it is no merge.
/// First/second branch does not matter because it is the message of the current node.
/// </summary>
/// <param name="node">the node of the revision to evaluate</param>
/// <param name="parent">
/// the node's parent in the branch which is currently descended
/// (used for the decision whether the node belongs to the first or second branch of the merge)
/// </param>
private bool CheckForMerge([NotNull] RevisionGraphRevision node, [CanBeNull] RevisionGraphRevision parent)
{
bool isTheFirstBranch = parent == null || node.Parents.Count == 0 || node.Parents.First() == parent;
string mergedInto;
string mergedWith;
(mergedInto, mergedWith) = ParseMergeMessage(node.GitRevision.Subject, appendPullRequest: isTheFirstBranch);

if (mergedInto != null)
{
CommittedTo = isTheFirstBranch ? mergedInto : mergedWith;
}

if (MergedWith == null)
{
MergedWith = mergedWith ?? string.Empty;
}

return CommittedTo != null;
}

private static (string into, string with) ParseMergeMessage([NotNull] string commitSubject, bool appendPullRequest)
{
string into = null;
string with = null;
var match = MergeRegex.Match(commitSubject);
if (match.Success)
{
var matchPullRequest = match.Groups[2];
var matchWith = match.Groups[4];
var matchInto = match.Groups[7];
into = matchInto.Success ? matchInto.Value : "master";
with = matchWith.Success ? matchWith.Value : "?";
if (appendPullRequest && matchPullRequest.Success)
{
with += string.Format(" {0} {1}", LaneInfoProvider.ByPullRequestText.Text, matchPullRequest);
}
}

return (into, with);
}
}
}
10 changes: 3 additions & 7 deletions GitUI/UserControls/RevisionGrid/Graph/RevisionGraphRevision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ public RevisionGraphRevision(ObjectId objectId, int guessScore)
{
Objectid = objectId;

Parents = new ConcurrentBag<RevisionGraphRevision>();
Children = new ConcurrentBag<RevisionGraphRevision>();
StartSegments = new SynchronizedCollection<RevisionGraphSegment>();

Score = guessScore;

LaneColor = -1;
Expand Down Expand Up @@ -70,9 +66,9 @@ public int EnsureScoreIsAbove(int minimalScore)

public ObjectId Objectid { get; set; }

public ConcurrentBag<RevisionGraphRevision> Parents { get; }
public ConcurrentBag<RevisionGraphRevision> Children { get; }
public SynchronizedCollection<RevisionGraphSegment> StartSegments { get; }
public SynchronizedCollection<RevisionGraphRevision> Parents { get; } = new SynchronizedCollection<RevisionGraphRevision>();
public SynchronizedCollection<RevisionGraphRevision> Children { get; } = new SynchronizedCollection<RevisionGraphRevision>();
public SynchronizedCollection<RevisionGraphSegment> StartSegments { get; } = new SynchronizedCollection<RevisionGraphSegment>();

// Mark this commit, and all its parents, as relative. Used for branch highlighting.
// By default, the current checkout will be marked relative.
Expand Down
Loading

0 comments on commit debef3a

Please sign in to comment.