Log window becomes unresponsive when selecting commit with large file list
What steps will reproduce the problem?
- Open log window for a repository containing commits that involve many files.
- Select (left/right click) the commit with many files changed.
- TortoiseGit log window stops responding for extended duration. For >100K files commit, this may even last minutes.
- Selecting another smaller commit, and coming back to the large commit results in similar wait.
I have created a test simulation repo containing varying sized commits up to 50K files at https://github.com/shinfd/largerepotest
Note that, due to its simplicity, the wait selecting 50K file commit from this seems to be much shorter than that of the work environment's equivalently sized commit.
What is the expected output? What do you see instead?
Log window resume responding to user input within few seconds.
Getting an equivalent changed file list for a very large commit quickly is possible in CLI git, with commands like
git log -m -1 --name-only --oneline COMMIT.
What version of TortoiseGit and Git are you using? On what operating system?
TortoiseGit 184.108.40.206 (C:\Program Files\TortoiseGit\bin)
git version 2.27.0.windows.1 (C:\Program Files\Git\bin; C:\Program Files\Git\mingw64; C:\Program Files\Git\etc\gitconfig)
on Windows 10 x64
Please provide any additional information below.
Such large commits are definitely not something recommended nor commonplace, but can be found in select occasions, such as migration from existing repository, or when changes are imported via automated scripts from other systems. Merging distant branches can also involve large file count. These commit tend to be important, and our integrator was experiencing long delay when touching such commit to do operations like rebasing or tagging.
Using performance profiler, TortoiseGitProc seems to be taking the most time in below 2 places.
- CListCtrl::GetStringWidth() is called for every column for each file in the changed file list. This function, called from CGitStatusListCtrl::Show() via CResizableColumnsListCtrl::AdjustColumnWidths() to auto-adjust column size, has to calculate width of the rendered string, taking in account the control's font setting. This is very time consuming for a large CListCtrl.
- LVITEM per each changed file is added to CListCtrl, in CGitStatusListCtrl::AddEntry() called from CGitStatusListCtrl::Show(). Though this is not as costly as the prior, expanding and constructing ListCtrl still becomes very demanding for a extremely large list.
Prior can be mitigated by limiting CListCtrl::GetStringWidth() calls, possibly for visible items only. Latter is harder to address; virtual list (LVS_OWNERDATA) is recommended for handling display of large list in Windows UI, but since CGitStatusListCtrl uses grouping -- which is not available for virtual lists -- this seems to be not viable without heavy modifications.
If virtual list cannot be used, I could suggest to add changed file list display count threshold, as a lightweight workaround. When file count is known to exceed number set in settings, message like "Too many files to display" is shown. I have attached an experimental udiff from REL_220.127.116.11_EXTERNAL to add such change, limiting log window's file list display to a fixed threshold (1000).