diff --git a/GitUI/CommandsDialogs/BrowseDialog/GitStatusMonitor.cs b/GitUI/CommandsDialogs/BrowseDialog/GitStatusMonitor.cs index 40a76f360cd..a42e81f1ae2 100644 --- a/GitUI/CommandsDialogs/BrowseDialog/GitStatusMonitor.cs +++ b/GitUI/CommandsDialogs/BrowseDialog/GitStatusMonitor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using GitCommands; @@ -12,7 +12,12 @@ public partial class GitStatusMonitor : IDisposable /// We often change several files at once. /// Wait a second so they're all changed before we try to get the status. /// - private const int UpdateDelay = 500; + private const int UpdateDelay = 1000; + + /// + /// Minimum interval between subsequent updates + /// + private static readonly int MinUpdateInterval = 3000; /// /// Update every 5min, just to make sure something didn't slip through the cracks. @@ -28,7 +33,10 @@ public partial class GitStatusMonitor : IDisposable private bool _ignoredFilesAreStale; private string _gitPath; private string _submodulesPath; + //Timestamps to schedule status updates, limit the update interval dynamically private int _nextUpdateTime; + private int _prevUpdateTime; + private int _currUpdateInterval = MinUpdateInterval; private GitStatusMonitorState _currentStatus; private HashSet _ignoredFiles = new HashSet(); @@ -46,6 +54,7 @@ public partial class GitStatusMonitor : IDisposable public GitStatusMonitor() { + _prevUpdateTime = 0; InitializeComponent(); ignoredFilesTimer.Interval = MaxUpdatePeriod; @@ -117,7 +126,7 @@ private GitStatusMonitorState CurrentStatus _workTreeWatcher.EnableRaisingEvents = true; _gitDirWatcher.EnableRaisingEvents = !_gitDirWatcher.Path.StartsWith(_workTreeWatcher.Path); _globalIgnoreWatcher.EnableRaisingEvents = !string.IsNullOrWhiteSpace(_globalIgnoreWatcher.Path); - ScheduleDeferredUpdate(); + ScheduleNext(UpdateDelay); } break; @@ -132,7 +141,6 @@ private GitStatusMonitorState CurrentStatus private IGitUICommandsSource UICommandsSource { get; set; } - public void Init(IGitUICommandsSource commandsSource) { if (commandsSource == null) @@ -144,7 +152,6 @@ public void Init(IGitUICommandsSource commandsSource) commandsSource_activate(commandsSource); } - protected void OnGitStatusMonitorStateChanged(GitStatusMonitorStateEventArgs e) { GitStatusMonitorStateChanged?.Invoke(this, e); @@ -155,13 +162,12 @@ protected void OnGitWorkingDirectoryStatusChanged(GitWorkingDirectoryStatusEvent GitWorkingDirectoryStatusChanged?.Invoke(this, e); } - private void GlobalIgnoreChanged(object sender, FileSystemEventArgs e) { if (e.FullPath == _globalIgnoreFilePath) { _ignoredFilesAreStale = true; - ScheduleDeferredUpdate(); + ScheduleNext(UpdateDelay); } } @@ -211,6 +217,8 @@ private void StartWatchingChanges(string workTreePath, string gitDirPath) } _gitPath = Path.GetDirectoryName(gitDirPath); _submodulesPath = Path.Combine(_gitPath, "modules"); + _currUpdateInterval = MinUpdateInterval; + _prevUpdateTime = 0; UpdateIgnoredFiles(true); ignoredFilesTimer.Stop(); ignoredFilesTimer.Start(); @@ -274,13 +282,14 @@ private void Update() _commandIsRunning = true; _statusIsUpToDate = true; + _prevUpdateTime = Environment.TickCount; if (_ignoredFilesAreStale) { UpdateIgnoredFiles(false); } AsyncLoader.DoAsync(RunStatusCommand, UpdatedStatusReceived, OnUpdateStatusError); - // Always update every 5 min, even if we don't know anything changed - ScheduleNextJustInCaseUpdate(); + // Schedule update every 5 min, even if we don't know that anything changed + ScheduleNext(MaxUpdatePeriod); } } @@ -298,41 +307,32 @@ private void OnUpdateStatusError(AsyncErrorEventArgs e) private void UpdatedStatusReceived(string updatedStatus) { + //Adjust the interval between updates. (This does not affect an update already scheculed). + _currUpdateInterval = Math.Max(MinUpdateInterval, 3 * (Environment.TickCount - _prevUpdateTime)); _commandIsRunning = false; if (CurrentStatus != GitStatusMonitorState.Running) return; - if (_statusIsUpToDate) + var allChangedFiles = GitCommandHelpers.GetAllChangedFilesFromString(Module, updatedStatus); + OnGitWorkingDirectoryStatusChanged(new GitWorkingDirectoryStatusEventArgs(allChangedFiles)); + if (!_statusIsUpToDate) { - var allChangedFiles = GitCommandHelpers.GetAllChangedFilesFromString(Module, updatedStatus); - OnGitWorkingDirectoryStatusChanged(new GitWorkingDirectoryStatusEventArgs(allChangedFiles)); + //Still not up-to-date, but present what received, GetAllChangedFilesCmd() is the heavy command + ScheduleNext(UpdateDelay); } - else - { - UpdateImmediately(); - } - } - - private void ScheduleNextJustInCaseUpdate() - { - _nextUpdateTime = Environment.TickCount + MaxUpdatePeriod; } - private void ScheduleDeferredUpdate() + private void ScheduleNext(int delay) { - _nextUpdateTime = Environment.TickCount + UpdateDelay; - } - - private void ScheduleImmediateUpdate() - { - _nextUpdateTime = Environment.TickCount; - } - - private void UpdateImmediately() - { - ScheduleImmediateUpdate(); - Update(); + var next = Environment.TickCount + delay; + if(_nextUpdateTime > Environment.TickCount) + { + //A time is already set, use closest + next = Math.Min(_nextUpdateTime, next); + } + //Enforce a minimal time between updates, to not update too frequently + _nextUpdateTime = Math.Max(next, _prevUpdateTime + _currUpdateInterval); } private void commandsSource_GitUICommandsChanged(object sender, GitUICommandsChangedEventArgs e) @@ -380,8 +380,8 @@ private void GitDirChanged(object sender, FileSystemEventArgs e) if (e.FullPath.StartsWith(_submodulesPath) && (Directory.Exists(e.FullPath))) return; - ScheduleDeferredUpdate(); - } + ScheduleNext(UpdateDelay); + } private void GitUICommands_PreCheckout(object sender, GitUIBaseEventArgs e) { @@ -411,11 +411,17 @@ private void timerRefresh_Tick(object sender, EventArgs e) // Called for instance at buffer overflow private void WorkTreeWatcherError(object sender, ErrorEventArgs e) { - ScheduleDeferredUpdate(); + ScheduleNext(UpdateDelay); } private void WorkTreeChanged(object sender, FileSystemEventArgs e) { + //Update already scheduled? + if (_nextUpdateTime < Environment.TickCount + UpdateDelay) + return; + + //TODO Smarter detection of ignored files created since the files were created + //Parse .gitignore? var fileName = e.FullPath.Substring(_workTreeWatcher.Path.Length).ToPosixPath(); if (_ignoredFiles.Contains(fileName)) return; @@ -434,7 +440,7 @@ private void WorkTreeChanged(object sender, FileSystemEventArgs e) if (e.FullPath.EndsWith("\\.git\\index.lock")) return; - ScheduleDeferredUpdate(); + ScheduleNext(UpdateDelay); } } } \ No newline at end of file