Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eliminate static methods in RevisionReader #10130

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
54 changes: 33 additions & 21 deletions GitCommands/RevisionReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,38 @@ public sealed class RevisionReader
/* Commit raw body */ "%B";

// Trace info for parse errors
private static int _noOfParseError = 0;
private int _noOfParseError = 0;

public async Task ExecuteAsync(
GitModule module,
private readonly GitModule _module;
private readonly Encoding _logOutputEncoding;
private readonly long _sixMonths;

public RevisionReader(GitModule module)
: this(module, module.LogOutputEncoding, new DateTimeOffset(DateTime.Now.ToUniversalTime() - TimeSpan.FromDays(30 * 6)).ToUnixTimeSeconds())
{
}

internal RevisionReader(GitModule module, Encoding logOutputEncoding, long sixMonths)
{
_module = module;
_logOutputEncoding = logOutputEncoding;
_sixMonths = sixMonths;
}

public async Task GetLogAsync(
IObserver<GitRevision> subject,
ArgumentBuilder arguments,
CancellationToken token)
{
await TaskScheduler.Default;
token.ThrowIfCancellationRequested();

var revisionCount = 0;

#if DEBUG
int revisionCount = 0;
var sw = Stopwatch.StartNew();
#endif

var logOutputEncoding = module.LogOutputEncoding;
long sixMonths = new DateTimeOffset(DateTime.Now.ToUniversalTime() - TimeSpan.FromDays(30 * 6)).ToUnixTimeSeconds();

using (var process = module.GitCommandRunner.RunDetached(arguments, redirectOutput: true, outputEncoding: GitModule.LosslessEncoding))
using (var process = _module.GitCommandRunner.RunDetached(arguments, redirectOutput: true, outputEncoding: GitModule.LosslessEncoding))
{
#if DEBUG
Debug.WriteLine($"git {arguments}");
Expand All @@ -69,10 +80,11 @@ public sealed class RevisionReader
{
token.ThrowIfCancellationRequested();

if (TryParseRevision(chunk, logOutputEncoding, sixMonths, out GitRevision? revision))
if (TryParseRevision(chunk, out GitRevision? revision))
{
#if DEBUG
revisionCount++;

#endif
subject.OnNext(revision);
}
}
Expand All @@ -89,7 +101,7 @@ public sealed class RevisionReader
}
}

public static ArgumentBuilder BuildArguments(int maxCount,
public ArgumentBuilder BuildArguments(int maxCount,
RefFilterOptions refFilterOptions,
string branchFilter,
string revisionFilter,
Expand Down Expand Up @@ -155,7 +167,7 @@ public sealed class RevisionReader
branchFilter.IndexOfAny(new[] { '?', '*', '[' }) == -1;
}

private static bool TryParseRevision(in ArraySegment<byte> chunk, in Encoding logOutputEncoding, long sixMonths, [NotNullWhen(returnValue: true)] out GitRevision? revision)
private bool TryParseRevision(in ArraySegment<byte> chunk, [NotNullWhen(returnValue: true)] out GitRevision? revision)
{
// The 'chunk' of data contains a complete git log item, encoded.
// This method decodes that chunk and produces a revision object.
Expand Down Expand Up @@ -275,15 +287,15 @@ long ParseUnixDateTime(in ReadOnlySpan<byte> array)
#region Encoded string values (names, emails, subject, body)

// Finally, decode the names, email, subject and body strings using the required text encoding
ReadOnlySpan<char> s = logOutputEncoding.GetString(array[offset..]).AsSpan();
ReadOnlySpan<char> s = _logOutputEncoding.GetString(array[offset..]).AsSpan();
StringLineReader reader = new(in s);

var author = reader.ReadLine();
var authorEmail = reader.ReadLine();
var committer = reader.ReadLine();
var committerEmail = reader.ReadLine();

bool skipBody = sixMonths > authorUnixTime;
bool skipBody = _sixMonths > authorUnixTime;
(string? subject, string? body, bool hasMultiLineMessage) = reader.PeekSubjectBody(skipBody);

// We keep a full multiline message body within the last six months.
Expand Down Expand Up @@ -317,7 +329,7 @@ long ParseUnixDateTime(in ReadOnlySpan<byte> array)

return true;

static void ParseAssert(string? message)
void ParseAssert(string? message)
{
_noOfParseError++;
Debug.Assert(_noOfParseError > 1, message);
Expand Down Expand Up @@ -402,13 +414,13 @@ internal TestAccessor(RevisionReader revisionReader)
_revisionReader = revisionReader;
}

internal static bool TryParseRevision(ArraySegment<byte> chunk, Encoding logOutputEncoding, long sixMonths, [NotNullWhen(returnValue: true)] out GitRevision? revision) =>
RevisionReader.TryParseRevision(chunk, logOutputEncoding, sixMonths, out revision);
internal bool TryParseRevision(ArraySegment<byte> chunk, [NotNullWhen(returnValue: true)] out GitRevision? revision) =>
_revisionReader.TryParseRevision(chunk, out revision);

internal static int NoOfParseError
internal int NoOfParseError
{
get { return _noOfParseError; }
set { _noOfParseError = value; }
get { return _revisionReader._noOfParseError; }
set { _revisionReader._noOfParseError = value; }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ void DrawItem()
int centerX = g.RenderingOrigin.X + (int)((currentRow.GetCurrentRevisionLane() + 0.5) * LaneWidth);
Rectangle nodeRect = new(centerX - (NodeDimension / 2), centerY - (NodeDimension / 2), NodeDimension, NodeDimension);

bool square = currentRow.Revision.HasRef;
bool hasOutline = currentRow.Revision.IsCheckedOut;
bool square = currentRow.Revision.GitRevision.Refs.Count > 0;
bool hasOutline = currentRow.Revision.GitRevision.ObjectId == _revisionGraph.HeadId;

Brush brush = GetBrushForLaneInfo(currentRowRevisionLaneInfo, currentRow.Revision.IsRelative);
if (square)
Expand Down
22 changes: 18 additions & 4 deletions GitUI/UserControls/RevisionGrid/Graph/RevisionGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public void LoadingCompleted()

public int Count => _nodes.Count;

public bool OnlyFirstParent { get; set; }
public ObjectId HeadId { get; set; }

/// <summary>
/// Checks whether the given hash is present in the graph.
/// </summary>
Expand Down Expand Up @@ -177,14 +180,25 @@ public void HighlightBranch(ObjectId id)
}
}

/// <summary>
/// Set HasNotes for all GitRevisions (marking Notes as fetched).
/// This is used when no Git Notes at all exist and notes never need to be retrieved.
/// </summary>
public void SetHasNotesForRevisions()
{
foreach (RevisionGraphRevision revision in _nodes)
{
revision.GitRevision.HasNotes = true;
}
}

/// <summary>
/// Add a single revision from the git log to the graph, including segments to parents.
/// </summary>
/// <param name="revision">The revision to add.</param>
/// <param name="types">The graph node flags.</param>
/// <param name="insertScore">Insert the (artificial) revision before the node with this score.</param>
/// <param name="insertRange">Number of scores "reserved" in the list when inserting.</param>
public void Add(GitRevision revision, RevisionNodeFlags types, int? insertScore = null, int insertRange = 0)
public void Add(GitRevision revision, int? insertScore = null, int insertRange = 0)
{
// The commits are sorted by the score (not contiuous numbering there may be gaps)
// This commit will be ordered after existing, _maxScore is a preliminary score
Expand Down Expand Up @@ -233,7 +247,7 @@ public void Add(GitRevision revision, RevisionNodeFlags types, int? insertScore

// This revision may have been added as a parent before. Probably only the ObjectId is known. Set all the other properties.
revisionGraphRevision.GitRevision = revision;
revisionGraphRevision.ApplyFlags(types);
revisionGraphRevision.ApplyFlags(isCheckedOut: HeadId == revision.ObjectId);

// Build the revisions parent/child structure. The parents need to added here. The child structure is kept in synch in
// the RevisionGraphRevision class.
Expand Down Expand Up @@ -265,7 +279,7 @@ public void Add(GitRevision revision, RevisionNodeFlags types, int? insertScore
revisionGraphRevision.AddParent(parentRevisionGraphRevision, out int newMaxScore);
_maxScore = Math.Max(_maxScore, newMaxScore);

if (types.HasFlag(RevisionNodeFlags.OnlyFirstParent))
if (OnlyFirstParent)
{
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,12 @@ public RevisionGraphRevision(ObjectId objectId, int guessScore)
Score = guessScore;
}

public void ApplyFlags(RevisionNodeFlags types)
public void ApplyFlags(bool isCheckedOut)
{
IsRelative |= (types & RevisionNodeFlags.CheckedOut) != 0;
HasRef = (types & RevisionNodeFlags.HasRef) != 0;
IsCheckedOut = (types & RevisionNodeFlags.CheckedOut) != 0;
IsRelative |= isCheckedOut;
}

public bool IsRelative { get; set; }
public bool HasRef { get; set; }
public bool IsCheckedOut { get; set; }

/// <summary>
/// The score is used to order the revisions in topo-order. The initial score will be assigned when a revision is loaded
Expand Down
14 changes: 2 additions & 12 deletions GitUI/UserControls/RevisionGrid/RevisionDataGridView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@

namespace GitUI.UserControls.RevisionGrid
{
[Flags]
public enum RevisionNodeFlags
{
None = 0,
CheckedOut = 1,
HasRef = 2,
OnlyFirstParent = 4
}

public sealed partial class RevisionDataGridView : DataGridView
{
private const int BackgroundThreadUpdatePeriod = 25;
Expand Down Expand Up @@ -321,11 +312,10 @@ private void OnCellPainting(object? sender, DataGridViewCellPaintingEventArgs e)
/// Update visible rows if needed.
/// </summary>
/// <param name="revision">The revision to add.</param>
/// <param name="types">The graph node flags.</param>
/// <param name="insertWithMatch">Insert the (artificial) revision with the first match in headParents or first if no match found (or headParents is null).</param>
/// <param name="insertRange">Number of scores "reserved" in the list when inserting.</param>
/// <param name="parents">Parent ids for the revision to find (and insert before).</param>
public void Add(GitRevision revision, RevisionNodeFlags types = RevisionNodeFlags.None, bool insertWithMatch = false, int insertRange = 0, IEnumerable<ObjectId>? parents = null)
public void Add(GitRevision revision, bool insertWithMatch = false, int insertRange = 0, IEnumerable<ObjectId>? parents = null)
{
// Where to insert the revision, null is last
int? insertScore = null;
Expand Down Expand Up @@ -358,7 +348,7 @@ public void Add(GitRevision revision, RevisionNodeFlags types = RevisionNodeFlag
}
}

_revisionGraph.Add(revision, types, insertScore, insertRange);
_revisionGraph.Add(revision, insertScore, insertRange);
if (ToBeSelectedObjectIds.Contains(revision.ObjectId))
{
++_loadedToBeSelectedRevisionsCount;
Expand Down
49 changes: 18 additions & 31 deletions GitUI/UserControls/RevisionGrid/RevisionGridControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -884,8 +884,6 @@ public void PerformRefreshRevisions(Func<RefsFilter, IReadOnlyList<IGitRef>> get
ILookup<ObjectId, IGitRef> refsByObjectId = null;
bool firstRevisionReceived = false;
bool headIsHandled = false;
bool hasAnyNotes = false;
bool onlyFirstParent = false;

// getRefs (refreshing from Browse) is Lazy already, but not from RevGrid (updating filters etc)
Lazy<IReadOnlyList<IGitRef>> getUnfilteredRefs = new(() => (getRefs ?? capturedModule.GetRefs)(RefsFilter.NoFilter));
Expand Down Expand Up @@ -959,9 +957,8 @@ public void PerformRefreshRevisions(Func<RefsFilter, IReadOnlyList<IGitRef>> get
UpdateSelectedRef(capturedModule, getUnfilteredRefs.Value, headRef);
_gridView.ToBeSelectedObjectIds = GetToBeSelectedRevisions(newCurrentCheckout, currentlySelectedObjectIds);

// optimize for no notes at all
hasAnyNotes = AppSettings.ShowGitNotes && getUnfilteredRefs.Value.Any(i => i.CompleteName == GitRefName.RefsNotesPrefix);
onlyFirstParent = _filterInfo.RefFilterOptions.HasFlag(RefFilterOptions.FirstParent);
_gridView._revisionGraph.OnlyFirstParent = _filterInfo.RefFilterOptions.HasFlag(RefFilterOptions.FirstParent);
_gridView._revisionGraph.HeadId = CurrentCheckout;

// Allow add revisions to the grid
semaphoreUpdateGrid.Release();
Expand All @@ -981,17 +978,17 @@ public void PerformRefreshRevisions(Func<RefsFilter, IReadOnlyList<IGitRef>> get
ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
{
await TaskScheduler.Default;
RevisionReader reader = new(capturedModule);
string pathFilter = BuildPathFilter(_filterInfo.PathFilter);
ArgumentBuilder args = RevisionReader.BuildArguments(_filterInfo.CommitsLimit,
ArgumentBuilder args = reader.BuildArguments(_filterInfo.CommitsLimit,
_filterInfo.RefFilterOptions,
_filterInfo.IsShowFilteredBranchesChecked ? _filterInfo.BranchFilter : string.Empty,
_filterInfo.GetRevisionFilter(),
pathFilter,
out bool parentsAreRewritten);
ParentsAreRewritten = parentsAreRewritten;

await new RevisionReader().ExecuteAsync(
capturedModule,
await reader.GetLogAsync(
observeRevisions,
args,
cancellationToken);
Expand Down Expand Up @@ -1147,44 +1144,23 @@ void OnRevisionRead(GitRevision revision)
if (!firstRevisionReceived)
{
// Wait for refs,CurrentCheckout to be available
this.InvokeAsync(() => { ShowLoading(showSpinner: false); }).FileAndForget();
semaphoreUpdateGrid.Wait(cancellationToken);
firstRevisionReceived = true;
this.InvokeAsync(() => { ShowLoading(showSpinner: false); }).FileAndForget();
}

RevisionNodeFlags flags = RevisionNodeFlags.None;

if (!headIsHandled && (revision.ObjectId.Equals(CurrentCheckout) || CurrentCheckout is null))
{
// Insert worktree/index before HEAD (CurrentCheckout)
// If grid is filtered and HEAD not visible, insert artificial in OnRevisionReadCompleted()
headIsHandled = true;
AddArtificialRevisions();
if (CurrentCheckout is not null)
{
flags = RevisionNodeFlags.CheckedOut;
}
}

// Look up any refs associated with this revision
revision.Refs = refsByObjectId[revision.ObjectId].AsReadOnlyList();
if (revision.Refs.Count != 0)
{
flags |= RevisionNodeFlags.HasRef;
}

if (onlyFirstParent)
{
flags |= RevisionNodeFlags.OnlyFirstParent;
}

if (!hasAnyNotes)
{
// No notes at all in this repo
revision.HasNotes = true;
}

_gridView.Add(revision, flags);
_gridView.Add(revision);

return;
}
Expand Down Expand Up @@ -1349,6 +1325,17 @@ void OnRevisionReadCompleted()
RevisionsLoaded?.Invoke(this, new RevisionLoadEventArgs(this, UICommands, getUnfilteredRefs, forceRefresh));
HighlightRevisionsByAuthor(GetSelectedRevisions());

await TaskScheduler.Default;

// optimize for no notes at all in repo
// Improvement: set .HasNotes for commits not included in git-notes
bool hasAnyNotes = AppSettings.ShowGitNotes && getUnfilteredRefs.Value.Any(i => i.CompleteName == GitRefName.RefsNotesPrefix);
if (!hasAnyNotes)
{
// No notes at all in this repo
_gridView._revisionGraph.SetHasNotesForRevisions();
}

if (ShowBuildServerInfo)
{
await _buildServerWatcher.LaunchBuildServerInfoFetchOperationAsync();
Expand Down