diff --git a/lua/lib/git.lua b/lua/lib/git.lua new file mode 100644 index 00000000..9aadef01 --- /dev/null +++ b/lua/lib/git.lua @@ -0,0 +1,93 @@ +local M = {} + +local roots = {} + +local not_git = 'not a git repo' + +local function update_root_status(root) + local status = vim.fn.systemlist('cd '..root..' && git status --porcelain=v1') + roots[root] = {} + + for _, v in pairs(status) do + local head = v:sub(0, 2) + local body = v:sub(4, -1) + if body:match('%->') ~= nil then + body = body:gsub('^.* %-> ', '') + end + roots[root][body] = head + end +end + +function M.reload_roots() + for root, status in pairs(roots) do + if status ~= not_git then + update_root_status(root) + end + end +end + +local function path_to_matching_str(path) + return path:gsub('(%-)', '(%%-)'):gsub('(%.)', '(%%.)') +end + +local function get_git_root(path) + if roots[path] then + return path, roots[path] + end + + for name, status in pairs(roots) do + if status ~= not_git then + if path:match(path_to_matching_str(name)) then + return name, status + end + end + end +end + +local function create_root(cwd) + local git_root = vim.fn.system('cd '..cwd..' && git rev-parse --show-toplevel') + if not git_root or #git_root == 0 or git_root:match('fatal: not a git repository') then + roots[cwd] = not_git + return false + end + update_root_status(git_root:sub(0, -2)) + return true +end + +function M.update_status(entries, cwd) + local git_root, git_status = get_git_root(cwd) + if not git_root then + if not create_root(cwd) then + return + end + git_root, git_status = get_git_root(cwd) + elseif git_status == not_git then + return + end + + local matching_cwd = path_to_matching_str(git_root..'/') + for _, node in pairs(entries) do + local relpath = node.absolute_path:gsub(matching_cwd, '') + if node.entries ~= nil then + relpath = relpath..'/' + node.git_status = nil + end + + local status = git_status[relpath] + if status then + node.git_status = status + elseif node.entries ~= nil then + local matcher = '^'..path_to_matching_str(relpath) + for key, _ in pairs(git_status) do + if key:match(matcher) then + node.git_status = 'dirty' + break + end + end + else + node.git_status = nil + end + end +end + +return M diff --git a/lua/lib/populate.lua b/lua/lib/populate.lua index f8ee64e1..f07eeabe 100644 --- a/lua/lib/populate.lua +++ b/lua/lib/populate.lua @@ -1,4 +1,5 @@ local config = require'lib.config' +local git = require'lib.git' local icon_config = config.get_icon_state() local api = vim.api @@ -190,52 +191,7 @@ function M.populate(entries, cwd) return end - M.update_git_status(entries, cwd) -end - -function M.update_git_status(entries, cwd) - local git_root = vim.fn.system('cd '..cwd..' && git rev-parse --show-toplevel') - if not git_root or #git_root == 0 or git_root:match('fatal: not a git repository') then - return - end - git_root = git_root:sub(0, -2) - - local git_statuslist = vim.fn.systemlist('cd '..cwd..' && git status --porcelain=v1') - local git_status = {} - - for _, v in pairs(git_statuslist) do - local head = v:sub(0, 2) - local body = v:sub(4, -1) - if body:match('%->') ~= nil then - body = body:gsub('^.* %-> ', '') - end - git_status[body] = head - end - - - local matching_cwd = path_to_matching_str(git_root..'/') - for _, node in pairs(entries) do - local relpath = node.absolute_path:gsub(matching_cwd, '') - if node.entries ~= nil then - relpath = relpath..'/' - node.git_status = nil - end - - local status = git_status[relpath] - if status then - node.git_status = status - elseif node.entries ~= nil then - local matcher = '^'..path_to_matching_str(relpath) - for key, _ in pairs(git_status) do - if key:match(matcher) then - node.git_status = 'dirty' - break - end - end - else - node.git_status = nil - end - end + git.update_status(entries, cwd) end return M diff --git a/lua/lib/tree.lua b/lua/lib/tree.lua index a72dddc8..25bb5590 100644 --- a/lua/lib/tree.lua +++ b/lua/lib/tree.lua @@ -3,10 +3,10 @@ local luv = vim.loop local renderer = require'lib.renderer' local config = require'lib.config' +local git = require'lib.git' local pops = require'lib.populate' local populate = pops.populate local refresh_entries = pops.refresh_entries -local update_git = pops.update_git_status local M = {} @@ -98,7 +98,7 @@ function M.unroll_dir(node) end local function refresh_git(node) - update_git(node.entries, node.absolute_path or node.cwd) + git.update_status(node.entries, node.absolute_path or node.cwd) for _, entry in pairs(node.entries) do if entry.entries ~= nil then refresh_git(entry) @@ -122,6 +122,7 @@ function M.refresh_tree() refresh_nodes(M.Tree) -- end if config.get_icon_state().show_git_icon then + git.reload_roots() refresh_git(M.Tree) end if M.Tree.winnr ~= nil then