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
Show Stash in rev grid #10138
Show Stash in rev grid #10138
Conversation
40e3fa5
to
7d4f50d
Compare
I have not had a look at the details yet. |
Some measurements: To avoid adding the "index" commit and empty untracked, processing time added is about 10 ms, similar handling as for inserting untracked commits |
No, that is fine.
I have a problem explaining this setting, so not keen on it. |
OK, so explicitly request just the most recent stash. |
d71a6a1
to
2b610b2
Compare
Changed to show the latest stash if ShowStashes If (currently hidden) and
|
2b610b2
to
ccb43d8
Compare
squashed, rebased (mstv had pulled the latest updates so they could be reviewed...) and updated the description, removed draft. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick review, will need more time for a more in depth review
GitCommands/RevisionReader.cs
Outdated
public List<GitRevision> GetStashes(CancellationToken token) | ||
{ | ||
Debug.Assert(_hasReflogSelector, "_hasReflogSelector must be set to get the reflog selectors (to identify stashes)"); | ||
GitArgumentBuilder arguments = new("stash") | ||
{ | ||
"list", | ||
"-z", | ||
$"--pretty=format:\"{LogFormat}\"" | ||
}; | ||
|
||
List<GitRevision> stashes = new(); | ||
using (IProcess process = _module.GitCommandRunner.RunDetached(arguments, redirectOutput: true, outputEncoding: GitModule.LosslessEncoding)) | ||
{ | ||
byte[] buffer = new byte[4096]; | ||
|
||
foreach (ArraySegment<byte> chunk in process.StandardOutput.BaseStream.ReadNullTerminatedChunks(ref buffer)) | ||
{ | ||
token.ThrowIfCancellationRequested(); | ||
|
||
if (TryParseRevision(chunk, out GitRevision? revision)) | ||
{ | ||
stashes.Add(revision); | ||
} | ||
} | ||
} | ||
|
||
return stashes; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe this functionality belong in this class. Probably this logically belongs to GitModule
(explosed via IGitModule
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is leading revisions, just a different Git command than the git-log currently in the class.
So an instance of RevisionReader is needed regardless.
GitCommands/RevisionReader.cs
Outdated
/// <param name="untracked">commit list</param> | ||
/// <param name="token">Cancellation token</param> | ||
/// <returns>List with GitRevisions</returns> | ||
public List<GitRevision> GetRevisionsFromList(IList<ObjectId> untracked, CancellationToken token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe we need to materialise the list, and we can use the sequence just as well with a little tweak:
public List<GitRevision> GetRevisionsFromList(IEnumerable<ObjectId> untracked, CancellationToken token)
{
List<GitRevision> stashes = new();
string untrackedIds = string.Join(" ", untracked);
if (string.IsNullOrWhiteSpace(untrackedIds))
{
return stashes;
}
GitArgumentBuilder arguments = new("log")
{
"-z",
$"--pretty=format:\"{LogFormat}\"",
"--dirstat=files",
untrackedIds,
".",
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the benefit with this?
I find it easier to read checking the size (and slightly more efficient even if that is not a focus here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We constantly reading revisions, and this code is on the hot path. So we want to avoid memory churn as much as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not see any improvement for allocation in your proposal, just a slower check.
For performance and allocation when loading, the important path is the reading of revisions (in RevisionReader that is optimized with my best effort) and in RevGrid (can likely be a few tuning done). The general improvement is to avoid calling Git when not needed.
I am adding an if and a null allocation in this PR to the rev reading loop, which will add ms.
(Replacing String.Empty with "" and removing precompiled regex is a more obvious optimization...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made a simple refactoring and changed this code slightly.
The restored commit "LOST_FOUND_1" (and the index commit) is not returned by |
d8af884
to
d9df4d6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Thank you
Fixes #9873
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looked through on a phone in a browser. I don't have any major objections. Supportive of taking it in to the release.
Show stash commits in the revision grid, with menu options to apply, pop and drop stashes. Stashes are listed in parallel to refs and the first part reading logs, to not delay the revision display in the grid. The first stash is always shown similar to current implementation, other stashes are shown only if the parent commit is shown (so all stashes are chown with rev grid). (Except for the first grid and for reflog, the index commit is not shown. The internal commit with untracked files is hidden if it is empty for the first stashes. (Limited to not delay the revision display.) Further performance evaluation in the pull request.
6bcf9eb
to
bdda77b
Compare
Plan to merge this tomorrow |
Show stash commits in the revision grid, with menu options to apply, pop and drop stashes. Stashes are listed in parallel to refs and the first part reading logs, to not delay the revision display in the grid. The first stash is always shown similar to current implementation (with index/untracked commits), other stashes are shown only if the parent commit is shown (so all stashes are chown with rev grid). (Except for the first grid and for reflog, the index commit is not shown. The internal commit with untracked files is hidden if it is empty for the first stashes. (Limited to 10 to not delay the revision display.) Further performance evaluation in the pull request. (cherry picked from commit ad6671d)
Resolves #5371 (and #5785
Fixes #9873
Proposed changes
Show stash commits in the revision grid, with menu options to pop and drop stashes.
A stash is implemented in Git with 3 internal commit.
Some more explanations in GitUI\UserControls\RevisionGrid\RevisionGridControl.cs:988
The implementations I have seen do not expose the third commit with untracked files.
The PR includes this "untracked" commit (empty "untracked" are filtered) for the first 10 stashes (see Performance below for details).
This setting is read from the settings but no setter is exposed, mostly because there is no natural place to show it (maybe in View is the best, but other similar settings are in General, that is considered full).
The implementation by default only show the stashes where the parents are shown in the grid.
So for instance if you stash and then rebase then the stash is not shown (but see below).
Unless reflogs are shown, only stashes that are related to commits visible in the grid is shown.
(Another difference with reflog is that stashes are presented in Git order, the "inserted" stashes are always shown related to the main commit).
The label for stash commits is partly for this reason set to the reflog selector like "stash@{0}" (required to pop/drop stashes) (another reason is to not get lost too easy when looking at commits, especially when many stashes are created for the same commit).
(For instance GitKraken shows these commits.)
FormStash can still be used to see these, they could also be added to the side panel (similar to SourceTree) (but the contents would not be shown). See #5460.
(Sublime merge shows both in grid and sidepanel)
GE currently always show the latest stash, this was expected by mstv in the initial version.
The latest stash is therefore always shown if stashes are shown.
This commit will always include the index and untracked commits.
See #10138 (comment) for a discussion about how that could be changed.
There is no new menu operation to create a stash, use stash button or Ctrl-Alt-Up to create a stash.
Create a stash operate on both Index and WorkTree commits, so there is no natural way to connect this to a revision (but partial commit should be possible to create in RevDiff).
FormStash was improved in #9303 too.
Performance
Loading revisions in 4.0 is heavily optimized, some performance measurements to ensure there is no regression.
(I have tried to make sure the addition has minimal impact on performance).
My GE repo has 19400 commits and 542 stashes, testing on Win10 with Zen2600, 16GB, SSD.
I consider 542 stashes to be an upper limit to what is reasonable to test with.
If stashes are not displayed, basically one null variable is, adding less than 1 ms to process all revisions. So usecases not showing stashes are not affected.
Enabled but no existing stashes has a few more checks, affects loading with about 9 ms.
Times are approximate, some variation (+/- 30 ms), but relative consistent differences.
Time will be affected by number of cores etc
Refreshing refs is done in three partly parallel steps:
Getting refs and checkout
Total step 175 ms (git-for-each-ref 125 ms)
Getting Stash commit (10 stash checked for untracked)
Total step 175 ms (git-stash-list 95 ms, git-log 70 ms)
If this time is higher than step 1 the load time is increasing.
Note that the second Git command for untracked is not needed if reflog is shown.
Getting revisions
Total git-log time 600 ms but first revision is delivered and could be processed for display after 140 ms (this is when the spinner is replaced with "Loading" in upper right corner) (as refs are slower, revgrid will be updated 35 ms after this)
(if sorting in the grid is enabled, revisions will be shown with at least a 100 ms delay)
Processing time to find if stashes should be added is about 9 ms.
The GUI update is done in about the time that git-log runs.
The "Loading" label is then removed, but some additional CPU may be needed for selecting a new revision (if filtering changed).
Stashes slow down showing revisions in the grid with about 9 ms plus possible additional time due to parallel processing overhead (but I have not seen any faster ref handling with stash disabled).
This is assuming that the 10 "untracked" takes the same time as getting refs, which in my case is 10, which I also believe is an OK value of untracked to show.
0 untracked limits step 2 with 60 ms (but revisions are not shown earlier), 50 untracked adds 30 ms and all 542 adds 400 ms.
Brief tests with Linux repo in wsl limited to 100000 commits shown with one local branch and 10 stashes.
With so few refs, refs are handled in 150 ms and stash-list in 170 and listing untracked will add 100 ms and 50 ms (processing adding to grid) out of total 4.1 s. So hardly noticeable.
Screenshots
With reflogs shown, the change is that labels and menu operations are enabled for all commits.
Before
Latest stash shown
After
If parent commit is not shown, stash is not shown (first is shown though, not updated example)
Other commits are shown, "untracked" commit only if it is not empty
Menu
Test methodology
A few tests added, some modified.
Test environment(s)
Git 2.37.1
No other version tested, nothing seen in the Git doc about any compatibility.
Merge strategy
I agree that the maintainer squash merge this PR (if the commit message is clear).
✒️ I contribute this code under The Developer Certificate of Origin.