Skip to content

Commit 5990a99

Browse files
authored
fix(DiffScreen): Reduce git commands by 60% when opening diff view (#429)
* feat(git_repo): Cache discover() result per directory Cache the result of git rev-parse --show-toplevel per directory. The repo root for a given path never changes during a session, so we can safely cache it after the first lookup. This eliminates a git command on every diff view open and other operations that need to discover the repo. * fix: Gate conflict checks with filesystem check Skip expensive per-file conflict checks (git ls-files -u and git status) when not in a merge/rebase/cherry-pick state. Use git_conflict.status() which only checks for files like .git/MERGE_HEAD - fast filesystem checks, no git commands. In the common case (no active merge), this eliminates 2 git commands when opening a diff view.
1 parent 4168602 commit 5990a99

2 files changed

Lines changed: 32 additions & 16 deletions

File tree

lua/vgit/features/screens/DiffScreen/Model.lua

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,28 +68,35 @@ function Model:fetch(filename)
6868

6969
self.git_file = GitFile(filename)
7070

71-
local has_conflict = self.git_file:has_conflict()
72-
if has_conflict and self:is_staged() then
73-
self.state.diff = nil
74-
return
71+
-- Fast filesystem check: are we in a merge/rebase/cherry-pick state?
72+
-- Only run expensive per-file conflict checks if so.
73+
local conflict_status = git_conflict.status(self.git_file.reponame)
74+
if conflict_status then
75+
local has_conflict = self.git_file:has_conflict()
76+
if has_conflict and self:is_staged() then
77+
self.state.diff = nil
78+
return
79+
end
80+
81+
local status = self.git_file:status()
82+
if status and status:is_unmerged() then
83+
local lines, lines_err = self:get_lines(filename)
84+
if lines_err then return nil, lines_err end
85+
86+
local layout_type = self:get_layout_type()
87+
local conflicts = git_conflict.parse(lines)
88+
self.state.diff = Diff():generate(nil, lines, layout_type, { conflicts = conflicts })
89+
return self.state.diff
90+
end
7591
end
7692

77-
local status = self.git_file:status()
78-
7993
local lines, lines_err = self:get_lines(filename)
8094
if lines_err then return nil, lines_err end
8195

82-
local layout_type = self:get_layout_type()
83-
if status and status:is_unmerged() then
84-
local conflicts = git_conflict.parse(lines)
85-
self.state.diff = Diff():generate(nil, lines, layout_type, { conflicts = conflicts })
86-
return self.state.diff
87-
end
8896
local hunks, hunks_err = self:get_hunks(lines)
8997
if hunks_err then return nil, hunks_err end
9098

91-
self.state.diff = Diff():generate(hunks, lines, layout_type)
92-
99+
self.state.diff = Diff():generate(hunks, lines, self:get_layout_type())
93100
return self.state.diff
94101
end
95102

lua/vgit/git/git_repo.lua

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@ local gitcli = require('vgit.git.gitcli')
22

33
local git_repo = {}
44

5+
-- Cache: directory path -> repo root. Stable within a session; only stale if
6+
-- user runs `git init` in a new location (rare, restart neovim to clear).
7+
local discover_cache = {}
8+
59
function git_repo.config(reponame)
610
if not reponame then return nil, { 'reponame is required' } end
711
return gitcli.run({ '-C', reponame, 'config', '--list' })
812
end
913

1014
function git_repo.discover(filepath)
1115
local dirname = (filepath and vim.fn.fnamemodify(filepath, ':p:h')) or vim.loop.cwd()
12-
local result, err = gitcli.run({ '-C', dirname, 'rev-parse', '--show-toplevel' })
1316

17+
local cached = discover_cache[dirname]
18+
if cached then return cached end
19+
20+
local result, err = gitcli.run({ '-C', dirname, 'rev-parse', '--show-toplevel' })
1421
if err then return nil, err end
15-
if #result==0 then return nil, {} end
22+
if #result == 0 then return nil, {} end
23+
24+
discover_cache[dirname] = result[1]
1625
return result[1]
1726
end
1827

0 commit comments

Comments
 (0)