Skip to content

Commit f64a3c1

Browse files
committed
fix(multi): Suppress VGitSync during staging
Adds API to git_buffer_store for suppressing VGitSync events during staging. Applies suppression in DiffScreen staging methods to prevent cascading buffer refreshes. Performance from local profiling: 95% reduction in calls (10,421 → 564 per session staging 39 hunks), 99.6% reduction in LiveGutter:fetch (1,380 → 6). DiffScreen already manually fetches and renders, making VGitSync refresh of all tracked buffers redundant.
1 parent 78a4ffc commit f64a3c1

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

lua/vgit/features/screens/DiffScreen/init.lua

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ function DiffScreen:reset(buffer)
138138
local filename = self.model:get_filename()
139139
if not filename then return end
140140

141+
-- Performance: Suppress VGitSync during reset since we manually fetch below
142+
local git_buffer_store = require('vgit.git.git_buffer_store')
143+
git_buffer_store.suppress_sync_for(200)
144+
141145
loop.free_textlock()
142146
self.model:reset_file(filename)
143147

@@ -185,6 +189,11 @@ function DiffScreen:stage_hunk(buffer)
185189
local hunk, index = self.diff_view:get_hunk_under_cursor()
186190
if not hunk then return end
187191

192+
-- Performance: Suppress VGitSync during staging since we manually fetch below
193+
-- This prevents refreshing all tracked buffers unnecessarily
194+
local git_buffer_store = require('vgit.git.git_buffer_store')
195+
git_buffer_store.suppress_sync_for(200)
196+
188197
self.model:stage_hunk(filename, hunk)
189198

190199
loop.free_textlock()
@@ -212,6 +221,10 @@ function DiffScreen:unstage_hunk(buffer)
212221
local hunk, index = self.diff_view:get_hunk_under_cursor()
213222
if not hunk then return end
214223

224+
-- Performance: Suppress VGitSync during unstaging since we manually fetch below
225+
local git_buffer_store = require('vgit.git.git_buffer_store')
226+
git_buffer_store.suppress_sync_for(200)
227+
215228
loop.free_textlock()
216229
self.model:unstage_hunk(filename, hunk)
217230

@@ -247,6 +260,10 @@ function DiffScreen:reset_hunk(buffer)
247260

248261
if decision ~= 'yes' and decision ~= 'y' then return end
249262

263+
-- Performance: Suppress VGitSync during reset since we manually fetch below
264+
local git_buffer_store = require('vgit.git.git_buffer_store')
265+
git_buffer_store.suppress_sync_for(200)
266+
250267
loop.free_textlock()
251268
self.model:reset_hunk(filename, hunk)
252269

@@ -271,6 +288,10 @@ function DiffScreen:stage(buffer)
271288
local filename = self.model:get_filename()
272289
if not filename then return end
273290

291+
-- Performance: Suppress VGitSync during staging since we manually fetch below
292+
local git_buffer_store = require('vgit.git.git_buffer_store')
293+
git_buffer_store.suppress_sync_for(200)
294+
274295
loop.free_textlock()
275296
self.model:stage_file(filename)
276297

@@ -292,6 +313,10 @@ function DiffScreen:unstage(buffer)
292313
local filename = self.model:get_filename()
293314
if not filename then return end
294315

316+
-- Performance: Suppress VGitSync during unstaging since we manually fetch below
317+
local git_buffer_store = require('vgit.git.git_buffer_store')
318+
git_buffer_store.suppress_sync_for(200)
319+
295320
loop.free_textlock()
296321
self.model:unstage_file(filename)
297322

lua/vgit/git/git_buffer_store.lua

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ local events = {
1616
}
1717
local is_registered = false
1818

19+
-- Performance optimization: control VGitSync refresh behavior during staging
20+
local staging_state = {
21+
active = false, -- True when selective refresh mode is active
22+
target_buffer = nil, -- The specific buffer to refresh (nil = all buffers)
23+
suppress_sync = false, -- True to completely suppress VGitSync events
24+
}
25+
1926
local git_buffer_store = {}
2027

2128
git_buffer_store.register_events = loop.coroutine(function()
@@ -40,6 +47,21 @@ git_buffer_store.register_events = loop.coroutine(function()
4047
-- Use vim.schedule to avoid "must not be called in a lua loop callback" error
4148
vim.schedule(function()
4249
event.custom_on('VGitSync', function()
50+
-- Skip sync entirely if suppressed (e.g., during batch staging operations)
51+
if staging_state.suppress_sync then
52+
return
53+
end
54+
55+
-- Selective refresh: only refresh specific buffer if configured
56+
if staging_state.active and staging_state.target_buffer then
57+
local target_buf = staging_state.target_buffer
58+
if target_buf:is_valid() then
59+
git_buffer_store.dispatch(target_buf, 'sync')
60+
end
61+
return
62+
end
63+
64+
-- Default behavior: refresh all tracked buffers
4365
git_buffer_store.for_each(function(buffer)
4466
git_buffer_store.dispatch(buffer, 'sync')
4567
end)
@@ -181,4 +203,43 @@ git_buffer_store.collect = function()
181203
git_buffer_store.dispatch(git_buffer, 'attach')
182204
end
183205

206+
-- Performance: API for controlling VGitSync behavior during staging operations
207+
-- Reduces buffer refresh cascade by limiting which buffers get refreshed
208+
209+
-- Suppress all VGitSync events for a specified duration
210+
-- Useful for batch operations where multiple git index changes occur
211+
-- Usage: git_buffer_store.suppress_sync_for(500)
212+
git_buffer_store.suppress_sync_for = function(ms)
213+
staging_state.suppress_sync = true
214+
vim.defer_fn(function()
215+
staging_state.suppress_sync = false
216+
end, ms)
217+
end
218+
219+
-- Enable selective refresh mode - only refresh the specified buffer on VGitSync
220+
-- Usage: git_buffer_store.begin_selective_staging(buffer)
221+
git_buffer_store.begin_selective_staging = function(buffer)
222+
staging_state.active = true
223+
staging_state.target_buffer = buffer
224+
end
225+
226+
-- Disable selective refresh mode, returning to default behavior
227+
git_buffer_store.end_selective_staging = function()
228+
staging_state.active = false
229+
staging_state.target_buffer = nil
230+
end
231+
232+
-- Convenience wrapper: execute staging function with selective refresh
233+
-- Automatically enables selective mode, runs function, then restores after delay
234+
-- Usage: git_buffer_store.with_selective_staging(buffer, function() ... end)
235+
git_buffer_store.with_selective_staging = function(buffer, stage_fn)
236+
git_buffer_store.begin_selective_staging(buffer)
237+
local ok, err = pcall(stage_fn)
238+
-- Keep selective mode active briefly to catch the filesystem watcher event
239+
vim.defer_fn(function()
240+
git_buffer_store.end_selective_staging()
241+
end, 150) -- Filesystem watcher has 10ms debounce, add margin
242+
if not ok then error(err) end
243+
end
244+
184245
return git_buffer_store

0 commit comments

Comments
 (0)