Skip to content

Conversation

@AlanIWBFT
Copy link

@AlanIWBFT AlanIWBFT commented Dec 26, 2025

Finally this experiment has come to a stage where it's reviewable. It is still very early. Opinions are welcome. Binaries for testing (win-x64 only) can be downloaded here: https://github.com/AlanIWBFT/sourcegit/releases

Problem it aims to solve: #1998

To accurately report and let the user pick each individual untracked file, SourceGit uses --untracked-files=all (-uall) when doing QueryLocalChanges, which is unfortunately very slow on large repositories. core.fsmonitor and core.untrackedcache cannot help this case if -uall is specified as it makes the untracked cache bypassed, yielding no performance improvement.
This pull request detects if core.fsmonitor and core.untrackedcache are enabled and uses a two pass algorithm: it runs a normal git status first to retrive a list of untracked directories (which is fast as untracked cache is functioning here), then run git status -uall only on these dirs. The time it takes to traverse these dirs are much faster than running -uall on the entire repository.

To use this experimental feature, the user must deliberately enable core.fsmonitor and core.untrackedcache, or not setting core.untrackedcache but setting feature.manyfiles to true.

Components

First, to robustly detect if the fast path is possible, Commands.Config.GetBoolAsync is added which leverages git itself to canonicalize possible weird values in config files, like "000" will be intepreted as "false", "010" -> "true".
Every time you open a repository the above detection is performed. If they are enabled, a normal, index-locking 'git status' will be run to populate/update the untracked cache. The first time will be slow as the cache needs to be populated entirely from empty. Subsequent runs will be much faster, in fact, negligible. (~0.4s on UE5-main). Thus the slow-down on UI start-up isn't very noticable expect for the first time. Since SourceGit always runs git status with --no-optional-locks which means the index will not be modified, the untracked cache thus cannot be updated either, once SourceGit is up. So we need a place to insert the update, and it is scheduled when you open the repository, as said above.

Performance results

On large repos like UE5, a consistent ~5x - ~6x speedup is observed whenever a status update is needed and the responsiveness is greatly improved.

Other fixes

As we've discussed in #1998 (comment), Stash.PushAsync bool includeUntracked's default value has been changed to false. The intention is to let git handle the untracked instead of manually stash them (which was performed with --included-untracked, very slow (2x slower than the slow status update)).

@AlanIWBFT
Copy link
Author

Oops, looks like I got the branch wrong and pulled in lots of commits. Fixing

@AlanIWBFT AlanIWBFT changed the base branch from master to develop December 26, 2025 13:45
@love-linger
Copy link
Collaborator

love-linger commented Dec 26, 2025 via email

@AlanIWBFT
Copy link
Author

Maintaining consistency can be hard. Afaik predicting what's changed is how UGit works. Since the background operation is slow, UGit needs to run in the background (minimized to tray) until the operation is done. If you try to stage something, it shows its staged, then if you exit UGit immediately and reopen it, you see your changes are not staged.
This pull request, while seems 'not elegant' at first glance, ensures the status you see is always real, i.e. no consistency issue to battle in the first place.

@AlanIWBFT
Copy link
Author

AlanIWBFT commented Dec 26, 2025

The real consistency issue emerges, when the user performs multiple ops in serial. Its like UE network prediction and rollback - if git status fails to catch up timely, you will need to design an algorithm to determine which user ops have failed and rollback to the specific point. If you don't allow such operation queues, the status update will be as slow as before once your user launched the second operation.
edit: example ops of such dependenices are: the user selects only some of the files in the shown status list to stage/unstage/dicard/stash
edit2: the most problematic case is when the user is mixing these ops while also changing files in the working copy - without a correct git status it isnt easy to do the predictions right

@AlanIWBFT AlanIWBFT closed this Dec 27, 2025
@AlanIWBFT AlanIWBFT deleted the twopass-test-3 branch December 27, 2025 05:52
@AlanIWBFT AlanIWBFT restored the twopass-test-3 branch December 27, 2025 05:55
@AlanIWBFT AlanIWBFT deleted the twopass-test-3 branch December 27, 2025 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants