From 0c40e6d5d821633f673968f52caea392d0138367 Mon Sep 17 00:00:00 2001 From: Philippe Miossec Date: Thu, 7 Sep 2023 12:11:29 +0200 Subject: [PATCH] Rebase: improve perfs when displaying list of commits by retrieving all the commits data with only one `git log` call instead of one by commit. It is possible as when rebasing, all the commits belong to the same branch. Kept a fall back to previous way to retrieve commit data for 1 commit if commit is not in the one loaded. Results with 7448 commits rebased: 2s instead of 4m50 previously Fixes #10698 and #11196 --- GitCommands/Git/GitModule.cs | 65 +++++++++++++++++++---------------- GitCommands/RevisionReader.cs | 24 +++++++++++++ 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/GitCommands/Git/GitModule.cs b/GitCommands/Git/GitModule.cs index 51edba204b6..4e3b6af12f8 100644 --- a/GitCommands/Git/GitModule.cs +++ b/GitCommands/Git/GitModule.cs @@ -2143,42 +2143,47 @@ public IReadOnlyList GetInteractiveRebasePatchFiles() List patchFiles = new(); - if (todoCommits is not null) + if (todoCommits is null) { - string commentChar = EffectiveConfigFile.GetString("core.commentChar", "#"); + return patchFiles; + } - string? currentCommitShortHash = File.Exists(CurrentFilePath) ? File.ReadAllText(CurrentFilePath).Trim() : null; - var isCurrentFound = false; - foreach (string todoCommit in todoCommits) - { - if (todoCommit.StartsWith(commentChar)) - { - continue; - } + string commentChar = EffectiveConfigFile.GetString("core.commentChar", "#"); - string[] parts = todoCommit.Split(Delimiters.Space); + string[][] todoCommitsInfos = todoCommits.Where(l => !l.StartsWith(commentChar)) + .Select(l => l.Split(Delimiters.Space)) + .Where(p => p.Length >= 3) + .ToArray(); - if (parts.Length < 3) - { - continue; - } + string? currentCommitShortHash = File.Exists(CurrentFilePath) ? File.ReadAllText(CurrentFilePath).Trim() : null; + var isCurrentFound = false; - string commitHash = parts[1]; - CommitData? data = _commitDataManager.GetCommitData(commitHash, out var error); - var isApplying = currentCommitShortHash is not null && commitHash.StartsWith(currentCommitShortHash); - isCurrentFound |= isApplying; + RevisionReader reader = new(this, hasReflogSelector: false); + CancellationTokenSource cts = new(2 * 60_000); + Dictionary rebasedCommitsRevisions = + reader.GetRevisionsFromRange(todoCommitsInfos[0][1], todoCommitsInfos[^1][1], cts.Token) + .ToDictionary(r => r.Guid, r => r); - patchFiles.Add(new PatchFile - { - Author = error ?? data?.Author, - ObjectId = data?.ObjectId, - Subject = error ?? data?.Body, - Action = parts[0], - Date = error ?? data?.CommitDate.LocalDateTime.ToString(), - IsNext = isApplying, - IsApplied = !isCurrentFound, - }); - } + foreach (string[] parts in todoCommitsInfos) + { + string commitHash = parts[1]; + CommitData? data = rebasedCommitsRevisions.TryGetValue(commitHash, out GitRevision commitRevision) + ? _commitDataManager.CreateFromRevision(commitRevision, null) + : _commitDataManager.GetCommitData(commitHash, out var _); + + var isApplying = currentCommitShortHash is not null && commitHash.StartsWith(currentCommitShortHash); + isCurrentFound |= isApplying; + + patchFiles.Add(new PatchFile + { + Author = data?.Author, + ObjectId = data?.ObjectId, + Subject = data?.Body, + Action = parts[0], + Date = data?.CommitDate.LocalDateTime.ToString(), + IsNext = isApplying, + IsApplied = !isCurrentFound, + }); } return patchFiles; diff --git a/GitCommands/RevisionReader.cs b/GitCommands/RevisionReader.cs index 57216925bb4..1cb6866334f 100644 --- a/GitCommands/RevisionReader.cs +++ b/GitCommands/RevisionReader.cs @@ -97,6 +97,30 @@ public IReadOnlyCollection GetRevisionsFromList(IList unt return GetRevisionsFromArguments(arguments, cancellationToken); } + /// + /// Get the GitRevisions for all commits between 2 commits (boundaries included). + /// + /// older commit Id. + /// newer commit Id. + /// Cancellation cancellationToken. + /// List with GitRevisions. + public IReadOnlyCollection GetRevisionsFromRange(string olderCommitId, string newerCommitId, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(olderCommitId) || string.IsNullOrWhiteSpace(newerCommitId)) + { + return Array.Empty(); + } + + GitArgumentBuilder arguments = new("log") + { + "-z", + $"--pretty=format:\"{FullFormat}\"", + $"{olderCommitId}~..{newerCommitId}" + }; + + return GetRevisionsFromArguments(arguments, cancellationToken); + } + /// /// Get the GitRevisions for Git argument. ///