From b0da10c10dd3aa36ef05cf2dcac0b3145d34b57b Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 18:36:21 +0200 Subject: [PATCH 01/95] Add filewatcher based on gitsigns implementation. This should set up a single watcher on the .git/ dir, and run the callback fn whenever any files are changed. --- lua/neogit/watcher.lua | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 lua/neogit/watcher.lua diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua new file mode 100644 index 000000000..8010d4a70 --- /dev/null +++ b/lua/neogit/watcher.lua @@ -0,0 +1,70 @@ +local M = {} + +local uv = vim.loop + +local config = require("neogit.config") +local logger = require("neogit.logger") +local util = require("neogit.lib.util") +local status = require("neogit.status") +local Path = require("plenary.path") + +M.watcher = {} + +local function git_dir() + return Path.new(require("neogit.lib.git").repo.cwd .. "/.git"):absolute() +end + +function M.setup() + local gitdir = git_dir() + local watcher = M.watch_git_dir(gitdir) + + M.watcher[gitdir] = watcher +end + +-- Adapted from https://github.com/lewis6991/gitsigns.nvim/blob/main/lua/gitsigns/manager.lua#L575 +--- @param gitdir string +--- @return uv_fs_event_t? +function M.watch_git_dir(gitdir) + if not config.values.auto_refresh then + return + end + + if M.watcher[gitdir] then + logger.debug(string.format("[WATCHER] for '%s' already setup! Bailing.", gitdir)) + return + end + + local watch_gitdir_handler_db = util.debounce_trailing(100, function() + status.dispatch_refresh() + end) + + logger.debug("[WATCHER] Watching git dir: " .. gitdir) + + local w = assert(uv.new_fs_event()) + + w:start(gitdir, {}, function(err, filename, events) + if err then + logger.error("[WATCHER] Git dir update error: %s", err) + return + end + + local info = string.format( + "[WATCHER] Git dir update: '%s' %s", + filename, + vim.inspect(events, { indent = "", newline = " " }) + ) + + if filename:match("%.lock$") then + logger.debug("%s (ignoring)", info) + return + end + + logger.debug(info) + + watch_gitdir_handler_db() + end) + + return w +end + +return M From 2ba5e576c7eb23f04de98e3858f00f1c05756d6e Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 18:37:22 +0200 Subject: [PATCH 02/95] Add debounce_trailing to utils --- lua/neogit/lib/util.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lua/neogit/lib/util.lua b/lua/neogit/lib/util.lua index 42a8d418a..15623797e 100644 --- a/lua/neogit/lib/util.lua +++ b/lua/neogit/lib/util.lua @@ -1,4 +1,5 @@ local a = require("plenary.async") +local uv = vim.loop ---@generic T: any ---@generic U: any @@ -249,6 +250,23 @@ local function build_reverse_lookup(tbl) return result end +--- Debounces a function on the trailing edge. +--- +--- @generic F: function +--- @param ms number Timeout in ms +--- @param fn F Function to debounce +--- @return F Debounced function. +local function debounce_trailing(ms, fn) + local timer = assert(uv.new_timer()) + return function(...) + local argv = { ... } + timer:start(ms, 0, function() + timer:stop() + fn(unpack(argv)) + end) + end +end + return { time = time, time_async = time_async, @@ -276,4 +294,5 @@ return { merge = merge, str_min_width = str_min_width, str_clamp = str_clamp, + debounce_trailing = debounce_trailing, } From 9502ef739e996cc9338a829a5a246ba41d73c7f0 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 18:40:39 +0200 Subject: [PATCH 03/95] Fix bug where setup is run twice, and initialize file watcher in setup. Also ensure repo state is loaded. --- lua/neogit.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 7be5f8909..e1292083b 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -6,6 +6,7 @@ local hl = require("neogit.lib.hl") local status = require("neogit.status") local state = require("neogit.lib.state") local input = require("neogit.lib.input") +local watcher = require("neogit.watcher") local logger = require("neogit.logger") local cli = require("neogit.lib.git.cli") @@ -39,6 +40,7 @@ local setup = function(opts) hl.setup() signs.setup() state.setup() + watcher.setup() require("neogit.autocmds").setup() end @@ -47,16 +49,16 @@ end local open = function(opts) opts = opts or {} - if opts.cwd and not opts.no_expand then - opts.cwd = vim.fn.expand(opts.cwd) - end - if not did_setup then notification.create("Neogit has not been setup!", vim.log.levels.ERROR) logger.error("Neogit not setup!") return end + if opts.cwd and not opts.no_expand then + opts.cwd = vim.fn.expand(opts.cwd) + end + if not cli.git_is_repository_sync(opts.cwd) then if input.get_confirmation( @@ -71,6 +73,8 @@ local open = function(opts) end end + require("neogit.lib.git").repo:refresh() + if opts[1] ~= nil then local popup_name = opts[1] local has_pop, popup = pcall(require, "neogit.popups." .. popup_name) From 491cae315cbf7d9b9af867d3712b487ee135bf1b Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 18:41:38 +0200 Subject: [PATCH 04/95] Since the watcher now is only run in an async context, some of the repo update functions now no longer need to be async - in fact, being async breaks things. So, these calls can now just be sync calls. --- lua/neogit/lib/git/log.lua | 4 ++- lua/neogit/lib/git/merge.lua | 34 +++++++------------------- lua/neogit/lib/git/pull.lua | 2 +- lua/neogit/lib/git/rebase.lua | 42 ++++++++++++++------------------ lua/neogit/lib/git/sequencer.lua | 33 ++++++------------------- lua/neogit/lib/git/stash.lua | 2 +- lua/neogit/lib/git/status.lua | 22 +++++------------ 7 files changed, 46 insertions(+), 93 deletions(-) diff --git a/lua/neogit/lib/git/log.lua b/lua/neogit/lib/git/log.lua index b8ae0ae88..2d3f24f36 100644 --- a/lua/neogit/lib/git/log.lua +++ b/lua/neogit/lib/git/log.lua @@ -255,7 +255,9 @@ function M.list(options, show_popup) table.insert(options, "--max-count=256") end - local output = cli.log.format(format).graph.arg_list(options or {}).show_popup(show_popup).call():trim() + local output = + cli.log.format(format).graph.arg_list(options or {}).show_popup(show_popup).call_sync():trim() + return parse_log(output.stdout, graph) end diff --git a/lua/neogit/lib/git/merge.lua b/lua/neogit/lib/git/merge.lua index 0e0c30417..7cd1eb756 100644 --- a/lua/neogit/lib/git/merge.lua +++ b/lua/neogit/lib/git/merge.lua @@ -3,6 +3,7 @@ local client = require("neogit.client") local notif = require("neogit.lib.notification") local cli = require("neogit.lib.git.cli") local branch_lib = require("neogit.lib.git.branch") +local Path = require("plenary.path") local M = {} @@ -31,40 +32,23 @@ function M.abort() return merge_command(cli.merge.abort) end -local uv = require("neogit.lib.uv") function M.update_merge_status(state) - local root = cli.git_root() - if root == "" then + if state.git_root == "" then return end - local merge = { - items = {}, - head = nil, - msg = "", - } + local merge = { items = {}, head = nil, msg = "" } - local mfile = root .. "/.git/MERGE_HEAD" - local _, stat = a.uv.fs_stat(mfile) - - -- Find the merge progress files - if not stat then - return - end - - local err, head = uv.read_file(mfile) - if not head then - logger.error("Failed to read merge head: " .. err) + local merge_head = Path.new(state.git_root .. "/.git/MERGE_HEAD") + if not merge_head:exists() then return end - head = head:match("([^\r\n]+)") - merge.head = head - local _, msg = uv.read_file(root .. "/.git/MERGE_MSG") + merge.head = merge_head:read():match("([^\r\n]+)") - -- we need \r? to support windows - if msg then - merge.msg = msg:match("([^\r\n]+)") + local message = Path.new(state.git_root .. "/.git/MERGE_MSG") + if message:exists() then + merge.msg = message:read():match("([^\r\n]+)") -- we need \r? to support windows end state.merge = merge diff --git a/lua/neogit/lib/git/pull.lua b/lua/neogit/lib/git/pull.lua index 11c287397..f294fb9da 100644 --- a/lua/neogit/lib/git/pull.lua +++ b/lua/neogit/lib/git/pull.lua @@ -14,7 +14,7 @@ local function update_unpulled(state) return end - local result = cli.log.oneline.for_range("..@{upstream}").show_popup(false).call():trim().stdout + local result = cli.log.oneline.for_range("..@{upstream}").show_popup(false).call_sync():trim().stdout state.unpulled.items = util.map(result, function(x) return { name = x } diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index 6c92f4b40..107c08f49 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -5,6 +5,7 @@ local notif = require("neogit.lib.notification") local M = {} local a = require("plenary.async") +local Path = require("plenary.path") local function rebase_command(cmd) local git = require("neogit.lib.git") @@ -48,47 +49,40 @@ function M.skip() return rebase_command(git.cli.rebase.skip) end -local uv = require("neogit.lib.uv") function M.update_rebase_status(state) - local cli = require("neogit.lib.git.cli") - local root = cli.git_root() - if root == "" then + if state.git_root == "" then return end local rebase = { items = {}, head = nil, + current = nil, } - local _, stat = a.uv.fs_stat(root .. "/.git/rebase-merge") - local rebase_file = nil + local rebase_file + local rebase_merge = Path:new(state.git_root .. "/.git/rebase-merge") + local rebase_apply = Path:new(state.git_root .. "/.git/rebase-apply") - -- Find the rebase progress files - if stat then - rebase_file = root .. "/.git/rebase-merge" - else - local _, stat = a.uv.fs_stat(root .. "/.git/rebase-apply") - if stat then - rebase_file = root .. "/.git/rebase-apply" - end + if rebase_merge:exists() then + rebase_file = rebase_merge + elseif rebase_apply:exists() then + rebase_file = rebase_apply end if rebase_file then - local err, head = uv.read_file(rebase_file .. "/head-name") - if not head then - logger.error("Failed to read rebase-merge head: " .. err) + local head = rebase_file:joinpath("/head-name") + if not head:exists() then + logger.error("Failed to read rebase-merge head") return end - head = head:match("refs/heads/([^\r\n]+)") - rebase.head = head - local _, todos = uv.read_file(rebase_file .. "/git-rebase-todo") - local _, done = uv.read_file(rebase_file .. "/done") + rebase.head = head:read():match("refs/heads/([^\r\n]+)") + local todo = rebase_file:joinpath("/git-rebase-todo") + local done = rebase_file:joinpath("/done") local current = 0 - -- we need \r? to support windows - for line in (done or ""):gmatch("[^\r\n]+") do + for line in done:iter() do if not line:match("^#") then current = current + 1 table.insert(rebase.items, { name = line, done = true }) @@ -103,7 +97,7 @@ function M.update_rebase_status(state) cur.stopped = true end - for line in (todos or ""):gmatch("[^\r\n]+") do + for line in todo:iter() do if not line:match("^#") then table.insert(rebase.items, { name = line }) end diff --git a/lua/neogit/lib/git/sequencer.lua b/lua/neogit/lib/git/sequencer.lua index fba22a1ea..a594ad76e 100644 --- a/lua/neogit/lib/git/sequencer.lua +++ b/lua/neogit/lib/git/sequencer.lua @@ -1,9 +1,8 @@ local M = {} local a = require("plenary.async") -local cli = require("neogit.lib.git.cli") local logger = require("neogit.logger") -local uv = require("neogit.lib.uv") +local Path = require("plenary.path") -- .git/sequencer/todo does not exist when there is only one commit left. -- @@ -25,38 +24,22 @@ function M.pick_or_revert_in_progress() end function M.update_sequencer_status(state) - local root = cli.git_root() - if root == "" then - return - end - local sequencer = { items = {}, head = nil } - local _, stat_revert_head = a.uv.fs_stat(root .. "/.git/REVERT_HEAD") - local _, stat_cherry_pick_head = a.uv.fs_stat(root .. "/.git/CHERRY_PICK_HEAD") + local revert_head = Path.new(state.git_root .. "/.git/REVERT_HEAD") + local cherry_head = Path.new(state.git_root .. "/.git/CHERRY_PICK_HEAD") - if stat_cherry_pick_head then + if cherry_head:exists() then sequencer.head = "CHERRY_PICK_HEAD" sequencer.cherry_pick = true - elseif stat_revert_head then + elseif revert_head:exists() then sequencer.head = "REVERT_HEAD" sequencer.revert = true end - local todo_file = root .. "/.git/sequencer/todo" - local _, stat_todo = a.uv.fs_stat(todo_file) - - if stat_todo then - local err, todo = uv.read_file(todo_file) - if not todo then - logger.error("[sequencer] Failed to read .git/sequencer/todo: " .. err) - return - end - - local _, todos = uv.read_file(todo_file) - - -- we need \r? to support windows - for line in (todos or ""):gmatch("[^\r\n]+") do + local todo = Path.new(state.git_root .. "/.git/sequencer/todo") + if todo:exists() then + for line in todo:iter() do if not line:match("^#") then table.insert(sequencer.items, { name = line }) end diff --git a/lua/neogit/lib/git/stash.lua b/lua/neogit/lib/git/stash.lua index fa078884f..f1c1d520a 100644 --- a/lua/neogit/lib/git/stash.lua +++ b/lua/neogit/lib/git/stash.lua @@ -105,7 +105,7 @@ function M.drop(stash) end function M.list() - return cli.stash.args("list").call():trim().stdout + return cli.stash.args("list").call_sync():trim().stdout end function M.register(meta) diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index a3e80a597..5a1799273 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -26,7 +26,7 @@ local function update_status(state) -- cwd may change after the status is refreshed and used, especially if using -- rooter plugins with lsp integration local cwd = vim.fn.getcwd() - local result = git.cli.status.porcelain(2).branch.call():trim() + local result = git.cli.status.porcelain(2).branch.call_sync():trim() local untracked_files, unstaged_files, staged_files = {}, {}, {} local old_files_hash = { @@ -119,26 +119,16 @@ local function update_status(state) end local function update_branch_information(state) - local tasks = {} - if state.head.oid ~= "(initial)" then - table.insert(tasks, function() - local result = git.cli.log.max_count(1).pretty("%B").call():trim() - state.head.commit_message = result.stdout[1] - end) + local result = git.cli.log.max_count(1).pretty("%B").call_sync():trim() + state.head.commit_message = result.stdout[1] if state.upstream.ref then - table.insert(tasks, function() - local result = - git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call():trim() - state.upstream.commit_message = result.stdout[1] - end) + local result = + git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call_sync():trim() + state.upstream.commit_message = result.stdout[1] end end - - if #tasks > 0 then - a.util.join(tasks) - end end local status = { From f4c641a6b44583d27c4288715c513193f6117985 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 18:42:49 +0200 Subject: [PATCH 05/95] Because the repo is now refreshed in async context, just refresh the entire thing every time. --- lua/neogit/lib/git/repository.lua | 96 ++++--------------------------- 1 file changed, 12 insertions(+), 84 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 3e280cd7c..8e14b95a0 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -6,6 +6,7 @@ local function empty_state() ---The cwd when this was updated. ---Used to generate absolute paths cwd = ".", + git_root = nil, head = { branch = nil, commit_message = "", @@ -67,95 +68,22 @@ function M.reset(self) self.state = empty_state() end -function M.refresh(self, lib) - local refreshes = {} +function M.refresh(self) + logger.debug("[REPO]: Refreshing START") - if lib and type(lib) == "table" then - if lib.status then - self.lib.update_status(self.state) - a.util.scheduler() - end - - if lib.branch_information then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing branch information") - self.lib.update_branch_information(self.state) - end) - end - - if lib.rebase then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing rebase information") - self.lib.update_rebase_status(self.state) - end) - end - - if lib.cherry_pick then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing cherry-pick information") - self.lib.update_cherry_pick_status(self.state) - end) - end - - if lib.merge then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing merge information") - self.lib.update_merge_status(self.state) - end) - end - - if lib.stashes then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing stash") - self.lib.update_stashes(self.state) - end) - end - - if lib.unpulled then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing unpulled commits") - self.lib.update_unpulled(self.state) - end) - end - - if lib.unmerged then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing unpushed commits") - self.lib.update_unmerged(self.state) - end) - end - - if lib.recent then - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing recent commits") - self.lib.update_recent(self.state) - end) - end - - if lib.diffs then - local filter = (type(lib) == "table" and type(lib.diffs) == "table") and lib.diffs or nil + self.state.git_root = require("neogit.lib.git.cli").git_root() + if self.state.git_root == "" then + logger.debug("[REPO]: Refreshing ABORTED") + return + end - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing diffs") - self.lib.update_diffs(self.state, filter) - end) - end - else - logger.debug("[REPO]: Refreshing ALL") - self.lib.update_status(self.state) - a.util.scheduler() + self.lib.update_status(self.state) - for name, fn in pairs(self.lib) do - table.insert(refreshes, function() - logger.debug("[REPO]: Refreshing " .. name) - fn(self.state) - end) - end + for name, fn in pairs(self.lib) do + logger.debug(string.format("[REPO]: Refreshing %s", name)) + fn(self.state) end - logger.debug(string.format("[REPO]: Running %d refresh(es)", #refreshes)) - a.util.join(refreshes) - a.util.scheduler() logger.debug("[REPO]: Refreshes completed") end From c72bdf77b1985199014b87e1cd7d17c8fd37038a Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 18:51:56 +0200 Subject: [PATCH 06/95] Remove autocmd - this is a poorer implementation of the filewatcher --- lua/neogit.lua | 2 -- lua/neogit/autocmds.lua | 35 ----------------------------------- 2 files changed, 37 deletions(-) delete mode 100644 lua/neogit/autocmds.lua diff --git a/lua/neogit.lua b/lua/neogit.lua index e1292083b..027144e00 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -41,8 +41,6 @@ local setup = function(opts) signs.setup() state.setup() watcher.setup() - - require("neogit.autocmds").setup() end ---@param opts OpenOpts diff --git a/lua/neogit/autocmds.lua b/lua/neogit/autocmds.lua deleted file mode 100644 index 522489987..000000000 --- a/lua/neogit/autocmds.lua +++ /dev/null @@ -1,35 +0,0 @@ -local M = {} - -local api = vim.api -local group = api.nvim_create_augroup("Neogit", { clear = true }) - -local a = require("plenary.async") -local status = require("neogit.status") -local fs = require("neogit.lib.fs") - -function M.setup() - api.nvim_create_autocmd({ "BufWritePost", "ShellCmdPost", "VimResume" }, { - callback = function(o) - -- Skip update if the buffer is not open - if not status.status_buffer then - return - end - - -- Do not trigger on neogit buffers such as commit - if api.nvim_buf_get_option(o.buf, "filetype"):find("Neogit") then - return - end - - a.run(function() - local path = fs.relpath_from_repository(o.file) - if not path then - return - end - status.refresh({ status = true, diffs = { "*:" .. path } }, string.format("%s:%s", o.event, o.file)) - end, function() end) - end, - group = group, - }) -end - -return M From fc594aa025ee25060c5cdcb7f11076b8c4cc5e5a Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 23:35:06 +0200 Subject: [PATCH 07/95] remove scheduler from all actions - remove manual status updates --- lua/neogit/integrations/diffview.lua | 1 - lua/neogit/lib/git/init.lua | 3 +-- lua/neogit/lib/git/rebase.lua | 3 --- lua/neogit/popups/branch/actions.lua | 12 +----------- lua/neogit/popups/fetch/actions.lua | 5 ----- lua/neogit/popups/pull/actions.lua | 4 ---- lua/neogit/popups/push/actions.lua | 4 ---- lua/neogit/popups/rebase/actions.lua | 19 ------------------- lua/neogit/popups/remote/actions.lua | 8 -------- lua/neogit/popups/reset/actions.lua | 5 ----- lua/neogit/popups/revert/actions.lua | 2 -- lua/neogit/popups/stash/actions.lua | 10 ---------- 12 files changed, 2 insertions(+), 74 deletions(-) diff --git a/lua/neogit/integrations/diffview.lua b/lua/neogit/integrations/diffview.lua index 34636d7ee..c86b1c72c 100644 --- a/lua/neogit/integrations/diffview.lua +++ b/lua/neogit/integrations/diffview.lua @@ -132,7 +132,6 @@ local function get_local_diff_view(selected_file_name) } view:on_files_staged(a.void(function(_) - status.refresh({ status = true, diffs = true }, "on_files_staged") view:update_files() end)) diff --git a/lua/neogit/lib/git/init.lua b/lua/neogit/lib/git/init.lua index 30f657f60..10cd1e059 100644 --- a/lua/neogit/lib/git/init.lua +++ b/lua/neogit/lib/git/init.lua @@ -49,8 +49,7 @@ M.init_repo = function() vim.cmd(string.format("cd %s", directory)) M.create(directory) - - status.refresh(true, "InitRepo") + status.refresh() end return M diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index 107c08f49..fdd3b6d40 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -23,9 +23,6 @@ function M.rebase_interactive(commit, args) else notif.create("Rebased successfully", vim.log.levels.INFO) end - a.util.scheduler() - local status = require("neogit.status") - status.refresh(true, "rebase_interactive") end function M.rebase_onto(branch, args) diff --git a/lua/neogit/popups/branch/actions.lua b/lua/neogit/popups/branch/actions.lua index fe2a1c1c6..1d6bacc17 100644 --- a/lua/neogit/popups/branch/actions.lua +++ b/lua/neogit/popups/branch/actions.lua @@ -1,6 +1,5 @@ local M = {} -local status = require("neogit.status") local git = require("neogit.lib.git") local input = require("neogit.lib.input") local util = require("neogit.lib.util") @@ -28,8 +27,7 @@ M.checkout_branch_revision = operation("checkout_branch_revision", function(popu return end - git.cli.checkout.branch(selected_branch).arg_list(popup:get_arguments()).call_sync():trim() - status.refresh(true, "checkout_branch") + git.cli.checkout.branch(selected_branch).arg_list(popup:get_arguments()).call_sync() end) M.checkout_local_branch = operation("checkout_local_branch", function(popup) @@ -55,8 +53,6 @@ M.checkout_local_branch = operation("checkout_local_branch", function(popup) elseif target then git.cli.checkout.branch(target).arg_list(popup:get_arguments()).call_sync() end - - status.refresh(true, "branch_checkout") end) M.checkout_create_branch = operation("checkout_create_branch", function() @@ -78,12 +74,10 @@ M.checkout_create_branch = operation("checkout_create_branch", function() end git.cli.checkout.new_branch_with_start_point(name, base_branch).call_sync():trim() - status.refresh(true, "branch_create") end) M.create_branch = operation("create_branch", function() git.branch.create() - status.refresh(true, "create_branch") end) M.configure_branch = operation("configure_branch", function() @@ -114,7 +108,6 @@ M.rename_branch = operation("rename_branch", function() new_name, _ = new_name:gsub("%s", "-") git.cli.branch.move.args(selected_branch, new_name).call_sync():trim() - status.refresh(true, "rename_branch") end) M.reset_branch = operation("reset_branch", function() @@ -145,7 +138,6 @@ M.reset_branch = operation("reset_branch", function() git.cli["update-ref"].message(string.format("reset: moving to %s", to)).args(from, to).call_sync() notif.create(string.format("Reset '%s'", git.repo.head.branch), vim.log.levels.INFO) - status.refresh(true, "reset_branch") end) M.delete_branch = operation("delete_branch", function() @@ -185,8 +177,6 @@ M.delete_branch = operation("delete_branch", function() notif.create(string.format("Deleted branch '%s'", branch_name), vim.log.levels.INFO) end end - - status.refresh(true, "delete_branch") end) return M diff --git a/lua/neogit/popups/fetch/actions.lua b/lua/neogit/popups/fetch/actions.lua index 3c56ba848..6c01725b7 100644 --- a/lua/neogit/popups/fetch/actions.lua +++ b/lua/neogit/popups/fetch/actions.lua @@ -1,10 +1,8 @@ local M = {} -local a = require("plenary.async") local git = require("neogit.lib.git") local logger = require("neogit.logger") local notif = require("neogit.lib.notification") -local status = require("neogit.status") local util = require("neogit.lib.util") local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") @@ -14,10 +12,8 @@ local function fetch_from(name, remote, branch, args) local res = git.fetch.fetch_interactive(remote, branch, args) if res and res.code == 0 then - a.util.scheduler() notif.create("Fetched from " .. name) logger.debug("Fetched from " .. name) - status.refresh(true, "fetch_from") vim.api.nvim_exec_autocmds("User", { pattern = "NeogitFetchComplete", modeline = false }) else logger.error("Failed to fetch from " .. name) @@ -106,7 +102,6 @@ end function M.fetch_submodules(_) notif.create("Fetching submodules") git.cli.fetch.recurse_submodules().verbose().jobs(4).call() - status.refresh(true, "fetch_submodules") end function M.set_variables() diff --git a/lua/neogit/popups/pull/actions.lua b/lua/neogit/popups/pull/actions.lua index 3ea8296ff..c4b8d8e99 100644 --- a/lua/neogit/popups/pull/actions.lua +++ b/lua/neogit/popups/pull/actions.lua @@ -1,8 +1,6 @@ -local a = require("plenary.async") local git = require("neogit.lib.git") local logger = require("neogit.logger") local notif = require("neogit.lib.notification") -local status = require("neogit.status") local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") @@ -23,10 +21,8 @@ local function pull_from(args, remote, branch, opts) local res = git.pull.pull_interactive(remote, branch, args) if res and res.code == 0 then - a.util.scheduler() notif.create("Pulled from " .. name) logger.debug("Pulled from " .. name) - status.refresh(true, "pull_from") vim.api.nvim_exec_autocmds("User", { pattern = "NeogitPullComplete", modeline = false }) else logger.error("Failed to pull from " .. name) diff --git a/lua/neogit/popups/push/actions.lua b/lua/neogit/popups/push/actions.lua index e3f0b7535..f90ed78ee 100644 --- a/lua/neogit/popups/push/actions.lua +++ b/lua/neogit/popups/push/actions.lua @@ -1,8 +1,6 @@ -local a = require("plenary.async") local git = require("neogit.lib.git") local logger = require("neogit.logger") local notif = require("neogit.lib.notification") -local status = require("neogit.status") local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") @@ -23,10 +21,8 @@ local function push_to(args, remote, branch, opts) local res = git.push.push_interactive(remote, branch, args) if res and res.code == 0 then - a.util.scheduler() logger.error("Pushed to " .. name) notif.create("Pushed to " .. name) - status.refresh(true, "push_to") vim.api.nvim_exec_autocmds("User", { pattern = "NeogitPushComplete", modeline = false }) else logger.error("Failed to push to " .. name) diff --git a/lua/neogit/popups/rebase/actions.lua b/lua/neogit/popups/rebase/actions.lua index 150f4866a..132969543 100644 --- a/lua/neogit/popups/rebase/actions.lua +++ b/lua/neogit/popups/rebase/actions.lua @@ -1,7 +1,5 @@ -local a = require("plenary.async") local git = require("neogit.lib.git") local input = require("neogit.lib.input") -local status = require("neogit.status") local CommitSelectViewBuffer = require("neogit.buffers.commit_select_view") local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") @@ -15,8 +13,6 @@ end function M.onto_base(popup) git.rebase.rebase_onto(M.base_branch(), popup:get_arguments()) - a.util.scheduler() - status.refresh(true, "rebase_master") end function M.onto_pushRemote(popup) @@ -30,9 +26,6 @@ function M.onto_pushRemote(popup) string.format("refs/remotes/%s/%s", pushRemote, git.branch.current()), popup:get_arguments() ) - - a.util.scheduler() - status.refresh(true, "rebase_pushremote") end end @@ -50,16 +43,12 @@ function M.onto_upstream(popup) end git.rebase.rebase_onto(upstream, popup:get_arguments()) - a.util.scheduler() - status.refresh(true, "rebase_upstream") end function M.onto_elsewhere(popup) local target = FuzzyFinderBuffer.new(git.branch.get_all_branches()):open_async() if target then git.rebase.rebase_onto(target, popup:get_arguments()) - a.util.scheduler() - status.refresh(true, "rebase_elsewhere") end end @@ -67,29 +56,21 @@ function M.interactively(popup) local commit = popup.state.env.commit[1] or CommitSelectViewBuffer.new(git.log.list()):open_async() if commit then git.rebase.rebase_interactive(commit, popup:get_arguments()) - a.util.scheduler() - status.refresh(true, "rebase_interactive") end end function M.continue() git.rebase.continue() - a.util.scheduler() - status.refresh(true, "rebase_continue") end function M.skip() git.rebase.skip() - a.util.scheduler() - status.refresh(true, "rebase_skip") end -- TODO: Extract to rebase lib? function M.abort() if input.get_confirmation("Abort rebase?", { values = { "&Yes", "&No" }, default = 2 }) then git.cli.rebase.abort.call_sync():trim() - a.util.scheduler() - status.refresh(true, "rebase_abort") end end diff --git a/lua/neogit/popups/remote/actions.lua b/lua/neogit/popups/remote/actions.lua index 1fc088616..1490f9fcf 100644 --- a/lua/neogit/popups/remote/actions.lua +++ b/lua/neogit/popups/remote/actions.lua @@ -1,10 +1,8 @@ local M = {} -local a = require("plenary.async") local git = require("neogit.lib.git") local input = require("neogit.lib.input") local notification = require("neogit.lib.notification") -local status = require("neogit.status") local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") local RemoteConfigPopup = require("neogit.popups.remote_config") @@ -52,9 +50,7 @@ function M.rename(_) end git.remote.rename(selected_remote, new_name) - a.util.scheduler() notification.create("Renamed remote " .. selected_remote .. " to " .. new_name) - status.refresh(true, "rename_remote") end function M.remove(_) @@ -64,9 +60,7 @@ function M.remove(_) end git.remote.remove(selected_remote) - a.util.scheduler() notification.create("Removed remote " .. selected_remote) - status.refresh(true, "remove_remote") end function M.configure(_) @@ -86,8 +80,6 @@ function M.prune_branches(_) notification.create("Pruning remote " .. selected_remote) git.remote.prune(selected_remote) - a.util.scheduler() - status.refresh(true, "prune_remote") end -- https://github.com/magit/magit/blob/main/lisp/magit-remote.el#L159 diff --git a/lua/neogit/popups/reset/actions.lua b/lua/neogit/popups/reset/actions.lua index 1b42986ac..126e64f60 100644 --- a/lua/neogit/popups/reset/actions.lua +++ b/lua/neogit/popups/reset/actions.lua @@ -1,6 +1,5 @@ local a = require("plenary.async") local git = require("neogit.lib.git") -local status = require("neogit.status") local util = require("neogit.lib.util") local CommitSelectViewBuffer = require("neogit.buffers.commit_select_view") @@ -15,8 +14,6 @@ local function reset(type) end git.reset[type](commit) - a.util.scheduler() - status.refresh(true, "reset_" .. type) end function M.mixed() @@ -63,8 +60,6 @@ function M.a_file() end git.reset.file(commit, files) - a.util.scheduler() - status.refresh(true, "reset_file") end return M diff --git a/lua/neogit/popups/revert/actions.lua b/lua/neogit/popups/revert/actions.lua index 227dccd5a..7d318953e 100644 --- a/lua/neogit/popups/revert/actions.lua +++ b/lua/neogit/popups/revert/actions.lua @@ -26,8 +26,6 @@ function M.commits(popup) end git.revert.commits(commits, popup:get_arguments()) - a.util.scheduler() - require("neogit.status").refresh(true, "revert_commits") end return M diff --git a/lua/neogit/popups/stash/actions.lua b/lua/neogit/popups/stash/actions.lua index c6f1d3d2d..28d8ecdf5 100644 --- a/lua/neogit/popups/stash/actions.lua +++ b/lua/neogit/popups/stash/actions.lua @@ -1,5 +1,3 @@ -local a = require("plenary.async") -local status = require("neogit.status") local git = require("neogit.lib.git") local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") @@ -8,14 +6,10 @@ local M = {} function M.both(popup) git.stash.stash_all(popup:get_arguments()) - a.util.scheduler() - status.refresh(true, "stash_both") end function M.index(popup) git.stash.stash_index(popup:get_arguments()) - a.util.scheduler() - status.refresh(true, "stash_index") end function M.push(popup) @@ -25,8 +19,6 @@ function M.push(popup) end git.stash.push(popup:get_arguments(), files) - a.util.scheduler() - status.refresh(true, "stash_push") end local function use(action, stash) @@ -45,8 +37,6 @@ local function use(action, stash) if name then git.stash[action](name) - a.util.scheduler() - status.refresh(true, "stash_" .. action) end end From 31142845ac84b29daabdc3072cd24eca3a98a996 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 25 Jun 2023 23:36:16 +0200 Subject: [PATCH 08/95] fix filepaths --- lua/neogit/lib/git/rebase.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index fdd3b6d40..024752fb3 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -68,7 +68,7 @@ function M.update_rebase_status(state) end if rebase_file then - local head = rebase_file:joinpath("/head-name") + local head = rebase_file:joinpath("head-name") if not head:exists() then logger.error("Failed to read rebase-merge head") return @@ -76,8 +76,8 @@ function M.update_rebase_status(state) rebase.head = head:read():match("refs/heads/([^\r\n]+)") - local todo = rebase_file:joinpath("/git-rebase-todo") - local done = rebase_file:joinpath("/done") + local todo = rebase_file:joinpath("git-rebase-todo") + local done = rebase_file:joinpath("done") local current = 0 for line in done:iter() do if not line:match("^#") then From 5c96a5557d7225c84f9d66c639cfeea6c7c0ee89 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 10:50:13 +0200 Subject: [PATCH 09/95] Mostly working --- lua/neogit/lib/git/repository.lua | 23 +++++- lua/neogit/lib/popup/builder.lua | 12 ++- lua/neogit/status.lua | 128 ++++++++++-------------------- lua/neogit/watcher.lua | 15 ++-- 4 files changed, 83 insertions(+), 95 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 8e14b95a0..38e237f53 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -68,9 +68,24 @@ function M.reset(self) self.state = empty_state() end -function M.refresh(self) +M.dispatch_refresh = a.void(function(...) + a.util.scheduler() + M.refresh(...) +end) + +local refresh_lock = a.control.Semaphore.new(1) + +function M.refresh(self, callback) logger.debug("[REPO]: Refreshing START") + if refresh_lock.permits == 0 then + logger.debug("[REPO]: Refresh lock not available. Aborting refresh.") + return + end + + local permit = refresh_lock:acquire() + logger.debug("[REPO]: Acquired refresh lock") + self.state.git_root = require("neogit.lib.git.cli").git_root() if self.state.git_root == "" then logger.debug("[REPO]: Refreshing ABORTED") @@ -85,6 +100,12 @@ function M.refresh(self) end logger.debug("[REPO]: Refreshes completed") + permit:forget() + logger.info("[REPO]: Refresh lock is now free") + + if callback then + callback() + end end if not M.initialized then diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index de77997b4..05b85ec78 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -256,10 +256,20 @@ end ---@return self function M:action(key, description, callback) if not self.state.keys[key] then + local action + + if callback then + action = a.void(function(...) + callback(...) + + git.repo:dispatch_refresh(require("neogit.status").dispatch_refresh) + end) + end + table.insert(self.state.actions[#self.state.actions], { key = key, description = description, - callback = callback and a.void(callback) or nil, + callback = action, }) self.state.keys[key] = true diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 7181c3154..509b37951 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -84,7 +84,7 @@ local function get_section_item_for_line(linenr) end ---@return Section|nil, StatusItem|nil -local function get_current_section_item() +function M.get_current_section_item() return get_section_item_for_line(vim.fn.line(".")) end @@ -385,37 +385,13 @@ local function refresh_status_buffer() vim.cmd("redraw") end -local refresh_lock = a.control.Semaphore.new(1) -local lock_holder = nil - -local function refresh(which, reason) +function M.refresh() logger.info("[STATUS BUFFER]: Starting refresh") - if refresh_lock.permits == 0 then - logger.debug( - string.format( - "[STATUS BUFFER]: Refresh lock not available. Aborting refresh. Lock held by: %q", - lock_holder - ) - ) - --- Undo the deadlock fix - --- This is because refresh wont properly wait but return immediately if - --- refresh is already in progress. This breaks as waiting for refresh does - --- not mean that a status buffer is drawn and ready - a.util.scheduler() - -- refresh_status() - -- return - end - - local permit = refresh_lock:acquire() - lock_holder = reason or "unknown" - logger.debug("[STATUS BUFFER]: Acquired refresh lock: " .. lock_holder) - a.util.scheduler() local s, f, h = save_cursor_location() - if cli.git_root() ~= "" then - git.repo:refresh(which) + if git.repo.git_root ~= "" then refresh_status_buffer() vim.api.nvim_exec_autocmds("User", { pattern = "NeogitStatusRefreshed", modeline = false }) end @@ -426,17 +402,13 @@ local function refresh(which, reason) end logger.info("[STATUS BUFFER]: Finished refresh") - - lock_holder = nil - permit:forget() - logger.info("[STATUS BUFFER]: Refresh lock is now free") end -local dispatch_refresh = a.void(function(v, reason) - refresh(v, reason) +M.dispatch_refresh = a.void(function() + M.refresh() end) -local refresh_manually = a.void(function(fname) +M.refresh_manually = a.void(function(fname) if not fname or fname == "" then return end @@ -445,23 +417,21 @@ local refresh_manually = a.void(function(fname) if not path then return end - refresh({ status = true, diffs = { "*:" .. path } }, "manually") + + M.refresh() end) --- Compatibility endpoint to refresh data from an autocommand. -- `fname` should be `` in this case. This function will take care of -- resolving the file name to the path relative to the repository root and -- refresh that file's cache data. -local function refresh_viml_compat(fname) +function M.refresh_viml_compat(fname) logger.info("[STATUS BUFFER]: refresh_viml_compat") - if not config.values.auto_refresh then - return - end if #vim.fs.find(".git/", { upward = true }) == 0 then -- not a git repository return end - refresh_manually(fname) + M.refresh_manually(fname) end local function current_line_is_hunk() @@ -495,8 +465,8 @@ local function get_current_hunk_of_item(item) return get_hunk_of_item_for_line(item, vim.fn.line(".")) end -local function toggle() - local section, item = get_current_section_item() +function M.toggle() + local section, item = M.get_current_section_item() if section == nil then return end @@ -517,18 +487,15 @@ local function toggle() refresh_status_buffer() end -local reset = function() +M.reset = function() git.repo:reset() M.locations = {} - if not config.values.auto_refresh then - return - end - refresh(true, "reset") + M.refresh(true, "reset") end -local dispatch_reset = a.void(reset) +M.dispatch_reset = a.void(M.reset) -local function close(skip_close) +function M.close(skip_close) if not skip_close then M.status_buffer:close() end @@ -540,7 +507,7 @@ local function close(skip_close) end end -local function generate_patch_from_selection(item, hunk, from, to, reverse) +function M.generate_patch_from_selection(item, hunk, from, to, reverse) reverse = reverse or false from = from or 1 to = to or hunk.diff_to - hunk.diff_from @@ -679,7 +646,7 @@ local stage_selection = function() else local section, item, hunk, from, to = get_selection() if section and from then - local patch = generate_patch_from_selection(item, hunk, from, to) + local patch = M.generate_patch_from_selection(item, hunk, from, to) cli.apply.cached.with_patch(patch).call() end end @@ -693,7 +660,7 @@ local unstage_selection = function() else local section, item, hunk, from, to = get_selection() if section and from then - local patch = generate_patch_from_selection(item, hunk, from, to, true) + local patch = M.generate_patch_from_selection(item, hunk, from, to, true) cli.apply.reverse.cached.with_patch(patch).call() end end @@ -701,7 +668,7 @@ end local stage = function() M.current_operation = "stage" - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() local mode = vim.api.nvim_get_mode() if @@ -727,13 +694,13 @@ local stage = function() end add.call() end - refresh(true, "stage") + M.refresh(true, "stage") M.current_operation = nil return else if on_hunk and section.name ~= "untracked" then local hunk = get_current_hunk_of_item(item) - local patch = generate_patch_from_selection(item, hunk) + local patch = M.generate_patch_from_selection(item, hunk) cli.apply.cached.with_patch(patch).call() else git.status.stage(item.name) @@ -742,12 +709,11 @@ local stage = function() end assert(item, "Stage item is nil") - refresh({ status = true, diffs = { "*:" .. item.name } }, "stage_finish") M.current_operation = nil end local unstage = function() - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() local mode = vim.api.nvim_get_mode() if section == nil or section.name ~= "staged" or (mode.mode == "V" and item == nil) then @@ -760,7 +726,7 @@ local unstage = function() else if item == nil then git.status.unstage_all() - refresh(true, "unstage") + M.refresh() M.current_operation = nil return else @@ -768,7 +734,7 @@ local unstage = function() if on_hunk then local hunk = get_current_hunk_of_item(item) - local patch = generate_patch_from_selection(item, hunk, nil, nil, true) + local patch = M.generate_patch_from_selection(item, hunk, nil, nil, true) cli.apply.reverse.cached.with_patch(patch).call() else git.status.unstage(item.name) @@ -777,7 +743,7 @@ local unstage = function() end assert(item, "Unstage item is nil") - refresh({ status = true, diffs = { "*:" .. item.name } }, "unstage_finish") + M.refresh() M.current_operation = nil end @@ -821,7 +787,7 @@ end ---Discards selected lines local function discard_selection(section, item, hunk, from, to) logger.debug("Discarding selection hunk:" .. vim.inspect(hunk)) - local patch = generate_patch_from_selection(item, hunk, from, to, true) + local patch = M.generate_patch_from_selection(item, hunk, from, to, true) logger.debug("Patch:" .. vim.inspect(patch)) if section.name == "staged" then @@ -849,7 +815,7 @@ local function discard_hunk(section, item, lines, hunk) end local discard = function() - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() if section == nil or item == nil then return end @@ -888,7 +854,7 @@ local discard = function() discard_selected_files({ item }, section.name) end - refresh(true, "discard") + M.refresh() M.current_operation = nil a.util.scheduler() @@ -907,7 +873,7 @@ local set_folds = function(to) end end) end) - refresh(true, "set_folds") + M.refresh() end local function cherry_pick() @@ -939,21 +905,18 @@ local cmd_func_map = function() ["Depth4"] = a.void(function() set_folds { false, false, false } end), - ["Toggle"] = toggle, + ["Toggle"] = M.toggle, ["Discard"] = { "nv", a.void(discard), true }, ["Stage"] = { "nv", a.void(stage), true }, ["StageUnstaged"] = a.void(function() git.status.stage_modified() - refresh({ status = true, diffs = true }, "StageUnstaged") end), ["StageAll"] = a.void(function() git.status.stage_all() - refresh { status = true, diffs = true } end), ["Unstage"] = { "nv", a.void(unstage), true }, ["UnstageStaged"] = a.void(function() git.status.unstage_all() - refresh({ status = true, diffs = true }, "UnstageStaged") end), ["CommandHistory"] = function() GitCommandHistory:new():show() @@ -963,25 +926,25 @@ local cmd_func_map = function() process.show_console() end, ["TabOpen"] = function() - local _, item = get_current_section_item() + local _, item = M.get_current_section_item() if item then vim.cmd("tabedit " .. item.name) end end, ["VSplitOpen"] = function() - local _, item = get_current_section_item() + local _, item = M.get_current_section_item() if item then vim.cmd("vsplit " .. item.name) end end, ["SplitOpen"] = function() - local _, item = get_current_section_item() + local _, item = M.get_current_section_item() if item then vim.cmd("split " .. item.name) end end, ["GoToPreviousHunkHeader"] = function() - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() if not section then return end @@ -1012,7 +975,7 @@ local cmd_func_map = function() end end, ["GoToNextHunkHeader"] = function() - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() if not section then return end @@ -1040,7 +1003,7 @@ local cmd_func_map = function() ["GoToFile"] = a.void(function() -- local repo_root = cli.git_root() a.util.scheduler() - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() if item and section then if section.name == "unstaged" or section.name == "staged" or section.name == "untracked" then @@ -1086,7 +1049,7 @@ local cmd_func_map = function() end end), ["RefreshBuffer"] = function() - dispatch_refresh(true) + M.dispatch_refresh() end, ["HelpPopup"] = function() local line = M.status_buffer:get_current_line() @@ -1105,7 +1068,7 @@ local cmd_func_map = function() return end local dv = require("neogit.integrations.diffview") - local section, item = get_current_section_item() + local section, item = M.get_current_section_item() if section and item then dv.open(section.name, item.name) @@ -1265,22 +1228,11 @@ function M.create(kind, cwd) set_decoration_provider(buffer) logger.debug("[STATUS BUFFER]: Dispatching initial render") - refresh(true, "Buffer.create") + M.refresh() end, } end -M.toggle = toggle -M.generate_patch_from_selection = generate_patch_from_selection -M.reset = reset -M.dispatch_reset = dispatch_reset -M.refresh = refresh -M.dispatch_refresh = dispatch_refresh -M.refresh_viml_compat = refresh_viml_compat -M.refresh_manually = refresh_manually -M.get_current_section_item = get_current_section_item -M.close = close - function M.enable() M.disabled = false end diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 8010d4a70..b0305db3d 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -6,7 +6,10 @@ local config = require("neogit.config") local logger = require("neogit.logger") local util = require("neogit.lib.util") local status = require("neogit.status") +local git = require("neogit.lib.git") + local Path = require("plenary.path") +local a = require("plenary.async") M.watcher = {} @@ -34,9 +37,12 @@ function M.watch_git_dir(gitdir) return end - local watch_gitdir_handler_db = util.debounce_trailing(100, function() - status.dispatch_refresh() - end) + local watch_gitdir_handler_db = util.debounce_trailing( + 100, + a.void(function() + git.repo:dispatch_refresh(status.dispatch_refresh) + end) + ) logger.debug("[WATCHER] Watching git dir: " .. gitdir) @@ -55,12 +61,11 @@ function M.watch_git_dir(gitdir) ) if filename:match("%.lock$") then - logger.debug("%s (ignoring)", info) + logger.debug(string.format("%s (ignoring)", info)) return end logger.debug(info) - watch_gitdir_handler_db() end) From 547fae53ba1b70535d175b0f41e3b4e4b8ac0239 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 10:50:21 +0200 Subject: [PATCH 10/95] Add debug command to print repo state --- plugin/neogit.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin/neogit.lua b/plugin/neogit.lua index 4a09965c1..00707c470 100644 --- a/plugin/neogit.lua +++ b/plugin/neogit.lua @@ -11,3 +11,10 @@ end, { return neogit.complete(arglead) end, }) + +api.nvim_create_user_command("NeogitRepoState", function() + vim.print(require("neogit.lib.git").repo.state) +end, { + nargs = "*", + desc = "Open Neogit Repo State", +}) From 05f907f58d7f3c6b42bc75b943af88f66ebbdacc Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 10:50:42 +0200 Subject: [PATCH 11/95] Cleanup --- lua/neogit/popups/cherry_pick/actions.lua | 14 -------------- lua/neogit/popups/commit/actions.lua | 4 ---- lua/neogit/popups/help/actions.lua | 8 +------- lua/neogit/popups/merge/actions.lua | 6 ------ lua/neogit/status.lua | 4 ++-- 5 files changed, 3 insertions(+), 33 deletions(-) diff --git a/lua/neogit/popups/cherry_pick/actions.lua b/lua/neogit/popups/cherry_pick/actions.lua index bb0c235e9..04089ebe1 100644 --- a/lua/neogit/popups/cherry_pick/actions.lua +++ b/lua/neogit/popups/cherry_pick/actions.lua @@ -23,11 +23,7 @@ function M.pick(popup) return end - a.util.scheduler() git.cherry_pick.pick(commits, popup:get_arguments()) - - a.util.scheduler() - require("neogit.status").refresh(true, "cherry_pick_pick") end function M.apply(popup) @@ -36,29 +32,19 @@ function M.apply(popup) return end - a.util.scheduler() git.cherry_pick.apply(commits, popup:get_arguments()) - - a.util.scheduler() - require("neogit.status").refresh(true, "cherry_pick_apply") end function M.continue() git.cherry_pick.continue() - a.util.scheduler() - require("neogit.status").refresh(true, "cherry_pick_continue") end function M.skip() git.cherry_pick.skip() - a.util.scheduler() - require("neogit.status").refresh(true, "cherry_pick_skip") end function M.abort() git.cherry_pick.abort() - a.util.scheduler() - require("neogit.status").refresh(true, "cherry_pick_abort") end return M diff --git a/lua/neogit/popups/commit/actions.lua b/lua/neogit/popups/commit/actions.lua index 25070d757..d59375e4d 100644 --- a/lua/neogit/popups/commit/actions.lua +++ b/lua/neogit/popups/commit/actions.lua @@ -24,10 +24,6 @@ local function do_commit(popup, cmd) notif.create("Successfully committed!") vim.api.nvim_exec_autocmds("User", { pattern = "NeogitCommitComplete", modeline = false }) end - - a.util.scheduler() - - require("neogit.status").refresh(true, "do_commit") end local function commit_special(popup, method) diff --git a/lua/neogit/popups/help/actions.lua b/lua/neogit/popups/help/actions.lua index 9188a4874..a888e81d5 100644 --- a/lua/neogit/popups/help/actions.lua +++ b/lua/neogit/popups/help/actions.lua @@ -68,13 +68,7 @@ end M.essential = function() return present { - { - "RefreshBuffer", - "Refresh", - function() - require("neogit.status").refresh(true, "user_refresh") - end, - }, + { "RefreshBuffer", "Refresh", require("neogit.status").refresh }, { "GoToFile", "Go to file", NONE }, { "Toggle", "Toggle", NONE }, } diff --git a/lua/neogit/popups/merge/actions.lua b/lua/neogit/popups/merge/actions.lua index 8d2eebeb9..f30c6e0ae 100644 --- a/lua/neogit/popups/merge/actions.lua +++ b/lua/neogit/popups/merge/actions.lua @@ -13,8 +13,6 @@ end function M.commit() git.merge.continue() - a.util.scheduler() - require("neogit.status").refresh(true, "merge_continue") end function M.abort() @@ -23,8 +21,6 @@ function M.abort() end git.merge.abort() - a.util.scheduler() - require("neogit.status").refresh(true, "merge_abort") end function M.merge(popup) @@ -34,8 +30,6 @@ function M.merge(popup) end git.merge.merge(branch, popup:get_arguments()) - a.util.scheduler() - require("neogit.status").refresh(true, "merge") end return M diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 509b37951..dfc30379c 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -490,7 +490,7 @@ end M.reset = function() git.repo:reset() M.locations = {} - M.refresh(true, "reset") + M.refresh() end M.dispatch_reset = a.void(M.reset) @@ -694,7 +694,7 @@ local stage = function() end add.call() end - M.refresh(true, "stage") + M.refresh() M.current_operation = nil return else From 6a802b427d1d32f0b2abaa92f435725d779d82e7 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 10:52:41 +0200 Subject: [PATCH 12/95] Ensure state is always updated, even if we bail early --- lua/neogit/lib/git/merge.lua | 8 +++----- lua/neogit/lib/git/rebase.lua | 33 +++++++++++++++----------------- lua/neogit/lib/git/sequencer.lua | 16 ++++++---------- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/lua/neogit/lib/git/merge.lua b/lua/neogit/lib/git/merge.lua index 7cd1eb756..2a2261c48 100644 --- a/lua/neogit/lib/git/merge.lua +++ b/lua/neogit/lib/git/merge.lua @@ -37,21 +37,19 @@ function M.update_merge_status(state) return end - local merge = { items = {}, head = nil, msg = "" } + state.merge = { head = nil, msg = "" } local merge_head = Path.new(state.git_root .. "/.git/MERGE_HEAD") if not merge_head:exists() then return end - merge.head = merge_head:read():match("([^\r\n]+)") + state.merge.head = merge_head:read():match("([^\r\n]+)") local message = Path.new(state.git_root .. "/.git/MERGE_MSG") if message:exists() then - merge.msg = message:read():match("([^\r\n]+)") -- we need \r? to support windows + state.merge.msg = message:read():match("([^\r\n]+)") -- we need \r? to support windows end - - state.merge = merge end M.register = function(meta) diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index 024752fb3..c9e3f2f34 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -51,11 +51,7 @@ function M.update_rebase_status(state) return end - local rebase = { - items = {}, - head = nil, - current = nil, - } + state.rebase = { items = {}, head = nil, current = nil } local rebase_file local rebase_merge = Path:new(state.git_root .. "/.git/rebase-merge") @@ -74,34 +70,35 @@ function M.update_rebase_status(state) return end - rebase.head = head:read():match("refs/heads/([^\r\n]+)") + state.rebase.head = head:read():match("refs/heads/([^\r\n]+)") local todo = rebase_file:joinpath("git-rebase-todo") local done = rebase_file:joinpath("done") local current = 0 - for line in done:iter() do - if not line:match("^#") then - current = current + 1 - table.insert(rebase.items, { name = line, done = true }) + + if done:exists() then + for line in done:iter() do + if not line:match("^#") then + current = current + 1 + table.insert(state.rebase.items, { name = line, done = true }) + end end end - rebase.current = current - - local cur = rebase.items[#rebase.items] + local cur = state.rebase.items[#state.rebase.items] if cur then cur.done = false cur.stopped = true end - for line in todo:iter() do - if not line:match("^#") then - table.insert(rebase.items, { name = line }) + if todo:exists() then + for line in todo:iter() do + if not line:match("^#") then + table.insert(state.rebase.items, { name = line }) + end end end end - - state.rebase = rebase end M.register = function(meta) diff --git a/lua/neogit/lib/git/sequencer.lua b/lua/neogit/lib/git/sequencer.lua index a594ad76e..b5d3aa108 100644 --- a/lua/neogit/lib/git/sequencer.lua +++ b/lua/neogit/lib/git/sequencer.lua @@ -1,7 +1,5 @@ local M = {} -local a = require("plenary.async") -local logger = require("neogit.logger") local Path = require("plenary.path") -- .git/sequencer/todo does not exist when there is only one commit left. @@ -24,29 +22,27 @@ function M.pick_or_revert_in_progress() end function M.update_sequencer_status(state) - local sequencer = { items = {}, head = nil } + state.sequencer = { items = {}, head = nil } local revert_head = Path.new(state.git_root .. "/.git/REVERT_HEAD") local cherry_head = Path.new(state.git_root .. "/.git/CHERRY_PICK_HEAD") if cherry_head:exists() then - sequencer.head = "CHERRY_PICK_HEAD" - sequencer.cherry_pick = true + state.sequencer.head = "CHERRY_PICK_HEAD" + state.sequencer.cherry_pick = true elseif revert_head:exists() then - sequencer.head = "REVERT_HEAD" - sequencer.revert = true + state.sequencer.head = "REVERT_HEAD" + state.sequencer.revert = true end local todo = Path.new(state.git_root .. "/.git/sequencer/todo") if todo:exists() then for line in todo:iter() do if not line:match("^#") then - table.insert(sequencer.items, { name = line }) + table.insert(state.sequencer.items, { name = line }) end end end - - state.sequencer = sequencer end M.register = function(meta) From f4c23f350db630c86b70d9b22f2f5ba62a7934dc Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 12:08:22 +0200 Subject: [PATCH 13/95] bugfix: get envs properly --- lua/neogit/client.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/lua/neogit/client.lua b/lua/neogit/client.lua index 8c2e1cf82..085273f05 100644 --- a/lua/neogit/client.lua +++ b/lua/neogit/client.lua @@ -103,9 +103,6 @@ function M.wrap(cmd, opts) else notif.create(opts.msg.fail) end - - a.util.scheduler() - require("neogit.status").refresh(true, opts.refresh) end return M From 7a869d2805b20f695b4182a9c16fb2160b030d8e Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 12:08:37 +0200 Subject: [PATCH 14/95] notes --- lua/neogit/lib/git/merge.lua | 1 + lua/neogit/lib/git/pull.lua | 1 + lua/neogit/lib/git/rebase.lua | 1 + 3 files changed, 3 insertions(+) diff --git a/lua/neogit/lib/git/merge.lua b/lua/neogit/lib/git/merge.lua index 2a2261c48..90ae8fced 100644 --- a/lua/neogit/lib/git/merge.lua +++ b/lua/neogit/lib/git/merge.lua @@ -9,6 +9,7 @@ local M = {} local a = require("plenary.async") +-- TODO: client.wrap() local function merge_command(cmd) local envs = client.get_envs_git_editor() return cmd.env(envs).show_popup(true):in_pty(true).call(true) diff --git a/lua/neogit/lib/git/pull.lua b/lua/neogit/lib/git/pull.lua index f294fb9da..32c72a6b5 100644 --- a/lua/neogit/lib/git/pull.lua +++ b/lua/neogit/lib/git/pull.lua @@ -3,6 +3,7 @@ local util = require("neogit.lib.util") local M = {} +-- TODO: client.wrap() function M.pull_interactive(remote, branch, args) local client = require("neogit.client") local envs = client.get_envs_git_editor() diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index c9e3f2f34..b5e3012ec 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -7,6 +7,7 @@ local M = {} local a = require("plenary.async") local Path = require("plenary.path") +-- TODO: client.wrap() local function rebase_command(cmd) local git = require("neogit.lib.git") cmd = cmd or git.cli.rebase From 68e1ad8580592251c1537f3ddbbd5a85ea755193 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 12:12:55 +0200 Subject: [PATCH 15/95] Use client.wrap for commit --- lua/neogit/lib/git/revert.lua | 1 - lua/neogit/popups/commit/actions.lua | 36 +++++++++++----------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/lua/neogit/lib/git/revert.lua b/lua/neogit/lib/git/revert.lua index 14007952e..37e49f2cf 100644 --- a/lua/neogit/lib/git/revert.lua +++ b/lua/neogit/lib/git/revert.lua @@ -7,7 +7,6 @@ local M = {} function M.commits(commits, args) client.wrap(cli.revert.args(table.concat(commits, " ")).arg_list(args), { autocmd = "NeogitRevertComplete", - refresh = "do_revert", msg = { setup = "Reverting...", success = "Reverted!", diff --git a/lua/neogit/popups/commit/actions.lua b/lua/neogit/popups/commit/actions.lua index d59375e4d..02b163387 100644 --- a/lua/neogit/popups/commit/actions.lua +++ b/lua/neogit/popups/commit/actions.lua @@ -1,29 +1,21 @@ local M = {} -local notif = require("neogit.lib.notification") local CommitSelectViewBuffer = require("neogit.buffers.commit_select_view") local git = require("neogit.lib.git") -local a = require("plenary.async") +-- local a = require("plenary.async") +local client = require("neogit.client") local function do_commit(popup, cmd) - a.util.scheduler() - - local notification = notif.create("Committing...", vim.log.levels.INFO, 9999) - - local client = require("neogit.client") - local envs = client.get_envs_git_editor() - - local result = cmd.env(envs).args(unpack(popup:get_arguments())):in_pty(true).call(true):trim() - - a.util.scheduler() - if notification then - notification:delete() - end - - if result.code == 0 then - notif.create("Successfully committed!") - vim.api.nvim_exec_autocmds("User", { pattern = "NeogitCommitComplete", modeline = false }) - end + client.wrap(cmd.arg_list(popup:get_arguments()), + { + autocmd = "NeogitCommitComplete", + msg = { + setup = "Committing...", + success = "Committed!", + fail = "Couldn't commit", + }, + } + ) end local function commit_special(popup, method) @@ -32,9 +24,9 @@ local function commit_special(popup, method) return end - a.util.scheduler() + -- a.util.scheduler() do_commit(popup, git.cli.commit.args(method, commit)) - a.util.scheduler() + -- a.util.scheduler() return commit end From 154cb237622597b40e55953c68039d4c890bf40d Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:34:10 +0200 Subject: [PATCH 16/95] Add source to repo refresh --- lua/neogit.lua | 9 +++++---- lua/neogit/lib/popup/builder.lua | 2 +- lua/neogit/watcher.lua | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 027144e00..2bd64abb3 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -47,15 +47,16 @@ end local open = function(opts) opts = opts or {} + if opts.cwd and not opts.no_expand then + opts.cwd = vim.fn.expand(opts.cwd) + end + if not did_setup then notification.create("Neogit has not been setup!", vim.log.levels.ERROR) logger.error("Neogit not setup!") return end - if opts.cwd and not opts.no_expand then - opts.cwd = vim.fn.expand(opts.cwd) - end if not cli.git_is_repository_sync(opts.cwd) then if @@ -71,7 +72,7 @@ local open = function(opts) end end - require("neogit.lib.git").repo:refresh() + require("neogit.lib.git").repo:dispatch_refresh({ source = "open" }) if opts[1] ~= nil then local popup_name = opts[1] diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index 05b85ec78..ab72f2e40 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -262,7 +262,7 @@ function M:action(key, description, callback) action = a.void(function(...) callback(...) - git.repo:dispatch_refresh(require("neogit.status").dispatch_refresh) + git.repo:dispatch_refresh { callback = require("neogit.status").dispatch_refresh, source = "action" } end) end diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index b0305db3d..7ecc4e63c 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -40,7 +40,7 @@ function M.watch_git_dir(gitdir) local watch_gitdir_handler_db = util.debounce_trailing( 100, a.void(function() - git.repo:dispatch_refresh(status.dispatch_refresh) + git.repo:dispatch_refresh { callback = status.dispatch_refresh, source = "watcher" } end) ) From 638bc35b17719e8c75bf5f6e582b534618c670a1 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:35:12 +0200 Subject: [PATCH 17/95] Make watcher fn async --- lua/neogit/watcher.lua | 44 +++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 7ecc4e63c..ee4b23b3c 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -48,26 +48,30 @@ function M.watch_git_dir(gitdir) local w = assert(uv.new_fs_event()) - w:start(gitdir, {}, function(err, filename, events) - if err then - logger.error("[WATCHER] Git dir update error: %s", err) - return - end - - local info = string.format( - "[WATCHER] Git dir update: '%s' %s", - filename, - vim.inspect(events, { indent = "", newline = " " }) - ) - - if filename:match("%.lock$") then - logger.debug(string.format("%s (ignoring)", info)) - return - end - - logger.debug(info) - watch_gitdir_handler_db() - end) + w:start( + gitdir, + {}, + a.void(function(err, filename, events) + if err then + logger.error("[WATCHER] Git dir update error: %s", err) + return + end + + local info = string.format( + "[WATCHER] Git dir update: '%s' %s", + filename, + vim.inspect(events, { indent = "", newline = " " }) + ) + + if filename:match("%.lock$") then + logger.debug(string.format("%s (ignoring)", info)) + return + end + + logger.debug(info) + watch_gitdir_handler_db() + end) + ) return w end From 5b1c6c5b156e52fe72de3f4777fddf0362b9874a Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:35:45 +0200 Subject: [PATCH 18/95] Add cwd/git_root to the initializer in repo --- lua/neogit/lib/git/repository.lua | 12 ++++++++---- lua/neogit/lib/git/status.lua | 7 ------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 38e237f53..16424f4fc 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -1,12 +1,16 @@ local a = require("plenary.async") local logger = require("neogit.logger") +-- git-status outputs files relative to the cwd. +-- +-- Save the working directory to allow resolution to absolute paths since the +-- cwd may change after the status is refreshed and used, especially if using +-- rooter plugins with lsp integration local function empty_state() return { - ---The cwd when this was updated. - ---Used to generate absolute paths - cwd = ".", - git_root = nil, + cwd = vim.fn.getcwd(), + git_root = require("neogit.lib.git.cli").git_root(), + rev_toplevel = nil, head = { branch = nil, commit_message = "", diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index 5a1799273..ac4aea161 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -20,12 +20,6 @@ local function update_file(file, mode, name) end local function update_status(state) - -- git-status outputs files relative to the cwd. - -- - -- Save the working directory to allow resolution to absolute paths since the - -- cwd may change after the status is refreshed and used, especially if using - -- rooter plugins with lsp integration - local cwd = vim.fn.getcwd() local result = git.cli.status.porcelain(2).branch.call_sync():trim() local untracked_files, unstaged_files, staged_files = {}, {}, {} @@ -110,7 +104,6 @@ local function update_status(state) upstream.commit_message = state.upstream.commit_message end - state.cwd = cwd state.head = head state.upstream = upstream state.untracked.items = untracked_files From 886b07785271a9c3bfcce59495b607793e5f8a8e Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:36:13 +0200 Subject: [PATCH 19/95] Rewrite repo refresh to use semaphore to prevent multiple refreshes being queued. --- lua/neogit/lib/git/repository.lua | 44 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 16424f4fc..572191edc 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -1,5 +1,6 @@ local a = require("plenary.async") local logger = require("neogit.logger") +local util = require("neogit.lib.util") -- git-status outputs files relative to the cwd. -- @@ -72,48 +73,53 @@ function M.reset(self) self.state = empty_state() end -M.dispatch_refresh = a.void(function(...) - a.util.scheduler() - M.refresh(...) -end) - local refresh_lock = a.control.Semaphore.new(1) +local lock_holder -function M.refresh(self, callback) - logger.debug("[REPO]: Refreshing START") +M.dispatch_refresh = a.void(function(self, opts) + opts = opts or {} if refresh_lock.permits == 0 then - logger.debug("[REPO]: Refresh lock not available. Aborting refresh.") + logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", lock_holder)) return end + lock_holder = opts.source or "UNKNOWN" + logger.info(string.format("[REPO]: Acquiring refresh lock (source: %s)", lock_holder)) local permit = refresh_lock:acquire() - logger.debug("[REPO]: Acquired refresh lock") - self.state.git_root = require("neogit.lib.git.cli").git_root() + a.util.scheduler() + M._refresh(self, opts) + + logger.info("[REPO]: freeing refresh lock") + lock_holder = nil + permit:forget() +end) + +function M._refresh(self, opts) + logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) + if self.state.git_root == "" then - logger.debug("[REPO]: Refreshing ABORTED") + logger.info("[REPO]: Refreshing ABORTED - No git_root") return end self.lib.update_status(self.state) for name, fn in pairs(self.lib) do - logger.debug(string.format("[REPO]: Refreshing %s", name)) + logger.info(string.format("[REPO]: Refreshing %s", name)) fn(self.state) end + logger.info("[REPO]: Refreshes completed") - logger.debug("[REPO]: Refreshes completed") - permit:forget() - logger.info("[REPO]: Refresh lock is now free") - - if callback then - callback() + if opts.callback then + logger.info("[REPO]: Running Callback") + opts.callback() end end if not M.initialized then - logger.debug("[REPO]: Initializing Repository") + logger.info("[REPO]: Initializing Repository") M.initialized = true setmetatable(M, meta) From e80ba09e3147f9a3a1a287841568506d4260aa39 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:37:21 +0200 Subject: [PATCH 20/95] Allow console highlights --- lua/neogit/logger.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/logger.lua b/lua/neogit/logger.lua index 71d285407..ee7e28d50 100644 --- a/lua/neogit/logger.lua +++ b/lua/neogit/logger.lua @@ -2,7 +2,7 @@ local log = require("plenary.log") return log.new { plugin = "neogit", - highlights = false, + highlights = vim.env.NEOGIT_LOG_HIGHLIGHTS or false, use_console = vim.env.NEOGIT_LOG_CONSOLE or false, use_file = vim.env.NEOGIT_LOG_FILE or false, level = vim.env.NEOGIT_LOG_LEVEL or "info", From fbbba456abfdedc1ad6038c5456d59c9104d580d Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:37:37 +0200 Subject: [PATCH 21/95] Use internal state and improve logging --- lua/neogit/status.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index dfc30379c..17b612dad 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -373,7 +373,7 @@ local function refresh_status_buffer() M.status_buffer:unlock() - logger.debug("[STATUS BUFFER]: Redrawing") + logger.debug("[STATUS BUFFER]: Starting Redrawing") draw_buffer() draw_signs() @@ -774,7 +774,7 @@ local function discard_selected_files(files, section) if section == "untracked" then a.util.scheduler() for _, file in ipairs(filenames) do - vim.fn.delete(cli.git_root() .. "/" .. file) + vim.fn.delete(string.format("%s/%s", git.repo.git_root, file)) end elseif section == "unstaged" then cli.checkout.files(unpack(filenames)).call() From 9f8faa5bee84caacc50fe514d288a65db85c3cee Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 16:57:15 +0200 Subject: [PATCH 22/95] Add NeogitMessages user command --- lua/neogit/lib/notification.lua | 10 ++++++---- plugin/neogit.lua | 9 +++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lua/neogit/lib/notification.lua b/lua/neogit/lib/notification.lua index ada4bf091..e45c78bed 100644 --- a/lua/neogit/lib/notification.lua +++ b/lua/neogit/lib/notification.lua @@ -3,6 +3,11 @@ local message_history = {} local notifications = {} local notification_count = 0 +local levels = {} +for k, v in pairs(vim.log.levels) do + table.insert(levels, v, k) +end + ---@param message string local function create(message, level, delay) if not level then @@ -119,10 +124,7 @@ local function create(message, level, delay) end end - table.insert(message_history, { - content = message, - level = level, - }) + table.insert(message_history, { content = message, level = level, kind = levels[level] }) if vim.fn.winbufnr(window) ~= -1 then vim.api.nvim_win_close(window, false) diff --git a/plugin/neogit.lua b/plugin/neogit.lua index 00707c470..d85df47d1 100644 --- a/plugin/neogit.lua +++ b/plugin/neogit.lua @@ -18,3 +18,12 @@ end, { nargs = "*", desc = "Open Neogit Repo State", }) + +api.nvim_create_user_command("NeogitMessages", function() + for _, message in ipairs(require("neogit.lib.notification").get_history()) do + print(string.format("[%s]: %s", message.kind, table.concat(message.content, " - "))) + end +end, { + nargs = "*", + desc = "Prints neogit message history", +}) From 45f08f132ba3b72f93a8a247b8c893fc1890e5da Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 17:03:50 +0200 Subject: [PATCH 23/95] Don't expose _refresh publicly --- lua/neogit/lib/git/repository.lua | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 572191edc..45171bfc9 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -73,6 +73,28 @@ function M.reset(self) self.state = empty_state() end +local function _refresh(self, opts) + logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) + + if self.state.git_root == "" then + logger.info("[REPO]: Refreshing ABORTED - No git_root") + return + end + + self.lib.update_status(self.state) + + for name, fn in pairs(self.lib) do + logger.info(string.format("[REPO]: Refreshing %s", name)) + fn(self.state) + end + logger.info("[REPO]: Refreshes completed") + + if opts.callback then + logger.info("[REPO]: Running Callback") + opts.callback() + end +end + local refresh_lock = a.control.Semaphore.new(1) local lock_holder @@ -89,35 +111,13 @@ M.dispatch_refresh = a.void(function(self, opts) local permit = refresh_lock:acquire() a.util.scheduler() - M._refresh(self, opts) + _refresh(self, opts) logger.info("[REPO]: freeing refresh lock") lock_holder = nil permit:forget() end) -function M._refresh(self, opts) - logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) - - if self.state.git_root == "" then - logger.info("[REPO]: Refreshing ABORTED - No git_root") - return - end - - self.lib.update_status(self.state) - - for name, fn in pairs(self.lib) do - logger.info(string.format("[REPO]: Refreshing %s", name)) - fn(self.state) - end - logger.info("[REPO]: Refreshes completed") - - if opts.callback then - logger.info("[REPO]: Running Callback") - opts.callback() - end -end - if not M.initialized then logger.info("[REPO]: Initializing Repository") M.initialized = true From c8d665a1dadc6c25c7b6b2770a4c95544860e0e7 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 26 Jun 2023 23:45:48 +0200 Subject: [PATCH 24/95] hide git-path details --- lua/neogit/lib/git/merge.lua | 6 +-- lua/neogit/lib/git/rebase.lua | 5 +-- lua/neogit/lib/git/repository.lua | 68 ++++++++++--------------------- lua/neogit/lib/git/sequencer.lua | 8 ++-- 4 files changed, 29 insertions(+), 58 deletions(-) diff --git a/lua/neogit/lib/git/merge.lua b/lua/neogit/lib/git/merge.lua index 90ae8fced..0c969b6b9 100644 --- a/lua/neogit/lib/git/merge.lua +++ b/lua/neogit/lib/git/merge.lua @@ -1,9 +1,7 @@ -local logger = require("neogit.logger") local client = require("neogit.client") local notif = require("neogit.lib.notification") local cli = require("neogit.lib.git.cli") local branch_lib = require("neogit.lib.git.branch") -local Path = require("plenary.path") local M = {} @@ -40,14 +38,14 @@ function M.update_merge_status(state) state.merge = { head = nil, msg = "" } - local merge_head = Path.new(state.git_root .. "/.git/MERGE_HEAD") + local merge_head = state.git_path("MERGE_HEAD") if not merge_head:exists() then return end state.merge.head = merge_head:read():match("([^\r\n]+)") - local message = Path.new(state.git_root .. "/.git/MERGE_MSG") + local message = state.git_path("MERGE_MSG") if message:exists() then state.merge.msg = message:read():match("([^\r\n]+)") -- we need \r? to support windows end diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index b5e3012ec..f3b4ed514 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -5,7 +5,6 @@ local notif = require("neogit.lib.notification") local M = {} local a = require("plenary.async") -local Path = require("plenary.path") -- TODO: client.wrap() local function rebase_command(cmd) @@ -55,8 +54,8 @@ function M.update_rebase_status(state) state.rebase = { items = {}, head = nil, current = nil } local rebase_file - local rebase_merge = Path:new(state.git_root .. "/.git/rebase-merge") - local rebase_apply = Path:new(state.git_root .. "/.git/rebase-apply") + local rebase_merge = state.git_path("rebase-merge") + local rebase_apply = state.git_path("rebase-apply") if rebase_merge:exists() then rebase_file = rebase_merge diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 45171bfc9..64760c6cf 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -1,62 +1,38 @@ local a = require("plenary.async") local logger = require("neogit.logger") -local util = require("neogit.lib.util") -- git-status outputs files relative to the cwd. -- -- Save the working directory to allow resolution to absolute paths since the -- cwd may change after the status is refreshed and used, especially if using -- rooter plugins with lsp integration +-- stylua: ignore start local function empty_state() + local root = require("neogit.lib.git.cli").git_root() + local Path = require("plenary.path") + return { - cwd = vim.fn.getcwd(), - git_root = require("neogit.lib.git.cli").git_root(), + git_path = function(path) + return Path.new(root):joinpath(".git", path) + end, + cwd = vim.fn.getcwd(), + git_root = root, rev_toplevel = nil, - head = { - branch = nil, - commit_message = "", - }, - upstream = { - remote = nil, - branch = nil, - commit_message = "", - }, - untracked = { - items = {}, - }, - unstaged = { - items = {}, - }, - staged = { - items = {}, - }, - stashes = { - items = {}, - }, - unpulled = { - items = {}, - }, - unmerged = { - items = {}, - }, - recent = { - items = {}, - }, - rebase = { - items = {}, - head = nil, - }, - sequencer = { - items = {}, - head = nil, - }, - merge = { - items = {}, - head = nil, - msg = nil, - }, + head = { branch = nil, commit_message = "" }, + upstream = { remote = nil, branch = nil, commit_message = "" }, + untracked = { items = {} }, + unstaged = { items = {} }, + staged = { items = {} }, + stashes = { items = {} }, + unpulled = { items = {} }, + unmerged = { items = {} }, + recent = { items = {} }, + rebase = { items = {}, head = nil }, + sequencer = { items = {}, head = nil }, + merge = { items = {}, head = nil, msg= nil }, } end +-- stylua: ignore end local meta = { __index = function(self, method) diff --git a/lua/neogit/lib/git/sequencer.lua b/lua/neogit/lib/git/sequencer.lua index b5d3aa108..c8885b970 100644 --- a/lua/neogit/lib/git/sequencer.lua +++ b/lua/neogit/lib/git/sequencer.lua @@ -1,7 +1,5 @@ local M = {} -local Path = require("plenary.path") - -- .git/sequencer/todo does not exist when there is only one commit left. -- -- And CHERRY_PICK_HEAD does not exist when a conflict happens while picking a series of commits with --no-commit. @@ -24,8 +22,8 @@ end function M.update_sequencer_status(state) state.sequencer = { items = {}, head = nil } - local revert_head = Path.new(state.git_root .. "/.git/REVERT_HEAD") - local cherry_head = Path.new(state.git_root .. "/.git/CHERRY_PICK_HEAD") + local revert_head = state.git_path("REVERT_HEAD") + local cherry_head = state.git_path("CHERRY_PICK_HEAD") if cherry_head:exists() then state.sequencer.head = "CHERRY_PICK_HEAD" @@ -35,7 +33,7 @@ function M.update_sequencer_status(state) state.sequencer.revert = true end - local todo = Path.new(state.git_root .. "/.git/sequencer/todo") + local todo = state.git_path("sequencer/todo") if todo:exists() then for line in todo:iter() do if not line:match("^#") then From 8cab98c9e29f4924c11c96f92f3b40b727c5e897 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 27 Jun 2023 16:31:34 +0200 Subject: [PATCH 25/95] This needs items for rendering --- lua/neogit/lib/git/merge.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/lib/git/merge.lua b/lua/neogit/lib/git/merge.lua index 0c969b6b9..603c0ac37 100644 --- a/lua/neogit/lib/git/merge.lua +++ b/lua/neogit/lib/git/merge.lua @@ -36,7 +36,7 @@ function M.update_merge_status(state) return end - state.merge = { head = nil, msg = "" } + state.merge = { head = nil, msg = "", items = {} } local merge_head = state.git_path("MERGE_HEAD") if not merge_head:exists() then From 58f00418e13b8aa37bd9c3bbac1097826ad824ff Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 27 Jun 2023 16:31:53 +0200 Subject: [PATCH 26/95] Bugfix: Include count (current) and discard blank lines --- lua/neogit/lib/git/rebase.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index f3b4ed514..2390cffc2 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -72,14 +72,10 @@ function M.update_rebase_status(state) state.rebase.head = head:read():match("refs/heads/([^\r\n]+)") - local todo = rebase_file:joinpath("git-rebase-todo") local done = rebase_file:joinpath("done") - local current = 0 - if done:exists() then for line in done:iter() do - if not line:match("^#") then - current = current + 1 + if line:match("^[^#]") and line ~= "" then table.insert(state.rebase.items, { name = line, done = true }) end end @@ -89,11 +85,13 @@ function M.update_rebase_status(state) if cur then cur.done = false cur.stopped = true + state.rebase.current = #state.rebase.items end + local todo = rebase_file:joinpath("git-rebase-todo") if todo:exists() then for line in todo:iter() do - if not line:match("^#") then + if line:match("^[^#]") and line ~= "" then table.insert(state.rebase.items, { name = line }) end end From 7a6101587ae7c3fe6e9a94e9e496c8670fed7c98 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 27 Jun 2023 20:14:26 +0200 Subject: [PATCH 27/95] Add logging to builder --- lua/neogit/lib/popup/builder.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index ab72f2e40..0e739ac27 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -295,6 +295,7 @@ function M:build() error("A popup needs to have a name!") end + logger.debug(string.format("[BUILDER] Building %s Popup", self.state.name)) return self.builder_fn(self.state) end From f6ba27fbb7559e683b6830d030c25dd33c4c79d0 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 27 Jun 2023 20:14:41 +0200 Subject: [PATCH 28/95] Use git root for watcher path --- lua/neogit/watcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index ee4b23b3c..27038719a 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -14,7 +14,7 @@ local a = require("plenary.async") M.watcher = {} local function git_dir() - return Path.new(require("neogit.lib.git").repo.cwd .. "/.git"):absolute() + return Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute() end function M.setup() From afb98b709015d712d41a23dc50336643b28fc114 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 27 Jun 2023 23:24:06 +0200 Subject: [PATCH 29/95] Organize --- lua/neogit/lib/git/repository.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 64760c6cf..9061cebb7 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -19,17 +19,17 @@ local function empty_state() git_root = root, rev_toplevel = nil, head = { branch = nil, commit_message = "" }, - upstream = { remote = nil, branch = nil, commit_message = "" }, - untracked = { items = {} }, - unstaged = { items = {} }, - staged = { items = {} }, - stashes = { items = {} }, - unpulled = { items = {} }, - unmerged = { items = {} }, - recent = { items = {} }, - rebase = { items = {}, head = nil }, - sequencer = { items = {}, head = nil }, - merge = { items = {}, head = nil, msg= nil }, + upstream = { branch = nil, commit_message = "", remote = nil }, + untracked = { items = {} }, + unstaged = { items = {} }, + staged = { items = {} }, + stashes = { items = {} }, + unpulled = { items = {} }, + unmerged = { items = {} }, + recent = { items = {} }, + rebase = { items = {}, head = nil }, + sequencer = { items = {}, head = nil }, + merge = { items = {}, head = nil, msg = nil }, } end -- stylua: ignore end From fc2f6c6564d81de4c233841b2d265dcf3b0c06ab Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 28 Jun 2023 11:11:42 +0200 Subject: [PATCH 30/95] Formatting --- lua/neogit/lib/popup/builder.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index 0e739ac27..1aeda133c 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -262,7 +262,10 @@ function M:action(key, description, callback) action = a.void(function(...) callback(...) - git.repo:dispatch_refresh { callback = require("neogit.status").dispatch_refresh, source = "action" } + git.repo:dispatch_refresh { + callback = require("neogit.status").dispatch_refresh, + source = "action", + } end) end From 91967efe2d5fca557ebbe22f13ef0806032fe659 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 15:06:18 +0200 Subject: [PATCH 31/95] Reformat to be more friendly to lsp's goto definition --- lua/neogit/lib/git/status.lua | 43 +++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index ac4aea161..cd7d98ad4 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -2,7 +2,6 @@ local git = { cli = require("neogit.lib.git.cli"), stash = require("neogit.lib.git.stash"), } -local a = require("plenary.async") local Collection = require("neogit.lib.collection") local function update_file(file, mode, name) @@ -124,27 +123,31 @@ local function update_branch_information(state) end end -local status = { - stage = function(...) - git.cli.add.files(...).call() - end, - stage_modified = function() - git.cli.add.update.call() - end, - stage_all = function() - git.cli.add.all.call() - end, - unstage = function(...) - git.cli.reset.files(...).call() - end, - unstage_all = function() - git.cli.reset.call() - end, -} +local M = {} + +function M.stage(...) + git.cli.add.files(...).call() +end + +function M.stage_modified() + git.cli.add.update.call() +end + +function M.stage_all() + git.cli.add.all.call() +end + +function M.unstage(...) + git.cli.reset.files(...).call() +end + +function M.unstage_all() + git.cli.reset.call() +end -status.register = function(meta) +function M.register(meta) meta.update_status = update_status meta.update_branch_information = update_branch_information end -return status +return M From 368cc4ed785419309340d0fa2c57a9af79cc1406 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 15:14:46 +0200 Subject: [PATCH 32/95] Add notes --- lua/neogit/lib/git/diff.lua | 8 ++++++++ lua/neogit/popups/log/actions.lua | 1 + 2 files changed, 9 insertions(+) diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index b7d3c73db..a4ad88838 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -215,6 +215,14 @@ local function raw_staged(name) end end +-- TODO: each item should track if it's been modified, and that should be used to invalidate the cached diff. +-- So, something like, +-- diff = { has_diff = true, invalid = true } +-- +-- Would necissitate rebuilding the diff, but +-- diff = { has_diff = true, invalid = false } +-- +-- would not. If `has_diff` is false, it shouldn't be counted either. local function invalidate_diff(filter, section, item) if not filter or filter:accepts(section, item.name) then logger.debug("[DIFF] Invalidating cached diff for: " .. item.name) diff --git a/lua/neogit/popups/log/actions.lua b/lua/neogit/popups/log/actions.lua index 7a5a91e25..4b73a5783 100644 --- a/lua/neogit/popups/log/actions.lua +++ b/lua/neogit/popups/log/actions.lua @@ -19,6 +19,7 @@ function M.log_head(popup) :open() end +-- TODO: Verify if branch is nil or empty with detatched head function M.log_local_branches(popup) LogViewBuffer.new( git.log.list(util.merge(popup:get_arguments(), { From b7d9a735276cc331cd784fa7b8ec18ccb27a2077 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 15:14:54 +0200 Subject: [PATCH 33/95] Make filewatcher slightly less aggressive --- lua/neogit/watcher.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 27038719a..5516d15dc 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -63,7 +63,11 @@ function M.watch_git_dir(gitdir) vim.inspect(events, { indent = "", newline = " " }) ) - if filename:match("%.lock$") then + -- stylua: ignore + if + filename:match("%.lock$") or + filename:match("COMMIT_EDITMSG") + then logger.debug(string.format("%s (ignoring)", info)) return end From ecf55e233c6f7a3f1fa9eefa8215aab21e625d30 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 15:15:32 +0200 Subject: [PATCH 34/95] Add status.update() function that will refresh repo state then redraw status buffer. This is because, with popup actions, we make sure this happens, but because the status buffer doesn't use that mechanism, I was experiencing issues where I would perform an action (stage hunk) and the buffer wouldn't get updated to reflect that. So, ensure it's triggered manually. --- lua/neogit/status.lua | 54 ++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 17b612dad..d4e8030e5 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -694,7 +694,6 @@ local stage = function() end add.call() end - M.refresh() M.current_operation = nil return else @@ -726,7 +725,6 @@ local unstage = function() else if item == nil then git.status.unstage_all() - M.refresh() M.current_operation = nil return else @@ -743,7 +741,6 @@ local unstage = function() end assert(item, "Unstage item is nil") - M.refresh() M.current_operation = nil end @@ -854,7 +851,6 @@ local discard = function() discard_selected_files({ item }, section.name) end - M.refresh() M.current_operation = nil a.util.scheduler() @@ -893,30 +889,46 @@ local cmd_func_map = function() M.status_buffer:close() end, ["InitRepo"] = a.void(git.init.init_repo), - ["Depth1"] = a.void(function() - set_folds { true, true, false } - end), - ["Depth2"] = a.void(function() - set_folds { false, true, false } - end), - ["Depth3"] = a.void(function() - set_folds { false, false, true } - end), - ["Depth4"] = a.void(function() - set_folds { false, false, false } - end), + ["Depth1"] = a.void(function() set_folds { true, true, false } end), + ["Depth2"] = a.void(function() set_folds { false, true, false } end), + ["Depth3"] = a.void(function() set_folds { false, false, true } end), + ["Depth4"] = a.void(function() set_folds { false, false, false } end), ["Toggle"] = M.toggle, - ["Discard"] = { "nv", a.void(discard), true }, - ["Stage"] = { "nv", a.void(stage), true }, + ["Discard"] = { + "nv", + a.void(function() + discard() + M.update() + end), + true + }, + ["Stage"] = { + "nv", + a.void(function() + stage() + M.update() + end), + true + }, ["StageUnstaged"] = a.void(function() git.status.stage_modified() + M.update() end), ["StageAll"] = a.void(function() git.status.stage_all() + M.update() end), - ["Unstage"] = { "nv", a.void(unstage), true }, + ["Unstage"] = { + "nv", + a.void(function() + unstage() + M.update() + end), + true + }, ["UnstageStaged"] = a.void(function() git.status.unstage_all() + M.update() end), ["CommandHistory"] = function() GitCommandHistory:new():show() @@ -1233,6 +1245,10 @@ function M.create(kind, cwd) } end +function M.update() + git.repo:dispatch_refresh({ source = "status", callback = M.dispatch_refresh }) +end + function M.enable() M.disabled = false end From b4c32c8f8ffc9ac5e684c5e13adfbf3aa081c634 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 15:17:14 +0200 Subject: [PATCH 35/95] Alphabetize git barrel imports --- lua/neogit/lib/git.lua | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lua/neogit/lib/git.lua b/lua/neogit/lib/git.lua index fccfe3b7c..7f2210538 100644 --- a/lua/neogit/lib/git.lua +++ b/lua/neogit/lib/git.lua @@ -1,23 +1,24 @@ return { - repo = require("neogit.lib.git.repository"), + branch = require("neogit.lib.git.branch"), + cherry_pick = require("neogit.lib.git.cherry_pick"), cli = require("neogit.lib.git.cli"), - init = require("neogit.lib.git.init"), - status = require("neogit.lib.git.status"), - stash = require("neogit.lib.git.stash"), - files = require("neogit.lib.git.files"), + config = require("neogit.lib.git.config"), + diff = require("neogit.lib.git.diff"), fetch = require("neogit.lib.git.fetch"), + files = require("neogit.lib.git.files"), + index = require("neogit.lib.git.index"), + init = require("neogit.lib.git.init"), log = require("neogit.lib.git.log"), - reflog = require("neogit.lib.git.reflog"), - branch = require("neogit.lib.git.branch"), - diff = require("neogit.lib.git.diff"), - rebase = require("neogit.lib.git.rebase"), merge = require("neogit.lib.git.merge"), - cherry_pick = require("neogit.lib.git.cherry_pick"), + pull = require("neogit.lib.git.pull"), + push = require("neogit.lib.git.push"), + rebase = require("neogit.lib.git.rebase"), + reflog = require("neogit.lib.git.reflog"), + remote = require("neogit.lib.git.remote"), + repo = require("neogit.lib.git.repository"), reset = require("neogit.lib.git.reset"), revert = require("neogit.lib.git.revert"), - remote = require("neogit.lib.git.remote"), - config = require("neogit.lib.git.config"), sequencer = require("neogit.lib.git.sequencer"), - pull = require("neogit.lib.git.pull"), - push = require("neogit.lib.git.push"), + stash = require("neogit.lib.git.stash"), + status = require("neogit.lib.git.status"), } From aa7bbdfe7f1e8493337a43f9e802403eb288312d Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 15:17:31 +0200 Subject: [PATCH 36/95] pull this function into a new git lib, "index" --- lua/neogit/lib/git/index.lua | 11 +++++++++++ lua/neogit/status.lua | 10 +--------- 2 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 lua/neogit/lib/git/index.lua diff --git a/lua/neogit/lib/git/index.lua b/lua/neogit/lib/git/index.lua new file mode 100644 index 000000000..cfaf2f540 --- /dev/null +++ b/lua/neogit/lib/git/index.lua @@ -0,0 +1,11 @@ +local M = {} + +-- Make sure the index is in sync as git-status skips it +-- Do this manually since the `cli` add --no-optional-locks +function M.update() + require("neogit.process") + .new({ cmd = { "git", "update-index", "-q", "--refresh" }, verbose = true }) + :spawn_async() +end + +return M diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index d4e8030e5..594735e25 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -744,12 +744,6 @@ local unstage = function() M.current_operation = nil end -local function update_index() - require("neogit.process") - .new({ cmd = { "git", "update-index", "-q", "--refresh" }, verbose = true }) - :spawn_async() -end - local function discard_message(item, mode) if mode.mode == "V" then return "Discard selection?" @@ -835,9 +829,7 @@ local discard = function() return end - -- Make sure the index is in sync as git-status skips it - -- Do this manually since the `cli` add --no-optional-locks - update_index() + git.index.update() if mode.mode == "V" then if multi_file then From ee740241f97d9477cde55bbb14e82327603d9a53 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 21:28:48 +0200 Subject: [PATCH 37/95] Increase watcher debounce from 100ms to 300ms --- lua/neogit/watcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 5516d15dc..3e4acd8f4 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -38,7 +38,7 @@ function M.watch_git_dir(gitdir) end local watch_gitdir_handler_db = util.debounce_trailing( - 100, + 300, a.void(function() git.repo:dispatch_refresh { callback = status.dispatch_refresh, source = "watcher" } end) From 3d8a32cb14598284d671b959b68c13c186ab4ebe Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 22:40:34 +0200 Subject: [PATCH 38/95] Tune up logging --- lua/neogit/lib/git/diff.lua | 2 +- lua/neogit/lib/git/repository.lua | 22 ++++++++++++++++------ lua/neogit/process.lua | 2 +- lua/neogit/watcher.lua | 1 + 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index a4ad88838..023d91966 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -225,7 +225,7 @@ end -- would not. If `has_diff` is false, it shouldn't be counted either. local function invalidate_diff(filter, section, item) if not filter or filter:accepts(section, item.name) then - logger.debug("[DIFF] Invalidating cached diff for: " .. item.name) + logger.debug(string.format("[DIFF] Invalidating diff for: %s", item.name)) item.diff = nil end end diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 9061cebb7..dd5c78326 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -53,20 +53,30 @@ local function _refresh(self, opts) logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) if self.state.git_root == "" then - logger.info("[REPO]: Refreshing ABORTED - No git_root") + logger.debug("[REPO]: Refreshing ABORTED - No git_root") return end - self.lib.update_status(self.state) + -- stylua: ignore + if + self.state.index.timestamp == self.state.index_stat() and + opts.source == "watcher" + then + logger.debug("[REPO]: Refreshing ABORTED - .git/index hasn't been modified since last refresh") + return + end for name, fn in pairs(self.lib) do - logger.info(string.format("[REPO]: Refreshing %s", name)) + logger.trace(string.format("[REPO]: Refreshing %s", name)) fn(self.state) end + + self.state.invalidate = {} + logger.info("[REPO]: Refreshes completed") if opts.callback then - logger.info("[REPO]: Running Callback") + logger.debug("[REPO]: Running refresh callback") opts.callback() end end @@ -83,13 +93,13 @@ M.dispatch_refresh = a.void(function(self, opts) end lock_holder = opts.source or "UNKNOWN" - logger.info(string.format("[REPO]: Acquiring refresh lock (source: %s)", lock_holder)) + logger.debug(string.format("[REPO]: Acquiring refresh lock (source: %s)", lock_holder)) local permit = refresh_lock:acquire() a.util.scheduler() _refresh(self, opts) - logger.info("[REPO]: freeing refresh lock") + logger.debug("[REPO]: freeing refresh lock") lock_holder = nil permit:forget() end) diff --git a/lua/neogit/process.lua b/lua/neogit/process.lua index c8fcf983c..fb1f880e0 100644 --- a/lua/neogit/process.lua +++ b/lua/neogit/process.lua @@ -386,7 +386,7 @@ function Process:spawn(cb) end end - logger.debug("Spawning: " .. vim.inspect(self.cmd)) + logger.trace("[PROCESS] Spawning: " .. vim.inspect(self.cmd)) local job = vim.fn.jobstart(self.cmd, { cwd = self.cwd, env = self.env, diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 3e4acd8f4..e543562e1 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -40,6 +40,7 @@ function M.watch_git_dir(gitdir) local watch_gitdir_handler_db = util.debounce_trailing( 300, a.void(function() + logger.debug("[WATCHER] Dispatching Refresh") git.repo:dispatch_refresh { callback = status.dispatch_refresh, source = "watcher" } end) ) From b3dcf90f419abdc0628086c2dc85d980e8f0857d Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 22:48:13 +0200 Subject: [PATCH 39/95] Don't print debug info for invalidating diffs that don't exist --- lua/neogit/lib/git/diff.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index 023d91966..43128d102 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -215,15 +215,14 @@ local function raw_staged(name) end end --- TODO: each item should track if it's been modified, and that should be used to invalidate the cached diff. --- So, something like, --- diff = { has_diff = true, invalid = true } --- --- Would necissitate rebuilding the diff, but --- diff = { has_diff = true, invalid = false } --- --- would not. If `has_diff` is false, it shouldn't be counted either. +-- When there is _no_ filter, invalidate all diffs +-- When there _is_ a filter, only invalidate matching items +-- And, of course, don't worry about items that haven't loaded diffs local function invalidate_diff(filter, section, item) + if not rawget(item, "diff") then + return + end + if not filter or filter:accepts(section, item.name) then logger.debug(string.format("[DIFF] Invalidating diff for: %s", item.name)) item.diff = nil From 0f114fab837f416a212876e83d7e2d122b7d9fcf Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 22:49:00 +0200 Subject: [PATCH 40/95] Add back mechanism for invalidating only specific diffs --- lua/neogit/lib/git/diff.lua | 8 ++++---- lua/neogit/lib/git/repository.lua | 9 +++++++++ lua/neogit/lib/git/status.lua | 2 ++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index 43128d102..ec0aef62e 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -232,10 +232,10 @@ end return { parse = parse_diff, register = function(meta) - meta.update_diffs = function(repo, filter) - filter = filter or false - if filter and type(filter) == "table" then - filter = ItemFilter.create(filter) + meta.update_diffs = function(repo) + local filter + if repo.invalidate[1] then + filter = ItemFilter.create(repo.invalidate) end for _, f in ipairs(repo.untracked.items) do diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index dd5c78326..bc7e0a195 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -18,6 +18,7 @@ local function empty_state() cwd = vim.fn.getcwd(), git_root = root, rev_toplevel = nil, + invalidate = {}, head = { branch = nil, commit_message = "" }, upstream = { branch = nil, commit_message = "", remote = nil }, untracked = { items = {} }, @@ -49,6 +50,14 @@ function M.reset(self) self.state = empty_state() end +-- Invalidates a cached diff for a file +function M.invalidate(self, ...) + local files = { ... } + for _, path in ipairs(files) do + table.insert(self.state.invalidate, string.format("*:%s", path)) + end +end + local function _refresh(self, opts) logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index cd7d98ad4..c3b6ee819 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -126,6 +126,7 @@ end local M = {} function M.stage(...) + require("neogit.lib.git.repository"):invalidate(...) git.cli.add.files(...).call() end @@ -138,6 +139,7 @@ function M.stage_all() end function M.unstage(...) + require("neogit.lib.git.repository"):invalidate(...) git.cli.reset.files(...).call() end From 568fbdaed2cae105d0e33db90cca82f68df848ec Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 22:49:27 +0200 Subject: [PATCH 41/95] Formatting --- lua/neogit/lib/git/status.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index c3b6ee819..f2304ebb4 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -47,9 +47,7 @@ local function update_status(state) else local kind, rest = l:match("(.) (.+)") if kind == "?" then - table.insert(untracked_files, { - name = rest, - }) + table.insert(untracked_files, { name = rest }) elseif kind == "u" then local mode, _, _, _, _, _, _, _, _, name = rest:match("(..) (....) (%d+) (%d+) (%d+) (%d+) (%w+) (%w+) (%w+) (.+)") From c3f58eb6964adf0e11517ef707022d983d66126a Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 22:50:03 +0200 Subject: [PATCH 42/95] Add index updating visitor to track the git index. Without this the watcher can be a bit too aggressive and refresh the repo just after it was triggered by an action. --- lua/neogit/lib/git/index.lua | 6 ++++++ lua/neogit/lib/git/repository.lua | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lua/neogit/lib/git/index.lua b/lua/neogit/lib/git/index.lua index cfaf2f540..56af8f6db 100644 --- a/lua/neogit/lib/git/index.lua +++ b/lua/neogit/lib/git/index.lua @@ -8,4 +8,10 @@ function M.update() :spawn_async() end +function M.register(meta) + meta.update_index = function(state) + state.index.timestamp = state.index_stat() + end +end + return M diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index bc7e0a195..41f734e22 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -15,10 +15,17 @@ local function empty_state() git_path = function(path) return Path.new(root):joinpath(".git", path) end, + index_stat = function() + local index = Path.new(root):joinpath(".git", "index") + if index:exists() then + return index:_stat().mtime.sec + end + end, cwd = vim.fn.getcwd(), git_root = root, rev_toplevel = nil, invalidate = {}, + index = { timestamp = 0 }, head = { branch = nil, commit_message = "" }, upstream = { branch = nil, commit_message = "", remote = nil }, untracked = { items = {} }, @@ -120,7 +127,8 @@ if not M.initialized then setmetatable(M, meta) local modules = { - "status", + "status", -- Needs to be first + "index", "diff", "stash", "pull", From 9433fcfe0e05c9ebdcdb562bda0b15d4578c957f Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 22:59:40 +0200 Subject: [PATCH 43/95] test From 20bd3196f9ef26719d89d7d303c9d3652b44520d Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 1 Jul 2023 23:00:13 +0200 Subject: [PATCH 44/95] test empty From 78bea0edab609e40e6eaf27004508f6309a35a50 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Jul 2023 13:48:21 +0200 Subject: [PATCH 45/95] Add logging for diff metatable --- lua/neogit/lib/git/diff.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index ec0aef62e..fbc2fdfee 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -187,6 +187,7 @@ local function build_metatable(f, raw_output_fn) end, }) + logger.trace("[DIFF] Adding metatable for: " .. f.name) f.has_diff = true end From 869c6b87caef6374771f5968f5495a6c69de5066 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 2 Jul 2023 13:48:57 +0200 Subject: [PATCH 46/95] Fix logging --- lua/neogit/process.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/process.lua b/lua/neogit/process.lua index fb1f880e0..05275261c 100644 --- a/lua/neogit/process.lua +++ b/lua/neogit/process.lua @@ -66,7 +66,7 @@ local function create_preview_buffer() -- May be called multiple times due to scheduling if preview_buffer then if preview_buffer.buffer then - logger.debug("Preview buffer already exists. Focusing the existing one") + logger.trace("[PROCESS] Preview buffer already exists. Focusing the existing one") preview_buffer.buffer:focus() end return From 10d13fe598ec21e87eeac58d01fbdcfbdbac51ac Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 3 Jul 2023 13:55:13 +0200 Subject: [PATCH 47/95] formatting --- lua/neogit/status.lua | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 594735e25..b4f0fb327 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -881,10 +881,18 @@ local cmd_func_map = function() M.status_buffer:close() end, ["InitRepo"] = a.void(git.init.init_repo), - ["Depth1"] = a.void(function() set_folds { true, true, false } end), - ["Depth2"] = a.void(function() set_folds { false, true, false } end), - ["Depth3"] = a.void(function() set_folds { false, false, true } end), - ["Depth4"] = a.void(function() set_folds { false, false, false } end), + ["Depth1"] = a.void(function() + set_folds { true, true, false } + end), + ["Depth2"] = a.void(function() + set_folds { false, true, false } + end), + ["Depth3"] = a.void(function() + set_folds { false, false, true } + end), + ["Depth4"] = a.void(function() + set_folds { false, false, false } + end), ["Toggle"] = M.toggle, ["Discard"] = { "nv", @@ -892,7 +900,7 @@ local cmd_func_map = function() discard() M.update() end), - true + true, }, ["Stage"] = { "nv", @@ -900,7 +908,7 @@ local cmd_func_map = function() stage() M.update() end), - true + true, }, ["StageUnstaged"] = a.void(function() git.status.stage_modified() @@ -916,7 +924,7 @@ local cmd_func_map = function() unstage() M.update() end), - true + true, }, ["UnstageStaged"] = a.void(function() git.status.unstage_all() @@ -1238,7 +1246,7 @@ function M.create(kind, cwd) end function M.update() - git.repo:dispatch_refresh({ source = "status", callback = M.dispatch_refresh }) + git.repo:dispatch_refresh { source = "status", callback = M.dispatch_refresh } end function M.enable() From d764411fb192e145e5e73b602a00ccaf8d7100fc Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 3 Jul 2023 13:56:32 +0200 Subject: [PATCH 48/95] It's a hash-like table so this needs to be called manually --- lua/neogit/lib/git/repository.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 41f734e22..0f6597b2f 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -82,6 +82,7 @@ local function _refresh(self, opts) return end + self.lib.update_status(self.state) for name, fn in pairs(self.lib) do logger.trace(string.format("[REPO]: Refreshing %s", name)) fn(self.state) @@ -127,7 +128,7 @@ if not M.initialized then setmetatable(M, meta) local modules = { - "status", -- Needs to be first + "status", "index", "diff", "stash", From ae49278ca4a692bfaef2b1aa615e5e8037d06831 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 3 Jul 2023 14:00:06 +0200 Subject: [PATCH 49/95] Test out a more limited watcher --- lua/neogit/watcher.lua | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index e543562e1..79a5a5942 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -38,7 +38,7 @@ function M.watch_git_dir(gitdir) end local watch_gitdir_handler_db = util.debounce_trailing( - 300, + 200, a.void(function() logger.debug("[WATCHER] Dispatching Refresh") git.repo:dispatch_refresh { callback = status.dispatch_refresh, source = "watcher" } @@ -54,7 +54,7 @@ function M.watch_git_dir(gitdir) {}, a.void(function(err, filename, events) if err then - logger.error("[WATCHER] Git dir update error: %s", err) + logger.error(string.format("[WATCHER] Git dir update error: %s", err)) return end @@ -64,17 +64,14 @@ function M.watch_git_dir(gitdir) vim.inspect(events, { indent = "", newline = " " }) ) - -- stylua: ignore - if - filename:match("%.lock$") or - filename:match("COMMIT_EDITMSG") - then - logger.debug(string.format("%s (ignoring)", info)) + if filename:match("%.lock$") then return end logger.debug(info) - watch_gitdir_handler_db() + if filename:match("^index$") or filename:match("HEAD$") then + watch_gitdir_handler_db() + end end) ) From 95cea901f19ff7d44de4ecbaeac8d9099ba75350 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Jul 2023 11:35:13 +0200 Subject: [PATCH 50/95] revert this: not picking up enough external events --- lua/neogit/watcher.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 79a5a5942..a69b81dfd 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -64,14 +64,18 @@ function M.watch_git_dir(gitdir) vim.inspect(events, { indent = "", newline = " " }) ) - if filename:match("%.lock$") then + -- stylua: ignore + if + filename:match("%.lock$") or + filename:match("COMMIT_EDITMSG") or + filename:match("~$") + then + logger.debug(string.format("%s (ignoring)", info)) return end logger.debug(info) - if filename:match("^index$") or filename:match("HEAD$") then - watch_gitdir_handler_db() - end + watch_gitdir_handler_db() end) ) From bccb548c0a807aa0f1a33193694c09f65acd54b7 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Jul 2023 16:21:37 +0200 Subject: [PATCH 51/95] refactor: extract git functions from status buffer into git lib --- lua/neogit/lib/git/index.lua | 85 ++++++++++++++++++++++++++++++++++ lua/neogit/status.lua | 88 +++++++----------------------------- tests/hunks_spec.lua | 2 +- 3 files changed, 102 insertions(+), 73 deletions(-) diff --git a/lua/neogit/lib/git/index.lua b/lua/neogit/lib/git/index.lua index 56af8f6db..0fcc202b7 100644 --- a/lua/neogit/lib/git/index.lua +++ b/lua/neogit/lib/git/index.lua @@ -1,5 +1,90 @@ local M = {} +---Generates a patch that can be applied to index +---@param item any +---@param hunk any +---@param from any +---@param to any +---@param reverse boolean|nil +---@return string +function M.generate_patch(item, hunk, from, to, reverse) + reverse = reverse or false + from = from or 1 + to = to or hunk.diff_to - hunk.diff_from + + if from > to then + from, to = to, from + end + from = from + hunk.diff_from + to = to + hunk.diff_from + + local diff_content = {} + local len_start = hunk.index_len + local len_offset = 0 + + -- + 1 skips the hunk header, since we construct that manually afterwards + for k = hunk.diff_from + 1, hunk.diff_to do + local v = item.diff.lines[k] + local operand, line = v:match("^([+ -])(.*)") + + if operand == "+" or operand == "-" then + if from <= k and k <= to then + len_offset = len_offset + (operand == "+" and 1 or -1) + table.insert(diff_content, v) + else + -- If we want to apply the patch normally, we need to include every `-` line we skip as a normal line, + -- since we want to keep that line. + if not reverse then + if operand == "-" then + table.insert(diff_content, " " .. line) + end + -- If we want to apply the patch in reverse, we need to include every `+` line we skip as a normal line, since + -- it's unchanged as far as the diff is concerned and should not be reversed. + -- We also need to adapt the original line offset based on if we skip or not + elseif reverse then + if operand == "+" then + table.insert(diff_content, " " .. line) + end + len_start = len_start + (operand == "-" and -1 or 1) + end + end + else + table.insert(diff_content, v) + end + end + + table.insert(diff_content, 1, string.format("@@ -%d,%d +%d,%d @@", hunk.index_from, len_start, hunk.index_from, len_start + len_offset)) + table.insert(diff_content, 1, string.format("+++ b/%s", item.name)) + table.insert(diff_content, 1, string.format("--- a/%s", item.name)) + table.insert(diff_content, "\n") + + return table.concat(diff_content, "\n") +end + +---comment +---@param patch string diff generated with M.generate_patch +---@param opts table +---@return table +function M.apply(patch, opts) + opts = opts or { reverse = false, cached = false, index = false } + + local cmd = require("neogit.lib.git.cli").apply + + if opts.reverse then + cmd = cmd.reverse + end + + if opts.cached then + cmd = cmd.cached + end + + if opts.index then + cmd = cmd.index + end + + return cmd.with_patch(patch).call() +end + -- Make sure the index is in sync as git-status skips it -- Do this manually since the `cli` add --no-optional-locks function M.update() diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index b4f0fb327..fcea05d1c 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -507,62 +507,6 @@ function M.close(skip_close) end end -function M.generate_patch_from_selection(item, hunk, from, to, reverse) - reverse = reverse or false - from = from or 1 - to = to or hunk.diff_to - hunk.diff_from - - if from > to then - from, to = to, from - end - from = from + hunk.diff_from - to = to + hunk.diff_from - - local diff_content = {} - local len_start = hunk.index_len - local len_offset = 0 - - -- + 1 skips the hunk header, since we construct that manually afterwards - for k = hunk.diff_from + 1, hunk.diff_to do - local v = item.diff.lines[k] - local operand, line = v:match("^([+ -])(.*)") - - if operand == "+" or operand == "-" then - if from <= k and k <= to then - len_offset = len_offset + (operand == "+" and 1 or -1) - table.insert(diff_content, v) - else - -- If we want to apply the patch normally, we need to include every `-` line we skip as a normal line, - -- since we want to keep that line. - if not reverse then - if operand == "-" then - table.insert(diff_content, " " .. line) - end - -- If we want to apply the patch in reverse, we need to include every `+` line we skip as a normal line, since - -- it's unchanged as far as the diff is concerned and should not be reversed. - -- We also need to adapt the original line offset based on if we skip or not - elseif reverse then - if operand == "+" then - table.insert(diff_content, " " .. line) - end - len_start = len_start + (operand == "-" and -1 or 1) - end - end - else - table.insert(diff_content, v) - end - end - - local diff_header = - string.format("@@ -%d,%d +%d,%d @@", hunk.index_from, len_start, hunk.index_from, len_start + len_offset) - - table.insert(diff_content, 1, diff_header) - table.insert(diff_content, 1, string.format("+++ b/%s", item.name)) - table.insert(diff_content, 1, string.format("--- a/%s", item.name)) - table.insert(diff_content, "\n") - return table.concat(diff_content, "\n") -end - --- Returns commits in selection ---@return table local function get_selected_commits() @@ -646,8 +590,8 @@ local stage_selection = function() else local section, item, hunk, from, to = get_selection() if section and from then - local patch = M.generate_patch_from_selection(item, hunk, from, to) - cli.apply.cached.with_patch(patch).call() + local patch = git.index.generate_patch(item, hunk, from, to) + git.index.apply(patch, { cached = true }) end end end @@ -660,8 +604,8 @@ local unstage_selection = function() else local section, item, hunk, from, to = get_selection() if section and from then - local patch = M.generate_patch_from_selection(item, hunk, from, to, true) - cli.apply.reverse.cached.with_patch(patch).call() + local patch = git.index.generate_patch(item, hunk, from, to, true) + git.index.apply(patch, { reverse = true, cached = true }) end end end @@ -699,8 +643,8 @@ local stage = function() else if on_hunk and section.name ~= "untracked" then local hunk = get_current_hunk_of_item(item) - local patch = M.generate_patch_from_selection(item, hunk) - cli.apply.cached.with_patch(patch).call() + local patch = git.index.generate_patch(item, hunk) + git.index.apply(patch, { cached = true }) else git.status.stage(item.name) end @@ -732,8 +676,8 @@ local unstage = function() if on_hunk then local hunk = get_current_hunk_of_item(item) - local patch = M.generate_patch_from_selection(item, hunk, nil, nil, true) - cli.apply.reverse.cached.with_patch(patch).call() + local patch = git.index.generate_patch(item, hunk, nil, nil, true) + git.index.apply(patch, { reverse = true, cached = true }) else git.status.unstage(item.name) end @@ -778,16 +722,16 @@ end ---Discards selected lines local function discard_selection(section, item, hunk, from, to) logger.debug("Discarding selection hunk:" .. vim.inspect(hunk)) - local patch = M.generate_patch_from_selection(item, hunk, from, to, true) + local patch = git.index.generate_patch(item, hunk, from, to, true) logger.debug("Patch:" .. vim.inspect(patch)) if section.name == "staged" then - local result = cli.apply.reverse.index.with_patch(patch).call() + local result = git.index.apply(patch, { reverse = true, index = true }) if result.code ~= 0 then error("Failed to discard" .. vim.inspect(result)) end else - cli.apply.reverse.with_patch(patch).call() + git.index.apply(patch, { reverse = true }) end end @@ -799,9 +743,9 @@ local function discard_hunk(section, item, lines, hunk) local diff = table.concat(lines or {}, "\n") diff = table.concat({ "--- a/" .. item.name, "+++ b/" .. item.name, diff, "" }, "\n") if section == "staged" then - cli.apply.reverse.index.with_patch(diff).call() + git.index.apply(diff, { reverse = true, index = true }) else - cli.apply.reverse.with_patch(diff).call() + git.index.apply(diff, { reverse = true }) end end @@ -1262,9 +1206,9 @@ function M.get_status() end function M.wait_on_current_operation(ms) - vim.wait(ms or 1000, function() - return not M.current_operation - end) + -- vim.wait(ms or 1000, function() + -- return not M.current_operation + -- end) end return M diff --git a/tests/hunks_spec.lua b/tests/hunks_spec.lua index 4f75bd057..aace51207 100644 --- a/tests/hunks_spec.lua +++ b/tests/hunks_spec.lua @@ -1,5 +1,5 @@ local eq = assert.are.same -local generate_patch_from_selection = require("neogit.status").generate_patch_from_selection +local generate_patch_from_selection = require("neogit.lib.git.index").generate_patch -- Helper-function to keep the testsuite clean, since the interface to the -- function under test is quite bloated From 4e72b92f98fe4d3b65cd3edc0b6300d8986dbbe2 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Jul 2023 16:32:00 +0200 Subject: [PATCH 52/95] extract util function --- lua/neogit/lib/util.lua | 9 +++++++++ lua/neogit/status.lua | 9 +++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lua/neogit/lib/util.lua b/lua/neogit/lib/util.lua index 15623797e..437c863d2 100644 --- a/lua/neogit/lib/util.lua +++ b/lua/neogit/lib/util.lua @@ -267,6 +267,14 @@ local function debounce_trailing(ms, fn) end end +-- Ensure a string is a minimum width +---@param s string +---@param len integer +---@return string +local function pad_right(s, len) + return s .. string.rep(" ", math.max(len - #s, 0)) +end + return { time = time, time_async = time_async, @@ -295,4 +303,5 @@ return { str_min_width = str_min_width, str_clamp = str_clamp, debounce_trailing = debounce_trailing, + pad_right = pad_right, } diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index fcea05d1c..a416fd147 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -13,7 +13,8 @@ local LineBuffer = require("neogit.lib.line_buffer") local fs = require("neogit.lib.fs") local input = require("neogit.lib.input") -local map = require("neogit.lib.util").map +local util = require("neogit.lib.util") +local map = util.map local api = vim.api local fn = vim.fn @@ -145,10 +146,6 @@ local function format_mode(mode) return mode end -local function pad_right(s, len) - return s .. string.rep(" ", math.max(len - #s, 0)) -end - local function draw_buffer() M.status_buffer:clear_sign_group("hl") M.status_buffer:clear_sign_group("fold_markers") @@ -207,7 +204,7 @@ local function draw_buffer() location.files = {} for _, f in ipairs(data.items) do - local label = pad_right(format_mode(f.mode), max_len) + local label = util.pad_right(format_mode(f.mode), max_len) if label and vim.o.columns < 120 then label = vim.trim(label) end From 8b54890d123477c677b24083fa078e213c72f792 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Jul 2023 16:32:18 +0200 Subject: [PATCH 53/95] Revert --- lua/neogit/status.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index a416fd147..643784753 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -1203,9 +1203,9 @@ function M.get_status() end function M.wait_on_current_operation(ms) - -- vim.wait(ms or 1000, function() - -- return not M.current_operation - -- end) + vim.wait(ms or 1000, function() + return not M.current_operation + end) end return M From 84776f616c8c2be8ecc057cbfd5fa67f75c35167 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Jul 2023 16:42:09 +0200 Subject: [PATCH 54/95] formatting --- lua/neogit.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 2bd64abb3..7ea3b2b5a 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -57,7 +57,6 @@ local open = function(opts) return end - if not cli.git_is_repository_sync(opts.cwd) then if input.get_confirmation( @@ -72,7 +71,7 @@ local open = function(opts) end end - require("neogit.lib.git").repo:dispatch_refresh({ source = "open" }) + require("neogit.lib.git").repo:dispatch_refresh { source = "open" } if opts[1] ~= nil then local popup_name = opts[1] From 8456dca67f1a2f13b6903954156653de95519617 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 4 Jul 2023 22:55:26 +0200 Subject: [PATCH 55/95] Clean up status --- lua/neogit/status.lua | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 643784753..33b25bf10 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -33,8 +33,6 @@ M.commit_view = nil ---@field files StatusItem[] M.locations = {} -M.outdated = {} - ---@class StatusItem ---@field name string ---@field first number @@ -415,7 +413,7 @@ M.refresh_manually = a.void(function(fname) return end - M.refresh() + git.repo:dispatch_refresh { callback = M.refresh } end) --- Compatibility endpoint to refresh data from an autocommand. @@ -587,8 +585,7 @@ local stage_selection = function() else local section, item, hunk, from, to = get_selection() if section and from then - local patch = git.index.generate_patch(item, hunk, from, to) - git.index.apply(patch, { cached = true }) + git.index.apply(git.index.generate_patch(item, hunk, from, to), { cached = true }) end end end @@ -601,8 +598,7 @@ local unstage_selection = function() else local section, item, hunk, from, to = get_selection() if section and from then - local patch = git.index.generate_patch(item, hunk, from, to, true) - git.index.apply(patch, { reverse = true, cached = true }) + git.index.apply(git.index.generate_patch(item, hunk, from, to, true), { reverse = true, cached = true }) end end end @@ -1002,7 +998,8 @@ local cmd_func_map = function() end end), ["RefreshBuffer"] = function() - M.dispatch_refresh() + notif.create("Refreshing Status") + git.repo:dispatch_refresh { callback = M.dispatch_refresh } end, ["HelpPopup"] = function() local line = M.status_buffer:get_current_line() @@ -1198,10 +1195,6 @@ function M.disable() M.disabled = true end -function M.get_status() - return M.status -end - function M.wait_on_current_operation(ms) vim.wait(ms or 1000, function() return not M.current_operation From 524e7d3ca6d5665f53a449dd6ba54e3411d4b97e Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 22:16:18 +0200 Subject: [PATCH 56/95] Add notes on vim.system --- todo.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/todo.md b/todo.md index 8d6434523..939846f56 100644 --- a/todo.md +++ b/todo.md @@ -34,4 +34,9 @@ ## Highlighting -## Jobs +## Process +Rewrite process to use `vim.system` instead of `vim.fn.jobstart` +https://github.com/neovim/neovim/commit/c0952e62fd0ee16a3275bb69e0de04c836b39015 +https://github.com/neovim/neovim/blob/1de82e16c1216e1dbe22cf7a8ec9ea9e9e69b631/runtime/lua/vim/_editor.lua#L84 +https://github.com/nvim-treesitter/nvim-treesitter/pull/4921/files +https://github.com/neovim/neovim/pull/23827 From 3e0071becf27355acd5022e087bcc049d3377fd5 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 22:18:41 +0200 Subject: [PATCH 57/95] Use lua instead of c --- lua/neogit/operations.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/operations.lua b/lua/neogit/operations.lua index 9602d3fea..d47799a11 100644 --- a/lua/neogit/operations.lua +++ b/lua/neogit/operations.lua @@ -17,7 +17,7 @@ function M.wait(key, time) if M[key] == nil then return end - vim.fn.wait(time or 1000, function() + vim.wait(time or 1000, function() return M[key] == false end, 100) end From ad26f4460688590713930953628653224f3a667c Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 22:19:28 +0200 Subject: [PATCH 58/95] Run all update functions async, with a callback after they're done. Much snappier performance --- lua/neogit/lib/git/repository.lua | 63 ++++++++++++++++--------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 0f6597b2f..4253b73c8 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -65,11 +65,16 @@ function M.invalidate(self, ...) end end -local function _refresh(self, opts) +local refresh_lock = a.control.Semaphore.new(1) +local lock_holder + +M.dispatch_refresh = a.void(function(self, opts) + opts = opts or {} + logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) - if self.state.git_root == "" then - logger.debug("[REPO]: Refreshing ABORTED - No git_root") + if refresh_lock.permits == 0 then + logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", lock_holder)) return end @@ -82,30 +87,8 @@ local function _refresh(self, opts) return end - self.lib.update_status(self.state) - for name, fn in pairs(self.lib) do - logger.trace(string.format("[REPO]: Refreshing %s", name)) - fn(self.state) - end - - self.state.invalidate = {} - - logger.info("[REPO]: Refreshes completed") - - if opts.callback then - logger.debug("[REPO]: Running refresh callback") - opts.callback() - end -end - -local refresh_lock = a.control.Semaphore.new(1) -local lock_holder - -M.dispatch_refresh = a.void(function(self, opts) - opts = opts or {} - - if refresh_lock.permits == 0 then - logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", lock_holder)) + if self.state.git_root == "" then + logger.debug("[REPO]: Refreshing ABORTED - No git_root") return end @@ -114,11 +97,29 @@ M.dispatch_refresh = a.void(function(self, opts) local permit = refresh_lock:acquire() a.util.scheduler() - _refresh(self, opts) + -- Status needs to run first because other update fn's depend on it + self.lib.update_status(self.state) + + local updates = {} + for name, fn in pairs(self.lib) do + table.insert(updates, function() + logger.trace(string.format("[REPO]: Refreshing %s", name)) + fn(self.state) + end) + end + + a.util.run_all(updates, function() + self.state.invalidate = {} + + logger.info("[REPO]: Refreshes completed - freeing refresh lock") + lock_holder = nil + permit:forget() - logger.debug("[REPO]: freeing refresh lock") - lock_holder = nil - permit:forget() + if opts.callback then + logger.debug("[REPO]: Running refresh callback") + opts.callback() + end + end) end) if not M.initialized then From cac1dcc563edfd66239f0fda7d0aca94820d0633 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 22:20:01 +0200 Subject: [PATCH 59/95] cleanup --- tests/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/init.lua b/tests/init.lua index 62fe24df5..3580a164d 100644 --- a/tests/init.lua +++ b/tests/init.lua @@ -22,5 +22,5 @@ end require("plenary.test_harness").test_directory("tests", { minimal_init = "tests/minimal_init.lua", - sequential = true, + -- sequential = true, }) From c375ae4bd6c8a18314911ee7edf95d5259678fd0 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 22:48:03 +0200 Subject: [PATCH 60/95] Reorder lock --- lua/neogit/lib/git/repository.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 4253b73c8..8b01ea6a9 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -112,8 +112,8 @@ M.dispatch_refresh = a.void(function(self, opts) self.state.invalidate = {} logger.info("[REPO]: Refreshes completed - freeing refresh lock") - lock_holder = nil permit:forget() + lock_holder = nil if opts.callback then logger.debug("[REPO]: Running refresh callback") From 1f2551212261b4a72ebd9a010f6bea144f551497 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 23:05:11 +0200 Subject: [PATCH 61/95] Log and only update status once --- lua/neogit/lib/git/repository.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 8b01ea6a9..83dedb90a 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -98,14 +98,17 @@ M.dispatch_refresh = a.void(function(self, opts) a.util.scheduler() -- Status needs to run first because other update fn's depend on it + logger.trace("[REPO]: Refreshing %s", "update_status") self.lib.update_status(self.state) local updates = {} for name, fn in pairs(self.lib) do - table.insert(updates, function() - logger.trace(string.format("[REPO]: Refreshing %s", name)) - fn(self.state) - end) + if name ~= "update_status" then + table.insert(updates, function() + logger.trace(string.format("[REPO]: Refreshing %s", name)) + fn(self.state) + end) + end end a.util.run_all(updates, function() From 27971af393160cd10f1d70fce47798d6a6e5ee48 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 10 Jul 2023 13:36:56 +0200 Subject: [PATCH 62/95] lint: unused variables --- lua/neogit/integrations/diffview.lua | 1 - lua/neogit/popups/cherry_pick/actions.lua | 1 - lua/neogit/popups/merge/actions.lua | 2 -- 3 files changed, 4 deletions(-) diff --git a/lua/neogit/integrations/diffview.lua b/lua/neogit/integrations/diffview.lua index c86b1c72c..a67f14a60 100644 --- a/lua/neogit/integrations/diffview.lua +++ b/lua/neogit/integrations/diffview.lua @@ -9,7 +9,6 @@ local dv_lib = require("diffview.lib") local dv_utils = require("diffview.utils") local neogit = require("neogit") -local status = require("neogit.status") local a = require("plenary.async") local old_config diff --git a/lua/neogit/popups/cherry_pick/actions.lua b/lua/neogit/popups/cherry_pick/actions.lua index 04089ebe1..069b98909 100644 --- a/lua/neogit/popups/cherry_pick/actions.lua +++ b/lua/neogit/popups/cherry_pick/actions.lua @@ -1,6 +1,5 @@ local M = {} -local a = require("plenary.async") local git = require("neogit.lib.git") local CommitSelectViewBuffer = require("neogit.buffers.commit_select_view") diff --git a/lua/neogit/popups/merge/actions.lua b/lua/neogit/popups/merge/actions.lua index f30c6e0ae..d8452018d 100644 --- a/lua/neogit/popups/merge/actions.lua +++ b/lua/neogit/popups/merge/actions.lua @@ -3,8 +3,6 @@ local M = {} local git = require("neogit.lib.git") local input = require("neogit.lib.input") -local a = require("plenary.async") - local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") function M.in_merge() From 4c8ae4e3cba8199f17e6ee8ebed4bc07fa39439b Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 10 Jul 2023 22:09:58 +0200 Subject: [PATCH 63/95] fix/lint --- lua/neogit/lib/git/index.lua | 6 +++++- lua/neogit/lib/popup/builder.lua | 2 ++ lua/neogit/popups/commit/actions.lua | 18 ++++++++---------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lua/neogit/lib/git/index.lua b/lua/neogit/lib/git/index.lua index 0fcc202b7..cafcb8989 100644 --- a/lua/neogit/lib/git/index.lua +++ b/lua/neogit/lib/git/index.lua @@ -53,7 +53,11 @@ function M.generate_patch(item, hunk, from, to, reverse) end end - table.insert(diff_content, 1, string.format("@@ -%d,%d +%d,%d @@", hunk.index_from, len_start, hunk.index_from, len_start + len_offset)) + table.insert( + diff_content, + 1, + string.format("@@ -%d,%d +%d,%d @@", hunk.index_from, len_start, hunk.index_from, len_start + len_offset) + ) table.insert(diff_content, 1, string.format("+++ b/%s", item.name)) table.insert(diff_content, 1, string.format("--- a/%s", item.name)) table.insert(diff_content, "\n") diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index 1aeda133c..c479e6435 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -1,7 +1,9 @@ local a = require("plenary.async") +local git = require("neogit.lib.git") local state = require("neogit.lib.state") local config = require("neogit.lib.git.config") local util = require("neogit.lib.util") +local logger = require("neogit.logger") local M = {} diff --git a/lua/neogit/popups/commit/actions.lua b/lua/neogit/popups/commit/actions.lua index 02b163387..4923d0fb5 100644 --- a/lua/neogit/popups/commit/actions.lua +++ b/lua/neogit/popups/commit/actions.lua @@ -6,16 +6,14 @@ local git = require("neogit.lib.git") local client = require("neogit.client") local function do_commit(popup, cmd) - client.wrap(cmd.arg_list(popup:get_arguments()), - { - autocmd = "NeogitCommitComplete", - msg = { - setup = "Committing...", - success = "Committed!", - fail = "Couldn't commit", - }, - } - ) + client.wrap(cmd.arg_list(popup:get_arguments()), { + autocmd = "NeogitCommitComplete", + msg = { + setup = "Committing...", + success = "Committed!", + fail = "Couldn't commit", + }, + }) end local function commit_special(popup, method) From c11e444d9bb5c6e4e497000b351f7b0a2615e71a Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 11 Jul 2023 13:49:36 +0200 Subject: [PATCH 64/95] Standardize option --- lua/neogit/lib/git/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/neogit/lib/git/init.lua b/lua/neogit/lib/git/init.lua index 10cd1e059..081bcefe2 100644 --- a/lua/neogit/lib/git/init.lua +++ b/lua/neogit/lib/git/init.lua @@ -36,8 +36,8 @@ M.init_repo = function() if cli.git_is_repository_sync() then if not input.get_confirmation( - string.format("Reinitialize existing repository %s? (y or n) ", directory), - { default = 2 } + string.format("Reinitialize existing repository %s?", directory), + { values = { "&Yes", "&No" }, default = 2 } ) then return From 389ba2961e410d8843e61296ce5e1f2e480b52c1 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 11 Jul 2023 13:49:53 +0200 Subject: [PATCH 65/95] Confirm modifying a commit if it has already been published upstream --- lua/neogit/popups/commit/actions.lua | 33 +++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/lua/neogit/popups/commit/actions.lua b/lua/neogit/popups/commit/actions.lua index 4923d0fb5..fd201a96e 100644 --- a/lua/neogit/popups/commit/actions.lua +++ b/lua/neogit/popups/commit/actions.lua @@ -2,8 +2,25 @@ local M = {} local CommitSelectViewBuffer = require("neogit.buffers.commit_select_view") local git = require("neogit.lib.git") --- local a = require("plenary.async") local client = require("neogit.client") +local input = require("neogit.lib.input") + +local function confirm_modifications() + if + #git.repo.unmerged.items < 1 + and not input.get_confirmation( + string.format( + "This commit has already been published to %s, do you really want to modify it?", + git.repo.upstream.ref + ), + { values = { "&Yes", "&No" }, default = 2 } + ) + then + return false + end + + return true +end local function do_commit(popup, cmd) client.wrap(cmd.arg_list(popup:get_arguments()), { @@ -22,9 +39,7 @@ local function commit_special(popup, method) return end - -- a.util.scheduler() do_commit(popup, git.cli.commit.args(method, commit)) - -- a.util.scheduler() return commit end @@ -34,14 +49,26 @@ function M.commit(popup) end function M.extend(popup) + if not confirm_modifications() then + return + end + do_commit(popup, git.cli.commit.no_edit.amend) end function M.reword(popup) + if not confirm_modifications() then + return + end + do_commit(popup, git.cli.commit.amend.only) end function M.amend(popup) + if not confirm_modifications() then + return + end + do_commit(popup, git.cli.commit.amend) end From 4d68bef44d4624c39f80528a153cf091fbafd57a Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 11 Jul 2023 19:26:17 +0200 Subject: [PATCH 66/95] Allow for rebasing onto pushremote, and add 'b' action to rebase onto base branch --- lua/neogit/popups/rebase/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/neogit/popups/rebase/init.lua b/lua/neogit/popups/rebase/init.lua index b6f2d61b2..3bd9c6839 100644 --- a/lua/neogit/popups/rebase/init.lua +++ b/lua/neogit/popups/rebase/init.lua @@ -30,6 +30,7 @@ function M.create(commit) :group_heading_if(not in_rebase, "Rebase " .. (branch and (branch .. " ") or "") .. "onto") :action_if(not in_rebase, "p", git.branch.pushRemote_label(), actions.onto_pushRemote) :action_if(not in_rebase, "u", git.branch.upstream_label(), actions.onto_upstream) + :action_if(not in_rebase and branch ~= base_branch, "b", base_branch, actions.onto_base) :action_if(not in_rebase, "e", "elsewhere", actions.onto_elsewhere) :action_if(not in_rebase and branch ~= base_branch, "b", base_branch, actions.onto_base) :new_action_group_if(not in_rebase, "Rebase") From 38dc48d36984589916849b6c4720adf992feb381 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 12 Jul 2023 11:30:06 +0200 Subject: [PATCH 67/95] Remove duplicate and add highlight to base branch name --- lua/neogit/popups/rebase/init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/neogit/popups/rebase/init.lua b/lua/neogit/popups/rebase/init.lua index 3bd9c6839..73743ec59 100644 --- a/lua/neogit/popups/rebase/init.lua +++ b/lua/neogit/popups/rebase/init.lua @@ -30,7 +30,6 @@ function M.create(commit) :group_heading_if(not in_rebase, "Rebase " .. (branch and (branch .. " ") or "") .. "onto") :action_if(not in_rebase, "p", git.branch.pushRemote_label(), actions.onto_pushRemote) :action_if(not in_rebase, "u", git.branch.upstream_label(), actions.onto_upstream) - :action_if(not in_rebase and branch ~= base_branch, "b", base_branch, actions.onto_base) :action_if(not in_rebase, "e", "elsewhere", actions.onto_elsewhere) :action_if(not in_rebase and branch ~= base_branch, "b", base_branch, actions.onto_base) :new_action_group_if(not in_rebase, "Rebase") @@ -43,7 +42,7 @@ function M.create(commit) :action_if(not in_rebase, "f", "to autosquash") :env({ commit = commit, - highlight = { branch, git.repo.upstream.ref }, + highlight = { branch, git.repo.upstream.ref, base_branch }, bold = { "@{upstream}", "pushRemote" }, }) :build() From e81232f9dca93f8466ac616a96449463bbbfd6f7 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 12 Jul 2023 22:24:50 +0200 Subject: [PATCH 68/95] If remote.defaultPush is set, use that instead of blank --- lua/neogit/popups/branch_config/actions.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lua/neogit/popups/branch_config/actions.lua b/lua/neogit/popups/branch_config/actions.lua index 56415c24a..f4504f2a2 100644 --- a/lua/neogit/popups/branch_config/actions.lua +++ b/lua/neogit/popups/branch_config/actions.lua @@ -7,14 +7,18 @@ local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder") local M = {} function M.remotes_for_config() - local remotes = { - { display = "", value = "" }, - } - + local remotes = {} for _, name in ipairs(git.remote.list()) do table.insert(remotes, { display = name, value = name }) end + local pushDefault = git.config.get("remote.pushDefault") + if pushDefault:is_set() then + table.insert(remotes, { display = "remote.pushDefault:" .. pushDefault.value, value = "" }) + else + table.insert(remotes, { display = "", value = "" }) + end + return remotes end From 95a4f9f731fed23f60b940bf5c62a1839d413a62 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 22 Jul 2023 22:57:20 +0200 Subject: [PATCH 69/95] revert sequential change --- tests/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/init.lua b/tests/init.lua index 0415991a2..b1c2fcea2 100644 --- a/tests/init.lua +++ b/tests/init.lua @@ -24,5 +24,5 @@ end require("plenary.test_harness").test_directory("tests/specs", { minimal_init = "tests/minimal_init.lua", - -- sequential = true, + sequential = true, }) From 917b450831ce25c78f184db132eecf2d63126625 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sat, 22 Jul 2023 23:47:32 +0200 Subject: [PATCH 70/95] Move filewatcher to status setup --- lua/neogit.lua | 5 ++++- lua/neogit/watcher.lua | 20 ++++++-------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 2140c7e59..c003adc8e 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -12,6 +12,8 @@ local logger = require("neogit.logger") local cli = require("neogit.lib.git.cli") local notification = require("neogit.lib.notification") +local Path = require("plenary.path") + ---@class OpenOpts ---@field cwd string|nil ---TODO: popup enum @@ -40,7 +42,6 @@ local setup = function(opts) hl.setup() signs.setup() state.setup() - watcher.setup() end ---@param opts OpenOpts @@ -83,6 +84,8 @@ local open = function(opts) popup.create() end else + watcher.setup(Path.new(opts.cwd, ".git"):absolute()) + a.run(function() status.create(opts.kind, opts.cwd) end) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index a69b81dfd..fd7629611 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -8,20 +8,17 @@ local util = require("neogit.lib.util") local status = require("neogit.status") local git = require("neogit.lib.git") -local Path = require("plenary.path") local a = require("plenary.async") M.watcher = {} -local function git_dir() - return Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute() -end - -function M.setup() - local gitdir = git_dir() - local watcher = M.watch_git_dir(gitdir) +function M.setup(gitdir) + if M.watcher[gitdir] then + logger.debug(string.format("[WATCHER] for '%s' already setup! Bailing.", gitdir)) + return + end - M.watcher[gitdir] = watcher + M.watcher[gitdir] = M.watch_git_dir(gitdir) end -- Adapted from https://github.com/lewis6991/gitsigns.nvim/blob/main/lua/gitsigns/manager.lua#L575 @@ -32,11 +29,6 @@ function M.watch_git_dir(gitdir) return end - if M.watcher[gitdir] then - logger.debug(string.format("[WATCHER] for '%s' already setup! Bailing.", gitdir)) - return - end - local watch_gitdir_handler_db = util.debounce_trailing( 200, a.void(function() From 2c0b0fda71cec903b45addd57387d82f6b66ee74 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 23 Jul 2023 19:03:06 +0200 Subject: [PATCH 71/95] Rewrite repo to be locked to CWD --- lua/neogit.lua | 8 +- lua/neogit/lib/git.lua | 26 ++++- lua/neogit/lib/git/diff.lua | 4 +- lua/neogit/lib/git/repository.lua | 172 +++++++++++++++--------------- lua/neogit/lib/git/status.lua | 4 +- lua/neogit/status.lua | 19 +++- lua/neogit/watcher.lua | 37 ++++--- plugin/neogit.lua | 2 +- 8 files changed, 155 insertions(+), 117 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index c003adc8e..e5d2f1c87 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -6,14 +6,11 @@ local hl = require("neogit.lib.hl") local status = require("neogit.status") local state = require("neogit.lib.state") local input = require("neogit.lib.input") -local watcher = require("neogit.watcher") local logger = require("neogit.logger") local cli = require("neogit.lib.git.cli") local notification = require("neogit.lib.notification") -local Path = require("plenary.path") - ---@class OpenOpts ---@field cwd string|nil ---TODO: popup enum @@ -41,7 +38,6 @@ local setup = function(opts) hl.setup() signs.setup() - state.setup() end ---@param opts OpenOpts @@ -72,7 +68,7 @@ local open = function(opts) end end - require("neogit.lib.git").repo:dispatch_refresh { source = "open" } + state.setup() if opts[1] ~= nil then local popup_name = opts[1] @@ -84,8 +80,6 @@ local open = function(opts) popup.create() end else - watcher.setup(Path.new(opts.cwd, ".git"):absolute()) - a.run(function() status.create(opts.kind, opts.cwd) end) diff --git a/lua/neogit/lib/git.lua b/lua/neogit/lib/git.lua index 7f2210538..0a4afd395 100644 --- a/lua/neogit/lib/git.lua +++ b/lua/neogit/lib/git.lua @@ -1,4 +1,4 @@ -return { +local git = { branch = require("neogit.lib.git.branch"), cherry_pick = require("neogit.lib.git.cherry_pick"), cli = require("neogit.lib.git.cli"), @@ -15,10 +15,32 @@ return { rebase = require("neogit.lib.git.rebase"), reflog = require("neogit.lib.git.reflog"), remote = require("neogit.lib.git.remote"), - repo = require("neogit.lib.git.repository"), reset = require("neogit.lib.git.reset"), revert = require("neogit.lib.git.revert"), sequencer = require("neogit.lib.git.sequencer"), stash = require("neogit.lib.git.stash"), status = require("neogit.lib.git.status"), } + +local repositories = {} + +setmetatable(git, { + __index = function(_, method) + if method == "repo" then + local cwd = require("neogit.status").cwd + if not cwd then + require("neogit.logger").error("[GIT] Cannot construct repository! No CWD") + return + end + + if not repositories[cwd] then + repositories[cwd] = require("neogit.lib.git.repository").new(cwd) + end + + require("neogit.logger").info("[GIT] Found repo for " .. cwd) + return repositories[cwd] + end + end, +}) + +return git diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index fbc2fdfee..d29fc656f 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -235,8 +235,8 @@ return { register = function(meta) meta.update_diffs = function(repo) local filter - if repo.invalidate[1] then - filter = ItemFilter.create(repo.invalidate) + if repo.invalid[1] then + filter = ItemFilter.create(repo.invalid) end for _, f in ipairs(repo.untracked.items) do diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 83dedb90a..187d17857 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -7,7 +7,7 @@ local logger = require("neogit.logger") -- cwd may change after the status is refreshed and used, especially if using -- rooter plugins with lsp integration -- stylua: ignore start -local function empty_state() +local function empty_state(cwd) local root = require("neogit.lib.git.cli").git_root() local Path = require("plenary.path") @@ -21,10 +21,10 @@ local function empty_state() return index:_stat().mtime.sec end end, - cwd = vim.fn.getcwd(), + cwd = cwd, git_root = root, rev_toplevel = nil, - invalidate = {}, + invalid = {}, index = { timestamp = 0 }, head = { branch = nil, commit_message = "" }, upstream = { branch = nil, commit_message = "", remote = nil }, @@ -42,111 +42,111 @@ local function empty_state() end -- stylua: ignore end -local meta = { - __index = function(self, method) - return self.state[method] - end, -} +local Repo = {} +Repo.__index = Repo -local M = {} +function Repo.new(cwd) + logger.info(string.format("[REPO]: Initializing Repository for %s", cwd)) -M.state = empty_state() -M.lib = {} + local repo = setmetatable( + vim.tbl_extend("error", empty_state(cwd), { + lib = {}, + refresh_lock = a.control.Semaphore.new(1), + lock_holder = nil, + }), + Repo + ) -function M.reset(self) - self.state = empty_state() + local modules = { + "status", + "index", + "diff", + "stash", + "pull", + "push", + "log", + "rebase", + "sequencer", + "merge", + } + + for _, m in ipairs(modules) do + require("neogit.lib.git." .. m).register(repo.lib) + end + + return repo +end + +function Repo:reset() + vim.tbl_extend("force", self, empty_state(vim.fn.getcwd())) end -- Invalidates a cached diff for a file -function M.invalidate(self, ...) +function Repo:invalidate(...) local files = { ... } for _, path in ipairs(files) do - table.insert(self.state.invalidate, string.format("*:%s", path)) + table.insert(self.invalid, string.format("*:%s", path)) end end -local refresh_lock = a.control.Semaphore.new(1) -local lock_holder - -M.dispatch_refresh = a.void(function(self, opts) - opts = opts or {} +function Repo:dispatch_refresh(opts) + a.void(function(self, opts) + opts = opts or {} - logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) + logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) - if refresh_lock.permits == 0 then - logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", lock_holder)) - return - end - - -- stylua: ignore - if - self.state.index.timestamp == self.state.index_stat() and - opts.source == "watcher" - then - logger.debug("[REPO]: Refreshing ABORTED - .git/index hasn't been modified since last refresh") - return - end + if self.refresh_lock.permits == 0 then + logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", self.lock_holder)) + return + end - if self.state.git_root == "" then - logger.debug("[REPO]: Refreshing ABORTED - No git_root") - return - end + -- stylua: ignore + if + self.index.timestamp == self.index_stat() and + opts.source == "watcher" + then + logger.debug("[REPO]: Refreshing ABORTED - .git/index hasn't been modified since last refresh") + return + end - lock_holder = opts.source or "UNKNOWN" - logger.debug(string.format("[REPO]: Acquiring refresh lock (source: %s)", lock_holder)) - local permit = refresh_lock:acquire() - - a.util.scheduler() - -- Status needs to run first because other update fn's depend on it - logger.trace("[REPO]: Refreshing %s", "update_status") - self.lib.update_status(self.state) - - local updates = {} - for name, fn in pairs(self.lib) do - if name ~= "update_status" then - table.insert(updates, function() - logger.trace(string.format("[REPO]: Refreshing %s", name)) - fn(self.state) - end) + if self.git_root == "" then + logger.debug("[REPO]: Refreshing ABORTED - No git_root") + return end - end - a.util.run_all(updates, function() - self.state.invalidate = {} + self.lock_holder = opts.source or "UNKNOWN" + logger.debug(string.format("[REPO]: Acquiring refresh lock (source: %s)", self.lock_holder)) + local permit = self.refresh_lock:acquire() - logger.info("[REPO]: Refreshes completed - freeing refresh lock") - permit:forget() - lock_holder = nil + a.util.scheduler() - if opts.callback then - logger.debug("[REPO]: Running refresh callback") - opts.callback() - end - end) -end) + -- Status needs to run first because other update fn's depend on it + logger.trace("[REPO]: Refreshing %s", "update_status") + self.lib.update_status(self) -if not M.initialized then - logger.info("[REPO]: Initializing Repository") - M.initialized = true + local updates = {} + for name, fn in pairs(self.lib) do + if name ~= "update_status" then + table.insert(updates, function() + logger.trace(string.format("[REPO]: Refreshing %s", name)) + fn(self) + end) + end + end - setmetatable(M, meta) + a.util.run_all(updates, function() + self.invalid = {} - local modules = { - "status", - "index", - "diff", - "stash", - "pull", - "push", - "log", - "rebase", - "sequencer", - "merge", - } + logger.info("[REPO]: Refreshes completed - freeing refresh lock") + permit:forget() + self.lock_holder = nil - for _, m in ipairs(modules) do - require("neogit.lib.git." .. m).register(M.lib) - end + if opts.callback then + logger.debug("[REPO]: Running refresh callback") + opts.callback() + end + end) + end)(self, opts) end -return M +return Repo diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index f2304ebb4..772a6ca15 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -124,7 +124,7 @@ end local M = {} function M.stage(...) - require("neogit.lib.git.repository"):invalidate(...) + require("neogit.lib.git").repo:invalidate(...) git.cli.add.files(...).call() end @@ -137,7 +137,7 @@ function M.stage_all() end function M.unstage(...) - require("neogit.lib.git.repository"):invalidate(...) + require("neogit.lib.git").repo:invalidate(...) git.cli.reset.files(...).call() end diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index ff1fbeaa0..6134504c2 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -11,6 +11,8 @@ local F = require("neogit.lib.functional") local LineBuffer = require("neogit.lib.line_buffer") local fs = require("neogit.lib.fs") local input = require("neogit.lib.input") +local Path = require("plenary.path") +local watcher = require("neogit.watcher") local util = require("neogit.lib.util") local map = util.map @@ -504,15 +506,19 @@ end M.dispatch_reset = a.void(M.reset) +-- TODO: Close Watcher, repo? function M.close(skip_close) if not skip_close then M.status_buffer:close() end notif.delete_all() + watcher.stop(Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute()) M.status_buffer = nil + M.cwd = nil vim.o.autochdir = M.prev_autochdir if M.cwd_changed then - vim.cmd("cd -") + vim.loop.chdir(M.cwd_reset) + M.cwd_reset = nil end end @@ -1126,18 +1132,25 @@ function M.create(kind, cwd) name = "NeogitStatus", filetype = "NeogitStatus", kind = kind, + after = function() + -- require("neogit.lib.git").repo:dispatch_refresh { source = "open" } + end, ---@param buffer Buffer initialize = function(buffer) logger.debug("[STATUS BUFFER]: Initializing...") M.status_buffer = buffer - M.prev_autochdir = vim.o.autochdir if cwd then M.cwd_changed = true - vim.cmd(string.format("cd %s", cwd)) + M.cwd_reset = vim.loop.cwd() + vim.loop.chdir(cwd) end + M.cwd = vim.loop.cwd() + + require("neogit.lib.git").repo:dispatch_refresh { source = "open" } + watcher.setup(Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute()) vim.o.autochdir = false diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index fd7629611..978b2cdfc 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -1,30 +1,20 @@ local M = {} +M.watcher = {} + local uv = vim.loop local config = require("neogit.config") local logger = require("neogit.logger") local util = require("neogit.lib.util") -local status = require("neogit.status") local git = require("neogit.lib.git") local a = require("plenary.async") -M.watcher = {} - -function M.setup(gitdir) - if M.watcher[gitdir] then - logger.debug(string.format("[WATCHER] for '%s' already setup! Bailing.", gitdir)) - return - end - - M.watcher[gitdir] = M.watch_git_dir(gitdir) -end - -- Adapted from https://github.com/lewis6991/gitsigns.nvim/blob/main/lua/gitsigns/manager.lua#L575 --- @param gitdir string --- @return uv_fs_event_t? -function M.watch_git_dir(gitdir) +local function start(gitdir) if not config.values.auto_refresh then return end @@ -33,7 +23,7 @@ function M.watch_git_dir(gitdir) 200, a.void(function() logger.debug("[WATCHER] Dispatching Refresh") - git.repo:dispatch_refresh { callback = status.dispatch_refresh, source = "watcher" } + git.repo:dispatch_refresh { callback = require("neogit.status").dispatch_refresh, source = "watcher" } end) ) @@ -74,4 +64,23 @@ function M.watch_git_dir(gitdir) return w end +function M.stop(gitdir) + local watcher = M.watcher[gitdir] + if watcher then + watcher:stop() + M.watcher[gitdir] = nil + + logger.debug("[WATCHER] Stopped watching git dir: " .. gitdir) + end +end + +function M.setup(gitdir) + if M.watcher[gitdir] then + logger.debug(string.format("[WATCHER] for '%s' already setup", gitdir)) + return + end + + M.watcher[gitdir] = start(gitdir) +end + return M diff --git a/plugin/neogit.lua b/plugin/neogit.lua index d85df47d1..8fab68bb0 100644 --- a/plugin/neogit.lua +++ b/plugin/neogit.lua @@ -13,7 +13,7 @@ end, { }) api.nvim_create_user_command("NeogitRepoState", function() - vim.print(require("neogit.lib.git").repo.state) + vim.print(require("neogit.lib.git").repo) end, { nargs = "*", desc = "Open Neogit Repo State", From 83a559b7ea13f5707dd11037a083757518b96048 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 16:47:20 +0200 Subject: [PATCH 72/95] Clean up after merging master --- lua/neogit.lua | 1 + lua/neogit/autocmds.lua | 26 ----- lua/neogit/lib/git.lua | 26 +---- lua/neogit/lib/git/merge.lua | 1 - lua/neogit/lib/git/pull.lua | 1 - lua/neogit/lib/git/rebase.lua | 1 - lua/neogit/lib/git/repository.lua | 170 +++++++++++++++--------------- lua/neogit/lib/git/status.lua | 2 +- lua/neogit/lib/util.lua | 16 +-- lua/neogit/status.lua | 8 +- 10 files changed, 100 insertions(+), 152 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 849134c7e..fc0ad3a42 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -33,6 +33,7 @@ local setup = function(opts) hl.setup() signs.setup() + require("neogit.autocmds").setup() end ---@param opts OpenOpts diff --git a/lua/neogit/autocmds.lua b/lua/neogit/autocmds.lua index d7e435f06..e326a9954 100644 --- a/lua/neogit/autocmds.lua +++ b/lua/neogit/autocmds.lua @@ -1,9 +1,6 @@ local M = {} local api = vim.api -local a = require("plenary.async") -local status = require("neogit.status") -local fs = require("neogit.lib.fs") local group = require("neogit").autocmd_group function M.setup() @@ -11,29 +8,6 @@ function M.setup() callback = require("neogit.lib.hl").setup, group = group, }) - - api.nvim_create_autocmd({ "BufWritePost", "ShellCmdPost", "VimResume" }, { - callback = function(o) - -- Skip update if the buffer is not open - if not status.status_buffer then - return - end - - -- Do not trigger on neogit buffers such as commit - if api.nvim_buf_get_option(o.buf, "filetype"):find("Neogit") then - return - end - - a.run(function() - local path = fs.relpath_from_repository(o.file) - if not path then - return - end - status.refresh({ status = true, diffs = { "*:" .. path } }, string.format("%s:%s", o.event, o.file)) - end, function() end) - end, - group = group, - }) end return M diff --git a/lua/neogit/lib/git.lua b/lua/neogit/lib/git.lua index 0a4afd395..7f2210538 100644 --- a/lua/neogit/lib/git.lua +++ b/lua/neogit/lib/git.lua @@ -1,4 +1,4 @@ -local git = { +return { branch = require("neogit.lib.git.branch"), cherry_pick = require("neogit.lib.git.cherry_pick"), cli = require("neogit.lib.git.cli"), @@ -15,32 +15,10 @@ local git = { rebase = require("neogit.lib.git.rebase"), reflog = require("neogit.lib.git.reflog"), remote = require("neogit.lib.git.remote"), + repo = require("neogit.lib.git.repository"), reset = require("neogit.lib.git.reset"), revert = require("neogit.lib.git.revert"), sequencer = require("neogit.lib.git.sequencer"), stash = require("neogit.lib.git.stash"), status = require("neogit.lib.git.status"), } - -local repositories = {} - -setmetatable(git, { - __index = function(_, method) - if method == "repo" then - local cwd = require("neogit.status").cwd - if not cwd then - require("neogit.logger").error("[GIT] Cannot construct repository! No CWD") - return - end - - if not repositories[cwd] then - repositories[cwd] = require("neogit.lib.git.repository").new(cwd) - end - - require("neogit.logger").info("[GIT] Found repo for " .. cwd) - return repositories[cwd] - end - end, -}) - -return git diff --git a/lua/neogit/lib/git/merge.lua b/lua/neogit/lib/git/merge.lua index 603c0ac37..88e811892 100644 --- a/lua/neogit/lib/git/merge.lua +++ b/lua/neogit/lib/git/merge.lua @@ -7,7 +7,6 @@ local M = {} local a = require("plenary.async") --- TODO: client.wrap() local function merge_command(cmd) local envs = client.get_envs_git_editor() return cmd.env(envs).show_popup(true):in_pty(true).call(true) diff --git a/lua/neogit/lib/git/pull.lua b/lua/neogit/lib/git/pull.lua index e5cdb9327..04e1689a8 100644 --- a/lua/neogit/lib/git/pull.lua +++ b/lua/neogit/lib/git/pull.lua @@ -3,7 +3,6 @@ local util = require("neogit.lib.util") local M = {} --- TODO: client.wrap() function M.pull_interactive(remote, branch, args) local client = require("neogit.client") local envs = client.get_envs_git_editor() diff --git a/lua/neogit/lib/git/rebase.lua b/lua/neogit/lib/git/rebase.lua index 2390cffc2..3e8cff45d 100644 --- a/lua/neogit/lib/git/rebase.lua +++ b/lua/neogit/lib/git/rebase.lua @@ -6,7 +6,6 @@ local M = {} local a = require("plenary.async") --- TODO: client.wrap() local function rebase_command(cmd) local git = require("neogit.lib.git") cmd = cmd or git.cli.rebase diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 8ed44a2d0..2b6739e92 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -7,7 +7,7 @@ local logger = require("neogit.logger") -- cwd may change after the status is refreshed and used, especially if using -- rooter plugins with lsp integration -- stylua: ignore start -local function empty_state(cwd) +local function empty_state() local root = require("neogit.lib.git.cli").git_root() local Path = require("plenary.path") @@ -21,7 +21,7 @@ local function empty_state(cwd) return index:_stat().mtime.sec end end, - cwd = cwd, + cwd = vim.loop.cwd(), git_root = root, rev_toplevel = nil, invalid = {}, @@ -52,111 +52,111 @@ local function empty_state(cwd) end -- stylua: ignore end -local Repo = {} -Repo.__index = Repo +local meta = { + __index = function(self, method) + return self.state[method] + end, +} -function Repo.new(cwd) - logger.info(string.format("[REPO]: Initializing Repository for %s", cwd)) +local M = {} - local repo = setmetatable( - vim.tbl_extend("error", empty_state(cwd), { - lib = {}, - refresh_lock = a.control.Semaphore.new(1), - lock_holder = nil, - }), - Repo - ) +M.state = empty_state() +M.lib = {} - local modules = { - "status", - "index", - "diff", - "stash", - "pull", - "push", - "log", - "rebase", - "sequencer", - "merge", - } - - for _, m in ipairs(modules) do - require("neogit.lib.git." .. m).register(repo.lib) - end - - return repo -end - -function Repo:reset() - vim.tbl_extend("force", self, empty_state(vim.fn.getcwd())) +function M.reset(self) + self.state = empty_state() end -- Invalidates a cached diff for a file -function Repo:invalidate(...) +function M.invalidate(self, ...) local files = { ... } for _, path in ipairs(files) do - table.insert(self.invalid, string.format("*:%s", path)) + table.insert(self.state.invalid, string.format("*:%s", path)) end end -function Repo:dispatch_refresh(opts) - a.void(function(self, opts) - opts = opts or {} +local refresh_lock = a.control.Semaphore.new(1) +local lock_holder - logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) +M.dispatch_refresh = a.void(function(self, opts) + opts = opts or {} - if self.refresh_lock.permits == 0 then - logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", self.lock_holder)) - return - end + logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) - -- stylua: ignore - if - self.index.timestamp == self.index_stat() and - opts.source == "watcher" - then - logger.debug("[REPO]: Refreshing ABORTED - .git/index hasn't been modified since last refresh") - return - end + if refresh_lock.permits == 0 then + logger.debug(string.format("[REPO]: Refreshing ABORTED - refresh_lock held by %s", lock_holder)) + return + end - if self.git_root == "" then - logger.debug("[REPO]: Refreshing ABORTED - No git_root") - return - end + -- stylua: ignore + if + self.state.index.timestamp == self.state.index_stat() and + opts.source == "watcher" + then + logger.debug("[REPO]: Refreshing ABORTED - .git/index hasn't been modified since last refresh") + return + end + + if self.state.git_root == "" then + logger.debug("[REPO]: Refreshing ABORTED - No git_root") + return + end - self.lock_holder = opts.source or "UNKNOWN" - logger.debug(string.format("[REPO]: Acquiring refresh lock (source: %s)", self.lock_holder)) - local permit = self.refresh_lock:acquire() + lock_holder = opts.source or "UNKNOWN" + logger.debug(string.format("[REPO]: Acquiring refresh lock (source: %s)", lock_holder)) + local permit = refresh_lock:acquire() + + a.util.scheduler() + -- Status needs to run first because other update fn's depend on it + logger.trace("[REPO]: Refreshing %s", "update_status") + self.lib.update_status(self.state) + + local updates = {} + for name, fn in pairs(self.lib) do + if name ~= "update_status" then + table.insert(updates, function() + logger.trace(string.format("[REPO]: Refreshing %s", name)) + fn(self.state) + end) + end + end - a.util.scheduler() + a.util.run_all(updates, function() + self.state.invalidate = {} - -- Status needs to run first because other update fn's depend on it - logger.trace("[REPO]: Refreshing %s", "update_status") - self.lib.update_status(self) + logger.info("[REPO]: Refreshes completed - freeing refresh lock") + permit:forget() + lock_holder = nil - local updates = {} - for name, fn in pairs(self.lib) do - if name ~= "update_status" then - table.insert(updates, function() - logger.trace(string.format("[REPO]: Refreshing %s", name)) - fn(self) - end) - end + if opts.callback then + logger.debug("[REPO]: Running refresh callback") + opts.callback() end + end) +end) - a.util.run_all(updates, function() - self.invalid = {} +if not M.initialized then + logger.info("[REPO]: Initializing Repository") + M.initialized = true - logger.info("[REPO]: Refreshes completed - freeing refresh lock") - permit:forget() - self.lock_holder = nil + setmetatable(M, meta) - if opts.callback then - logger.debug("[REPO]: Running refresh callback") - opts.callback() - end - end) - end)(self, opts) + local modules = { + "status", + "index", + "diff", + "stash", + "pull", + "push", + "log", + "rebase", + "sequencer", + "merge", + } + + for _, m in ipairs(modules) do + require("neogit.lib.git." .. m).register(M.lib) + end end -return Repo +return M diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index 1cb546cc5..92c553a04 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -90,7 +90,7 @@ local function update_status(state) end end - state.cwd = cwd + state.cwd = vim.loop.cwd() state.untracked.items = untracked_files state.unstaged.items = unstaged_files state.staged.items = staged_files diff --git a/lua/neogit/lib/util.lua b/lua/neogit/lib/util.lua index 78fb9ba6b..385d0c3af 100644 --- a/lua/neogit/lib/util.lua +++ b/lua/neogit/lib/util.lua @@ -285,14 +285,6 @@ local function debounce_trailing(ms, fn) end end --- Ensure a string is a minimum width ----@param s string ----@param len integer ----@return string -local function pad_right(s, len) - return s .. string.rep(" ", math.max(len - #s, 0)) -end - ---Removes the given value from the table ---@param tbl table ---@param value any @@ -322,6 +314,14 @@ local function lists_equal(l1, l2) return true end +-- Ensure a string is a minimum width +---@param s string +---@param len integer +---@return string +local function pad_right(s, len) + return s .. string.rep(" ", math.max(len - #s, 0)) +end + return { time = time, time_async = time_async, diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 88db4391a..41b756508 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -556,7 +556,6 @@ end M.dispatch_reset = a.void(M.reset) --- TODO: Close Watcher, repo? function M.close(skip_close) if not skip_close then M.status_buffer:close() @@ -1184,9 +1183,6 @@ function M.create(kind, cwd) name = "NeogitStatus", filetype = "NeogitStatus", kind = kind, - after = function() - -- require("neogit.lib.git").repo:dispatch_refresh { source = "open" } - end, ---@param buffer Buffer initialize = function(buffer) logger.debug("[STATUS BUFFER]: Initializing...") @@ -1202,7 +1198,6 @@ function M.create(kind, cwd) M.cwd = vim.loop.cwd() require("neogit.lib.git").repo:dispatch_refresh { source = "open" } - watcher.setup(Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute()) vim.o.autochdir = false @@ -1229,6 +1224,9 @@ function M.create(kind, cwd) logger.debug("[STATUS BUFFER]: Dispatching initial render") M.refresh() end, + after = function() + watcher.setup(Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute()) + end } end From 17786b972b4fb622f7513b6d3c6d99902a7f1acb Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 17:14:53 +0200 Subject: [PATCH 73/95] Properly async-wrap action functions so we don't need to change the repo update calls to be sync --- lua/neogit/lib/git/log.lua | 3 +-- lua/neogit/lib/git/pull.lua | 2 +- lua/neogit/lib/git/stash.lua | 2 +- lua/neogit/lib/git/status.lua | 8 ++++---- lua/neogit/lib/popup/builder.lua | 19 +++++++++++++------ 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lua/neogit/lib/git/log.lua b/lua/neogit/lib/git/log.lua index 5cf79e762..8984c2373 100644 --- a/lua/neogit/lib/git/log.lua +++ b/lua/neogit/lib/git/log.lua @@ -255,8 +255,7 @@ function M.list(options, show_popup) table.insert(options, "--max-count=256") end - local output = - cli.log.format(format).graph.arg_list(options or {}).show_popup(show_popup).call_sync():trim() + local output = cli.log.format(format).graph.arg_list(options or {}).show_popup(show_popup).call():trim() return parse_log(output.stdout, graph) end diff --git a/lua/neogit/lib/git/pull.lua b/lua/neogit/lib/git/pull.lua index 04e1689a8..36f44fbf0 100644 --- a/lua/neogit/lib/git/pull.lua +++ b/lua/neogit/lib/git/pull.lua @@ -21,7 +21,7 @@ local function update_unpulled(state) local pushRemote = require("neogit.lib.git").branch.pushRemote_ref() if pushRemote then - local result = cli.log.oneline.for_range(".." .. pushRemote).show_popup(false).call_sync():trim().stdout + local result = cli.log.oneline.for_range(".." .. pushRemote).show_popup(false).call():trim().stdout state.pushRemote.unpulled = { items = {} } state.pushRemote.unpulled.items = util.map(result, function(x) diff --git a/lua/neogit/lib/git/stash.lua b/lua/neogit/lib/git/stash.lua index f1c1d520a..fa078884f 100644 --- a/lua/neogit/lib/git/stash.lua +++ b/lua/neogit/lib/git/stash.lua @@ -105,7 +105,7 @@ function M.drop(stash) end function M.list() - return cli.stash.args("list").call_sync():trim().stdout + return cli.stash.args("list").call():trim().stdout end function M.register(meta) diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index 92c553a04..3c7d9c96a 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -19,7 +19,7 @@ local function update_file(file, mode, name) end local function update_status(state) - local result = git.cli.status.porcelain(2).branch.call_sync():trim() + local result = git.cli.status.porcelain(2).branch.call():trim() local untracked_files, unstaged_files, staged_files = {}, {}, {} local old_files_hash = { @@ -98,19 +98,19 @@ end local function update_branch_information(state) if state.head.oid ~= "(initial)" then - local result = git.cli.log.max_count(1).pretty("%B").call_sync():trim() + local result = git.cli.log.max_count(1).pretty("%B").call():trim() state.head.commit_message = result.stdout[1] if state.upstream.ref then local result = - git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call_sync():trim() + git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call():trim() state.upstream.commit_message = result.stdout[1] end local pushRemote = require("neogit.lib.git").branch.pushRemote_ref() if pushRemote then local result = - git.cli.log.max_count(1).pretty("%B").for_range(pushRemote).show_popup(false).call_sync():trim() + git.cli.log.max_count(1).pretty("%B").for_range(pushRemote).show_popup(false).call():trim() state.pushRemote.commit_message = result.stdout[1] end end diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index 4d64fa88b..dde3b3f44 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -272,12 +272,19 @@ function M:action(keys, description, callback) local action if callback then action = a.void(function(...) - callback(...) - - git.repo:dispatch_refresh { - callback = require("neogit.status").dispatch_refresh, - source = "action", - } + local args = { ... } + local cb = function() + callback(unpack(args)) + end + + local refresh = function() + git.repo:dispatch_refresh { + callback = require("neogit.status").dispatch_refresh, + source = "action", + } + end + + a.run(cb, refresh) end) end From 838422e68a052812f3d3273cf0c8e91385985b7c Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 17:15:22 +0200 Subject: [PATCH 74/95] clean --- lua/neogit/lib/git/log.lua | 1 - lua/neogit/popups/branch/actions.lua | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/neogit/lib/git/log.lua b/lua/neogit/lib/git/log.lua index 8984c2373..9bacc03c4 100644 --- a/lua/neogit/lib/git/log.lua +++ b/lua/neogit/lib/git/log.lua @@ -256,7 +256,6 @@ function M.list(options, show_popup) end local output = cli.log.format(format).graph.arg_list(options or {}).show_popup(show_popup).call():trim() - return parse_log(output.stdout, graph) end diff --git a/lua/neogit/popups/branch/actions.lua b/lua/neogit/popups/branch/actions.lua index 084fd19dc..51ee6bb27 100644 --- a/lua/neogit/popups/branch/actions.lua +++ b/lua/neogit/popups/branch/actions.lua @@ -113,7 +113,7 @@ M.rename_branch = operation("rename_branch", function() end new_name, _ = new_name:gsub("%s", "-") - git.cli.branch.move.args(selected_branch, new_name).call_sync():trim() + git.cli.branch.move.args(selected_branch, new_name).call_sync() end) M.reset_branch = operation("reset_branch", function() @@ -162,7 +162,7 @@ M.delete_branch = operation("delete_branch", function() { values = { "&Yes", "&No" }, default = 2 } ) then - git.cli.push.remote(remote).delete.to(branch_name).call_sync():trim() + git.cli.push.remote(remote).delete.to(branch_name).call_sync() notif.create(string.format("Deleted remote branch '%s/%s'", remote, branch_name), vim.log.levels.INFO) elseif branch_name then if git.branch.is_unmerged(branch_name) then From 5137cd019f2f70d9bc19bccb2b502926063b20ef Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 17:52:22 +0200 Subject: [PATCH 75/95] Ensure repo state is built before rendering status buffer --- lua/neogit/status.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 41b756508..6258cc3be 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -1197,8 +1197,6 @@ function M.create(kind, cwd) end M.cwd = vim.loop.cwd() - require("neogit.lib.git").repo:dispatch_refresh { source = "open" } - vim.o.autochdir = false local mappings = buffer.mmanager.mappings @@ -1222,11 +1220,11 @@ function M.create(kind, cwd) set_decoration_provider(buffer) logger.debug("[STATUS BUFFER]: Dispatching initial render") - M.refresh() + require("neogit.lib.git").repo:dispatch_refresh { source = "open", callback = M.dispatch_refresh } end, after = function() watcher.setup(Path.new(require("neogit.lib.git").repo.git_root, ".git"):absolute()) - end + end, } end From 2573b337dfa0321e1df1fc2dfc8063be1becd6f7 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 21:24:48 +0200 Subject: [PATCH 76/95] Cleanup: Don't shield function, and respect config --- lua/neogit/status.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 6258cc3be..a329a9c59 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -465,9 +465,7 @@ function M.refresh() logger.info("[STATUS BUFFER]: Finished refresh") end -M.dispatch_refresh = a.void(function() - M.refresh() -end) +M.dispatch_refresh = a.void(M.refresh) M.refresh_manually = a.void(function(fname) if not fname or fname == "" then @@ -551,7 +549,9 @@ end M.reset = function() git.repo:reset() M.locations = {} - M.refresh() + if config.values.auto_refresh then + M.refresh() + end end M.dispatch_reset = a.void(M.reset) From ddb7e08a70e3b3d271ad9bafe3fa8eb54a754353 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 21:26:19 +0200 Subject: [PATCH 77/95] revert: use fn --- lua/neogit/operations.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/operations.lua b/lua/neogit/operations.lua index d47799a11..9602d3fea 100644 --- a/lua/neogit/operations.lua +++ b/lua/neogit/operations.lua @@ -17,7 +17,7 @@ function M.wait(key, time) if M[key] == nil then return end - vim.wait(time or 1000, function() + vim.fn.wait(time or 1000, function() return M[key] == false end, 100) end From 0e561012cea547b4ca9d77c92fd699d1ebe326a3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 21:27:20 +0200 Subject: [PATCH 78/95] Bugfix: use lib function instead of state --- lua/neogit/popups/branch/actions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/popups/branch/actions.lua b/lua/neogit/popups/branch/actions.lua index 51ee6bb27..534ae97b7 100644 --- a/lua/neogit/popups/branch/actions.lua +++ b/lua/neogit/popups/branch/actions.lua @@ -129,7 +129,7 @@ M.reset_branch = operation("reset_branch", function() local branches = git.branch.get_all_branches(false) local to = FuzzyFinderBuffer.new(branches):open_async { - prompt_prefix = " reset " .. git.repo.head.branch .. " to > ", + prompt_prefix = string.format(" reset %s to > ", git.branch.current()), } if not to then From 418cabf32b45bad0b49b1a4b847677313c1b0170 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 21:28:04 +0200 Subject: [PATCH 79/95] Wrap in operation fn to clean up --- lua/neogit/status.lua | 48 +++++++++++-------------------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index a329a9c59..03c9475c1 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -870,6 +870,12 @@ local set_folds = function(to) M.refresh() end +local operation = function(fn) + return a.void(function() + a.run(fn, M.update) + end) +end + --- These needs to be a function to avoid a circular dependency --- between this module and the popup modules local cmd_func_map = function() @@ -891,42 +897,12 @@ local cmd_func_map = function() set_folds { false, false, false } end), ["Toggle"] = M.toggle, - ["Discard"] = { - "nv", - a.void(function() - discard() - M.update() - end), - true, - }, - ["Stage"] = { - "nv", - a.void(function() - stage() - M.update() - end), - true, - }, - ["StageUnstaged"] = a.void(function() - git.status.stage_modified() - M.update() - end), - ["StageAll"] = a.void(function() - git.status.stage_all() - M.update() - end), - ["Unstage"] = { - "nv", - a.void(function() - unstage() - M.update() - end), - true, - }, - ["UnstageStaged"] = a.void(function() - git.status.unstage_all() - M.update() - end), + ["Discard"] = { "nv", operation(discard), true }, + ["Stage"] = { "nv", operation(stage), true }, + ["StageUnstaged"] = operation(git.status.stage_modified), + ["StageAll"] = operation(git.status.stage_all), + ["Unstage"] = { "nv", operation(unstage), true }, + ["UnstageStaged"] = operation(git.status.unstage_all), ["CommandHistory"] = function() GitCommandHistory:new():show() end, From f8f491da472f42dc71d0a5c181ab5e5456495f30 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 22:14:29 +0200 Subject: [PATCH 80/95] Extract refresh as it's own function --- lua/neogit/lib/git/repository.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 2b6739e92..02b09cc77 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -78,7 +78,7 @@ end local refresh_lock = a.control.Semaphore.new(1) local lock_holder -M.dispatch_refresh = a.void(function(self, opts) +local function refresh(self, opts) opts = opts or {} logger.info(string.format("[REPO]: Refreshing START (source: %s)", opts.source or "UNKNOWN")) @@ -133,7 +133,9 @@ M.dispatch_refresh = a.void(function(self, opts) opts.callback() end end) -end) +end + +M.dispatch_refresh = a.void(refresh) if not M.initialized then logger.info("[REPO]: Initializing Repository") From d5ec1ab984e8b9383d3f25eab6842a7cb67b8d1a Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:06:20 +0200 Subject: [PATCH 81/95] Fix missed string formatting --- lua/neogit/lib/git/repository.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 02b09cc77..24d1855b6 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -108,7 +108,7 @@ local function refresh(self, opts) a.util.scheduler() -- Status needs to run first because other update fn's depend on it - logger.trace("[REPO]: Refreshing %s", "update_status") + logger.trace("[REPO]: Refreshing update_status") self.lib.update_status(self.state) local updates = {} From 2185056261f22785b1ac5cdb0490167bc13145e8 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:07:49 +0200 Subject: [PATCH 82/95] Rewrite how status.current_operation works - all actions that need to be tracked are wrapped in operation() function, which sets value. The value is only cleared after the status buffer finishes redrawing. This is a little hacky, and everything works _fine_ without it, but it's for the test suite. Async stuff is tough to test. --- lua/neogit/status.lua | 49 +++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/lua/neogit/status.lua b/lua/neogit/status.lua index 03c9475c1..d79d1d2d4 100644 --- a/lua/neogit/status.lua +++ b/lua/neogit/status.lua @@ -462,6 +462,7 @@ function M.refresh() restore_cursor_location(s, f, h) end + M.current_operation = nil logger.info("[STATUS BUFFER]: Finished refresh") end @@ -546,15 +547,14 @@ function M.toggle() refresh_status_buffer() end -M.reset = function() - git.repo:reset() +function M.reset() + M.current_operation = "reset" M.locations = {} - if config.values.auto_refresh then - M.refresh() - end -end -M.dispatch_reset = a.void(M.reset) + git.repo:reset() + git.repo:dispatch_refresh { source = "reset", callback = M.dispatch_refresh } + M.wait_on_current_operation() +end function M.close(skip_close) if not skip_close then @@ -673,7 +673,6 @@ local unstage_selection = function() end local stage = function() - M.current_operation = "stage" local section, item = M.get_current_section_item() local mode = vim.api.nvim_get_mode() @@ -697,7 +696,7 @@ local stage = function() return item.name end)) end - M.current_operation = nil + return else if on_hunk and section.name ~= "untracked" then @@ -710,7 +709,6 @@ local stage = function() end assert(item, "Stage item is nil") - M.current_operation = nil end local unstage = function() @@ -720,14 +718,12 @@ local unstage = function() if section == nil or section.name ~= "staged" or (mode.mode == "V" and item == nil) then return end - M.current_operation = "unstage" if mode.mode == "V" then unstage_selection() else if item == nil then git.status.unstage_all() - M.current_operation = nil return else local on_hunk = current_line_is_hunk() @@ -745,7 +741,6 @@ local unstage = function() end assert(item, "Unstage item is nil") - M.current_operation = nil end local function discard_message(item, mode) @@ -815,8 +810,6 @@ local discard = function() return end - M.current_operation = "discard" - local mode = vim.api.nvim_get_mode() -- These all need to be captured _before_ the get_confirmation() call, since that @@ -849,8 +842,6 @@ local discard = function() discard_selected_files({ item }, section.name) end - M.current_operation = nil - a.util.scheduler() vim.cmd("checktime") end @@ -870,10 +861,13 @@ local set_folds = function(to) M.refresh() end -local operation = function(fn) - return a.void(function() - a.run(fn, M.update) - end) +local operation = function(fn, name) + return function() + M.current_operation = name + a.void(function() + a.run(fn, M.update) + end)() + end end --- These needs to be a function to avoid a circular dependency @@ -897,12 +891,12 @@ local cmd_func_map = function() set_folds { false, false, false } end), ["Toggle"] = M.toggle, - ["Discard"] = { "nv", operation(discard), true }, - ["Stage"] = { "nv", operation(stage), true }, - ["StageUnstaged"] = operation(git.status.stage_modified), - ["StageAll"] = operation(git.status.stage_all), - ["Unstage"] = { "nv", operation(unstage), true }, - ["UnstageStaged"] = operation(git.status.unstage_all), + ["Discard"] = { "nv", operation(discard, "discard"), true }, + ["Stage"] = { "nv", operation(stage, "stage"), true }, + ["Unstage"] = { "nv", operation(unstage, "unstage"), true }, + ["StageUnstaged"] = operation(git.status.stage_modified, "stage_unstaged"), + ["StageAll"] = operation(git.status.stage_all, "stage_all"), + ["UnstageStaged"] = operation(git.status.unstage_all, "unstage_staged"), ["CommandHistory"] = function() GitCommandHistory:new():show() end, @@ -1218,6 +1212,7 @@ end function M.wait_on_current_operation(ms) vim.wait(ms or 1000, function() + logger.trace("[STATUS] Waiting on current_operation: " .. (M.current_operation or "none")) return not M.current_operation end) end From a3b4bbc2114a0471f231d900f062551de4fbdf74 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:09:15 +0200 Subject: [PATCH 83/95] Disable watcher for testing --- lua/neogit/watcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neogit/watcher.lua b/lua/neogit/watcher.lua index 978b2cdfc..646a84263 100644 --- a/lua/neogit/watcher.lua +++ b/lua/neogit/watcher.lua @@ -15,7 +15,7 @@ local a = require("plenary.async") --- @param gitdir string --- @return uv_fs_event_t? local function start(gitdir) - if not config.values.auto_refresh then + if not config.values.auto_refresh or vim.env.NVIM_APPNAME == "neogit-test" then return end From 113884cef13ac3da33bc71e905dec8569ffc3bbc Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:09:38 +0200 Subject: [PATCH 84/95] Don't block on this function - it's now self-blocking, in that it will not return until the current operation (reset) is done --- tests/specs/neogit/lib/git/branch_spec.lua | 2 +- tests/util/git_harness.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/specs/neogit/lib/git/branch_spec.lua b/tests/specs/neogit/lib/git/branch_spec.lua index 33bfb3ddc..a140f3a15 100644 --- a/tests/specs/neogit/lib/git/branch_spec.lua +++ b/tests/specs/neogit/lib/git/branch_spec.lua @@ -31,7 +31,7 @@ describe("git branch lib", function() before_each(function() git_harness.prepare_repository() - plenary_async.util.block_on(status.reset) + status.reset() setup_local_git_branches() require("neogit").setup() end) diff --git a/tests/util/git_harness.lua b/tests/util/git_harness.lua index c63c329ab..3341c0031 100644 --- a/tests/util/git_harness.lua +++ b/tests/util/git_harness.lua @@ -54,7 +54,7 @@ function M.in_prepared_repo(cb) local dir = M.prepare_repository() require("neogit").setup() vim.cmd("Neogit") - a.util.block_on(status.reset) + status.reset() local _, err = pcall(cb, dir) if err ~= nil then error(err) From fa79f49f8447fc40bab97c77864d7434b216b5ce Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:13:02 +0200 Subject: [PATCH 85/95] Cleanup --- lua/neogit/lib/git/repository.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 24d1855b6..ac64a0276 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -23,7 +23,6 @@ local function empty_state() end, cwd = vim.loop.cwd(), git_root = root, - rev_toplevel = nil, invalid = {}, index = { timestamp = 0 }, head = { branch = nil, commit_message = "" }, From 7dca40ae9d3ec0b122c14125a41b81aa9b6d2e92 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:22:23 +0200 Subject: [PATCH 86/95] Ignore direnv config --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index fed0c5a95..b4570ab95 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ tmp/ # Generated files doc/tags + +# direnv config +.envrc From f9bc1200113bb4129d2b9427c0b525588ad25286 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:24:26 +0200 Subject: [PATCH 87/95] Patch up diffview integration --- lua/neogit/integrations/diffview.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/neogit/integrations/diffview.lua b/lua/neogit/integrations/diffview.lua index b97cacb82..03120c401 100644 --- a/lua/neogit/integrations/diffview.lua +++ b/lua/neogit/integrations/diffview.lua @@ -131,6 +131,7 @@ local function get_local_diff_view(selected_file_name) } view:on_files_staged(a.void(function(_) + require("neogit.status").update() view:update_files() end)) From f73af2f770ec41a0e1924129bb09f9f27d0b24c2 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:43:09 +0200 Subject: [PATCH 88/95] Notes --- lua/neogit/lib/git/index.lua | 3 +++ lua/neogit/lib/git/status.lua | 33 ++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lua/neogit/lib/git/index.lua b/lua/neogit/lib/git/index.lua index 43302877e..550429d47 100644 --- a/lua/neogit/lib/git/index.lua +++ b/lua/neogit/lib/git/index.lua @@ -111,6 +111,9 @@ end function M.register(meta) meta.update_index = function(state) + -- This exists to prevent double-refreshing the repo, when triggered by both the action callback and filewatcher + -- callback. We are assuming that if the `.git/index` file has not been modified, then there's no need to update the + -- status buffer/repo state. state.index.timestamp = state.index_stat() end end diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index 3c7d9c96a..8a318a2ec 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -3,6 +3,7 @@ local git = { stash = require("neogit.lib.git.stash"), } local Collection = require("neogit.lib.collection") +local a = require("plenary.async") local function update_file(file, mode, name) local mt, diff, has_diff @@ -19,6 +20,12 @@ local function update_file(file, mode, name) end local function update_status(state) + -- git-status outputs files relative to the cwd. + -- + -- Save the working directory to allow resolution to absolute paths since the + -- cwd may change after the status is refreshed and used, especially if using + -- rooter plugins with lsp integration + local cwd = vim.loop.cwd() local result = git.cli.status.porcelain(2).branch.call():trim() local untracked_files, unstaged_files, staged_files = {}, {}, {} @@ -90,28 +97,36 @@ local function update_status(state) end end - state.cwd = vim.loop.cwd() + state.cwd = cwd + state.untracked.items = untracked_files state.unstaged.items = unstaged_files state.staged.items = staged_files end local function update_branch_information(state) + local tasks = {} + if state.head.oid ~= "(initial)" then - local result = git.cli.log.max_count(1).pretty("%B").call():trim() - state.head.commit_message = result.stdout[1] + table.insert(tasks, function() + state.head.commit_message = git.cli.log.max_count(1).pretty("%B").call():trim().stdout[1] + end) if state.upstream.ref then - local result = - git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call():trim() - state.upstream.commit_message = result.stdout[1] + table.insert(tasks, function() + state.upstream.commit_message = git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call():trim().stdout[1] + end) end local pushRemote = require("neogit.lib.git").branch.pushRemote_ref() if pushRemote then - local result = - git.cli.log.max_count(1).pretty("%B").for_range(pushRemote).show_popup(false).call():trim() - state.pushRemote.commit_message = result.stdout[1] + table.insert(tasks, function() + state.pushRemote.commit_message = git.cli.log.max_count(1).pretty("%B").for_range(pushRemote).show_popup(false).call():trim().stdout[1] + end) + end + + if #tasks > 0 then + a.util.join(tasks) end end end From b4666a39f8c1fcab88d5aa7ac5077d64209c0e82 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 1 Aug 2023 23:51:28 +0200 Subject: [PATCH 89/95] Move invalidate() to diff lib since it only deals with diffs --- lua/neogit/lib/git/diff.lua | 64 ++++++++++++++++++------------- lua/neogit/lib/git/repository.lua | 10 ----- lua/neogit/lib/git/status.lua | 7 ++-- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/lua/neogit/lib/git/diff.lua b/lua/neogit/lib/git/diff.lua index d29fc656f..68dbc6984 100644 --- a/lua/neogit/lib/git/diff.lua +++ b/lua/neogit/lib/git/diff.lua @@ -8,6 +8,8 @@ local ItemFilter = require("neogit.lib.item_filter") local insert = table.insert local sha256 = vim.fn.sha256 +local M = {} + local function parse_diff_stats(raw) if type(raw) == "string" then raw = vim.split(raw, ", ") @@ -155,7 +157,7 @@ local function build_hunks(lines) return hunks end -local function parse_diff(raw_diff, raw_stats) +function M.parse(raw_diff, raw_stats) local header, start_idx = build_diff_header(raw_diff) local lines = build_lines(raw_diff, start_idx) local hunks = build_hunks(lines) @@ -179,7 +181,7 @@ local function build_metatable(f, raw_output_fn) if method == "diff" then self.diff = a.util.block_on(function() logger.debug("[DIFF] Loading diff for: " .. f.name) - return parse_diff(raw_output_fn()) + return M.parse(raw_output_fn()) end) return self.diff @@ -230,33 +232,43 @@ local function invalidate_diff(filter, section, item) end end -return { - parse = parse_diff, - register = function(meta) - meta.update_diffs = function(repo) - local filter - if repo.invalid[1] then - filter = ItemFilter.create(repo.invalid) - end +M.invalid = {} - for _, f in ipairs(repo.untracked.items) do - invalidate_diff(filter, "untracked", f) - build_metatable(f, raw_untracked(f.name)) - end +-- Invalidates a cached diff for a file +function M.invalidate(...) + local files = { ... } + for _, path in ipairs(files) do + table.insert(M.invalid, string.format("*:%s", path)) + end +end - for _, f in ipairs(repo.unstaged.items) do - if f.mode ~= "F" then - invalidate_diff(filter, "unstaged", f) - build_metatable(f, raw_unstaged(f.name)) - end +function M.register(meta) + meta.update_diffs = function(repo) + local filter + if #M.invalid > 0 then + filter = ItemFilter.create(M.invalid) + M.invalid = {} + end + + for _, f in ipairs(repo.untracked.items) do + invalidate_diff(filter, "untracked", f) + build_metatable(f, raw_untracked(f.name)) + end + + for _, f in ipairs(repo.unstaged.items) do + if f.mode ~= "F" then + invalidate_diff(filter, "unstaged", f) + build_metatable(f, raw_unstaged(f.name)) end + end - for _, f in ipairs(repo.staged.items) do - if f.mode ~= "F" then - invalidate_diff(filter, "staged", f) - build_metatable(f, raw_staged(f.name)) - end + for _, f in ipairs(repo.staged.items) do + if f.mode ~= "F" then + invalidate_diff(filter, "staged", f) + build_metatable(f, raw_staged(f.name)) end end - end, -} + end +end + +return M diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index ac64a0276..f8b7751bc 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -66,14 +66,6 @@ function M.reset(self) self.state = empty_state() end --- Invalidates a cached diff for a file -function M.invalidate(self, ...) - local files = { ... } - for _, path in ipairs(files) do - table.insert(self.state.invalid, string.format("*:%s", path)) - end -end - local refresh_lock = a.control.Semaphore.new(1) local lock_holder @@ -121,8 +113,6 @@ local function refresh(self, opts) end a.util.run_all(updates, function() - self.state.invalidate = {} - logger.info("[REPO]: Refreshes completed - freeing refresh lock") permit:forget() lock_holder = nil diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index 8a318a2ec..1e7c2595f 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -1,9 +1,10 @@ local git = { cli = require("neogit.lib.git.cli"), + diff = require("neogit.lib.git.diff"), stash = require("neogit.lib.git.stash"), } -local Collection = require("neogit.lib.collection") local a = require("plenary.async") +local Collection = require("neogit.lib.collection") local function update_file(file, mode, name) local mt, diff, has_diff @@ -134,7 +135,7 @@ end local M = {} function M.stage(...) - require("neogit.lib.git").repo:invalidate(...) + git.diff.invalidate(...) git.cli.add.files(...).call() end @@ -147,7 +148,7 @@ function M.stage_all() end function M.unstage(...) - require("neogit.lib.git").repo:invalidate(...) + git.diff.invalidate(...) git.cli.reset.files(...).call() end From 3d70cf81db148c3b393df3ddd25b8a7603869626 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 2 Aug 2023 00:08:03 +0200 Subject: [PATCH 90/95] Remove mechanism for preventing watcher from double-triggering refresh. Just do the refresh, otherwise we could miss stuff like FETCH_HEAD being updated, but index not --- lua/neogit/lib/git/index.lua | 9 --------- lua/neogit/lib/git/repository.lua | 17 ----------------- 2 files changed, 26 deletions(-) diff --git a/lua/neogit/lib/git/index.lua b/lua/neogit/lib/git/index.lua index 550429d47..3dcc42c20 100644 --- a/lua/neogit/lib/git/index.lua +++ b/lua/neogit/lib/git/index.lua @@ -109,13 +109,4 @@ function M.update() :spawn_async() end -function M.register(meta) - meta.update_index = function(state) - -- This exists to prevent double-refreshing the repo, when triggered by both the action callback and filewatcher - -- callback. We are assuming that if the `.git/index` file has not been modified, then there's no need to update the - -- status buffer/repo state. - state.index.timestamp = state.index_stat() - end -end - return M diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index f8b7751bc..8c70d8a7c 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -15,16 +15,9 @@ local function empty_state() git_path = function(path) return Path.new(root):joinpath(".git", path) end, - index_stat = function() - local index = Path.new(root):joinpath(".git", "index") - if index:exists() then - return index:_stat().mtime.sec - end - end, cwd = vim.loop.cwd(), git_root = root, invalid = {}, - index = { timestamp = 0 }, head = { branch = nil, commit_message = "" }, upstream = { branch = nil, @@ -79,15 +72,6 @@ local function refresh(self, opts) return end - -- stylua: ignore - if - self.state.index.timestamp == self.state.index_stat() and - opts.source == "watcher" - then - logger.debug("[REPO]: Refreshing ABORTED - .git/index hasn't been modified since last refresh") - return - end - if self.state.git_root == "" then logger.debug("[REPO]: Refreshing ABORTED - No git_root") return @@ -134,7 +118,6 @@ if not M.initialized then local modules = { "status", - "index", "diff", "stash", "pull", From 99464ba46d0e5671eecdd41cb788dfe7b0a87278 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 2 Aug 2023 00:10:45 +0200 Subject: [PATCH 91/95] No need to double wrap this in async functions. --- lua/neogit/lib/popup/builder.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index dde3b3f44..07731b2e1 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -269,9 +269,9 @@ function M:action(keys, description, callback) self.state.keys[key] = true end - local action + local action_fn if callback then - action = a.void(function(...) + action_fn = function(...) local args = { ... } local cb = function() callback(unpack(args)) @@ -285,13 +285,13 @@ function M:action(keys, description, callback) end a.run(cb, refresh) - end) + end end table.insert(self.state.actions[#self.state.actions], { keys = keys, description = description, - callback = action, + callback = action_fn, }) return self From 43eed6f3a2f6227829ec38b135d7ee5f105d150f Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 2 Aug 2023 00:11:41 +0200 Subject: [PATCH 92/95] Format --- lua/neogit/lib/git/status.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lua/neogit/lib/git/status.lua b/lua/neogit/lib/git/status.lua index 1e7c2595f..1a1500534 100644 --- a/lua/neogit/lib/git/status.lua +++ b/lua/neogit/lib/git/status.lua @@ -115,14 +115,26 @@ local function update_branch_information(state) if state.upstream.ref then table.insert(tasks, function() - state.upstream.commit_message = git.cli.log.max_count(1).pretty("%B").for_range("@{upstream}").show_popup(false).call():trim().stdout[1] + state.upstream.commit_message = git.cli.log + .max_count(1) + .pretty("%B") + .for_range("@{upstream}") + .show_popup(false) + .call() + :trim().stdout[1] end) end local pushRemote = require("neogit.lib.git").branch.pushRemote_ref() if pushRemote then table.insert(tasks, function() - state.pushRemote.commit_message = git.cli.log.max_count(1).pretty("%B").for_range(pushRemote).show_popup(false).call():trim().stdout[1] + state.pushRemote.commit_message = git.cli.log + .max_count(1) + .pretty("%B") + .for_range(pushRemote) + .show_popup(false) + .call() + :trim().stdout[1] end) end From 9b5ad5605b5c036ca02aedf187289c44bbba97c1 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 2 Aug 2023 13:19:51 +0200 Subject: [PATCH 93/95] Cleanup --- lua/neogit/lib/git/repository.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index 8c70d8a7c..549a279e0 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -12,12 +12,11 @@ local function empty_state() local Path = require("plenary.path") return { - git_path = function(path) - return Path.new(root):joinpath(".git", path) + git_path = function(...) + return Path.new(root):joinpath(".git", ...) end, cwd = vim.loop.cwd(), git_root = root, - invalid = {}, head = { branch = nil, commit_message = "" }, upstream = { branch = nil, From 22af74d360250fd56e1d7bf0ded554143c09c61f Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 2 Aug 2023 14:15:56 +0200 Subject: [PATCH 94/95] It's impossible for this to return anything because it's async.void wrapped. So.. Don't check the return. Also, nothing uses this. --- lua/neogit/lib/popup.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lua/neogit/lib/popup.lua b/lua/neogit/lib/popup.lua index 6b4abef94..9a2c2f42a 100644 --- a/lua/neogit/lib/popup.lua +++ b/lua/neogit/lib/popup.lua @@ -512,11 +512,8 @@ function M:show() for _, key in ipairs(action.keys) do mappings.n[key] = function() logger.debug(string.format("[POPUP]: Invoking action '%s' of %s", key, self.state.name)) - local ret = action.callback(self) + action.callback(self) self:close() - if type(ret) == "function" then - ret() - end end end else From e655c558765bc61a224855c41c7f261e4d80d9a1 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 2 Aug 2023 14:19:36 +0200 Subject: [PATCH 95/95] Pass popup instead of varargs --- lua/neogit/lib/popup/builder.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lua/neogit/lib/popup/builder.lua b/lua/neogit/lib/popup/builder.lua index 07731b2e1..bbd0fa0bf 100644 --- a/lua/neogit/lib/popup/builder.lua +++ b/lua/neogit/lib/popup/builder.lua @@ -271,10 +271,9 @@ function M:action(keys, description, callback) local action_fn if callback then - action_fn = function(...) - local args = { ... } + action_fn = a.void(function(popup) local cb = function() - callback(unpack(args)) + callback(popup) end local refresh = function() @@ -285,7 +284,7 @@ function M:action(keys, description, callback) end a.run(cb, refresh) - end + end) end table.insert(self.state.actions[#self.state.actions], {