From 838b2d58893ce423e1ec6cc637752c4c37a2f69c Mon Sep 17 00:00:00 2001 From: Chris Roscher Date: Sun, 8 Jun 2025 21:09:10 +0200 Subject: [PATCH 1/3] feat: Add auto-fetch feature --- lua/neogit.lua | 29 +++++++++++++++++++++++++++++ lua/neogit/config.lua | 9 +++++++++ lua/neogit/lib/git/fetch.lua | 25 +++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 7f66dcce0..2d77aedb9 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -73,6 +73,35 @@ function M.setup(opts) signs.setup(config.values) state.setup(config.values) autocmds.setup() + + if config.values.auto_fetch_enabled then + local fetch = require("neogit.lib.git.fetch") + local git = require("neogit.lib.git") + + if M.auto_fetch_timer then + M.auto_fetch_timer:close() + end + + M.auto_fetch_timer = vim.uv.new_timer() + M.auto_fetch_timer:start( + config.values.auto_fetch_interval, + config.values.auto_fetch_interval, + vim.schedule_wrap(function() + if git.cli.is_inside_worktree(vim.uv.cwd()) then + fetch.fetch("--all") + end + end) + ) + + if config.values.auto_fetch_on_startup then + if git.cli.is_inside_worktree(vim.uv.cwd()) then + fetch.fetch("--all") + end + end + elseif M.auto_fetch_timer then + M.auto_fetch_timer:close() + M.auto_fetch_timer = nil + end end local function construct_opts(opts) diff --git a/lua/neogit/config.lua b/lua/neogit/config.lua index fd979e712..44aab80f0 100644 --- a/lua/neogit/config.lua +++ b/lua/neogit/config.lua @@ -332,6 +332,9 @@ end ---@field disable_signs? boolean Special signs to draw for sections etc. in Neogit ---@field prompt_force_push? boolean Offer to force push when branches diverge ---@field git_services? table Templartes to use when opening a pull request for a branch +---@field auto_fetch_enabled? boolean Enable/disable automatic fetching +---@field auto_fetch_interval? integer The interval (in milliseconds) for automatic fetching +---@field auto_fetch_on_startup? boolean Perform an initial fetch when Neogit starts ---@field fetch_after_checkout? boolean Perform a fetch if the newly checked out branch has an upstream or pushRemote set ---@field telescope_sorter? function The sorter telescope will use ---@field process_spinner? boolean Hide/Show the process spinner @@ -399,6 +402,9 @@ function M.get_default_values() disable_insert_on_commit = "auto", use_per_project_settings = true, remember_settings = true, + auto_fetch_enabled = false, + auto_fetch_interval = 300000, -- 5 minutes in milliseconds + auto_fetch_on_startup = false, fetch_after_checkout = false, sort_branches = "-committerdate", kind = "tab", @@ -1132,6 +1138,9 @@ function M.validate_config() end if validate_type(config, "base config", "table") then + validate_type(config.auto_fetch_enabled, "auto_fetch_enabled", "boolean") + validate_type(config.auto_fetch_interval, "auto_fetch_interval", "number") + validate_type(config.auto_fetch_on_startup, "auto_fetch_on_startup", "boolean") validate_type(config.disable_hint, "disable_hint", "boolean") validate_type(config.disable_context_highlighting, "disable_context_highlighting", "boolean") validate_type(config.disable_signs, "disable_signs", "boolean") diff --git a/lua/neogit/lib/git/fetch.lua b/lua/neogit/lib/git/fetch.lua index 332c1333b..dd2ccc237 100644 --- a/lua/neogit/lib/git/fetch.lua +++ b/lua/neogit/lib/git/fetch.lua @@ -12,11 +12,32 @@ function M.fetch_interactive(remote, branch, args) return git.cli.fetch.args(remote or "", branch or "").arg_list(args).call { pty = true } end +local a = require("plenary.async") +local notification = require("neogit.lib.notification") + ---@param remote string ---@param branch string ----@return ProcessResult function M.fetch(remote, branch) - return git.cli.fetch.args(remote, branch).call { ignore_error = true } + notification.info("Fetching...") + a.void(function() + local result = git.cli.fetch.args(remote, branch).call { ignore_error = true } + + if result and result.code == 0 then + notification.info("Fetch complete.") + elseif result then + local error_message = "Fetch failed: " + if type(result.stderr) == "table" then + error_message = error_message .. table.concat(result.stderr, "\n") + elseif type(result.stderr) == "string" then + error_message = error_message .. result.stderr + else + error_message = error_message .. "Unknown error format." + end + notification.error(error_message) + else + notification.error("Fetch failed: An unexpected error occurred and no result was returned.") + end + end)() end return M From a0b8a0530eba7050cd1f7ddae38414d4cfd09ff8 Mon Sep 17 00:00:00 2001 From: Chris Roscher Date: Mon, 9 Jun 2025 22:49:31 +0200 Subject: [PATCH 2/3] chore: use git require for fetch --- lua/neogit.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 2d77aedb9..5d8f853ac 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -75,7 +75,6 @@ function M.setup(opts) autocmds.setup() if config.values.auto_fetch_enabled then - local fetch = require("neogit.lib.git.fetch") local git = require("neogit.lib.git") if M.auto_fetch_timer then @@ -88,14 +87,14 @@ function M.setup(opts) config.values.auto_fetch_interval, vim.schedule_wrap(function() if git.cli.is_inside_worktree(vim.uv.cwd()) then - fetch.fetch("--all") + git.fetch.fetch("--all") end end) ) if config.values.auto_fetch_on_startup then if git.cli.is_inside_worktree(vim.uv.cwd()) then - fetch.fetch("--all") + git.fetch.fetch("--all") end end elseif M.auto_fetch_timer then From c6f7d439779716c00a864f2dbefb6f35dbab5e2e Mon Sep 17 00:00:00 2001 From: Chris Roscher Date: Sun, 15 Jun 2025 01:31:33 +0200 Subject: [PATCH 3/3] Move auto fetch to NeogitRepo instance --- lua/neogit.lua | 28 ----------- lua/neogit/autocmds.lua | 15 ++++++ lua/neogit/lib/git/fetch.lua | 32 +++--------- lua/neogit/lib/git/repository.lua | 82 +++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 52 deletions(-) diff --git a/lua/neogit.lua b/lua/neogit.lua index 5d8f853ac..7f66dcce0 100644 --- a/lua/neogit.lua +++ b/lua/neogit.lua @@ -73,34 +73,6 @@ function M.setup(opts) signs.setup(config.values) state.setup(config.values) autocmds.setup() - - if config.values.auto_fetch_enabled then - local git = require("neogit.lib.git") - - if M.auto_fetch_timer then - M.auto_fetch_timer:close() - end - - M.auto_fetch_timer = vim.uv.new_timer() - M.auto_fetch_timer:start( - config.values.auto_fetch_interval, - config.values.auto_fetch_interval, - vim.schedule_wrap(function() - if git.cli.is_inside_worktree(vim.uv.cwd()) then - git.fetch.fetch("--all") - end - end) - ) - - if config.values.auto_fetch_on_startup then - if git.cli.is_inside_worktree(vim.uv.cwd()) then - git.fetch.fetch("--all") - end - end - elseif M.auto_fetch_timer then - M.auto_fetch_timer:close() - M.auto_fetch_timer = nil - end end local function construct_opts(opts) diff --git a/lua/neogit/autocmds.lua b/lua/neogit/autocmds.lua index edcd7926c..e821a9a96 100644 --- a/lua/neogit/autocmds.lua +++ b/lua/neogit/autocmds.lua @@ -7,6 +7,21 @@ function M.setup() local status_buffer = require("neogit.buffers.status") local git = require("neogit.lib.git") local group = require("neogit").autocmd_group + local config = require("neogit.config") + + if config.values.auto_fetch_enabled and config.values.auto_fetch_on_startup then + api.nvim_create_autocmd("VimEnter", { + callback = function() + if git.cli.is_inside_worktree(".") then + local repo = require("neogit.lib.git.repository").instance(".") + vim.schedule(function() + repo:_fetch() + end) + end + end, + group = group, + }) + end api.nvim_create_autocmd({ "ColorScheme" }, { callback = function() diff --git a/lua/neogit/lib/git/fetch.lua b/lua/neogit/lib/git/fetch.lua index dd2ccc237..f4240cab4 100644 --- a/lua/neogit/lib/git/fetch.lua +++ b/lua/neogit/lib/git/fetch.lua @@ -12,32 +12,16 @@ function M.fetch_interactive(remote, branch, args) return git.cli.fetch.args(remote or "", branch or "").arg_list(args).call { pty = true } end -local a = require("plenary.async") -local notification = require("neogit.lib.notification") - ----@param remote string ----@param branch string +---@param remote string | nil +---@param branch string | nil function M.fetch(remote, branch) - notification.info("Fetching...") - a.void(function() - local result = git.cli.fetch.args(remote, branch).call { ignore_error = true } + local result = git.cli.fetch.args(remote, branch).call { ignore_error = true } - if result and result.code == 0 then - notification.info("Fetch complete.") - elseif result then - local error_message = "Fetch failed: " - if type(result.stderr) == "table" then - error_message = error_message .. table.concat(result.stderr, "\n") - elseif type(result.stderr) == "string" then - error_message = error_message .. result.stderr - else - error_message = error_message .. "Unknown error format." - end - notification.error(error_message) - else - notification.error("Fetch failed: An unexpected error occurred and no result was returned.") - end - end)() + if result and result.code == 0 then + return true, result + else + return false, result + end end return M diff --git a/lua/neogit/lib/git/repository.lua b/lua/neogit/lib/git/repository.lua index a8e55fcc4..67b6fd1ee 100644 --- a/lua/neogit/lib/git/repository.lua +++ b/lua/neogit/lib/git/repository.lua @@ -4,6 +4,8 @@ local Path = require("plenary.path") local git = require("neogit.lib.git") local ItemFilter = require("neogit.lib.item_filter") local util = require("neogit.lib.util") +local notification = require("neogit.lib.notification") +local event = require("neogit.lib.event") local modules = { "status", @@ -169,6 +171,10 @@ local function empty_state() } end +---@class NeogitRepoAutoFetch +---@field timer uv_timer_t +---@field interval integer + ---@class NeogitRepo ---@field lib table ---@field state NeogitRepoState @@ -179,6 +185,7 @@ end ---@field interrupt table ---@field tmp_state table ---@field refresh_callbacks function[] +---@field auto_fetch NeogitRepoAutoFetch|nil local Repo = {} Repo.__index = Repo @@ -219,6 +226,7 @@ function Repo.new(dir) running = util.weak_table(), interrupt = util.weak_table(), tmp_state = util.weak_table("v"), + auto_fetch = nil, } instance.state.worktree_root = instance.worktree_root @@ -231,6 +239,10 @@ function Repo.new(dir) require("neogit.lib.git." .. m).register(instance.lib) end + if instance.worktree_root ~= "" then + instance:_setup_auto_fetch() + end + return instance end @@ -344,4 +356,74 @@ Repo.dispatch_refresh = a.void(function(self, opts) self:refresh(opts) end) +function Repo:_setup_auto_fetch() + local config = require("neogit.config") + + if config.values.auto_fetch_enabled then + logger.debug("[REPO]: Setting up auto-fetch for " .. self.worktree_root) + + self.auto_fetch = { + timer = vim.uv.new_timer(), + interval = config.values.auto_fetch_interval, + } + + self:_start_auto_fetch() + end +end + +function Repo:_start_auto_fetch() + if not self.auto_fetch then + return + end + + logger.debug("[REPO]: Starting auto-fetch timer") + + self.auto_fetch.timer:start( + self.auto_fetch.interval, + self.auto_fetch.interval, + vim.schedule_wrap(function() + self:_fetch() + end) + ) +end + +function Repo:_fetch() + notification.info("Auto-fetching...") + a.void(function() + local success, result = git.fetch.fetch("--all") + + if success then + notification.info("Auto-fetch complete.") + else + local error_message = "Auto-fetch failed: " + if result then + if result.stderr and #result.stderr > 0 then + if type(result.stderr) == "table" then + error_message = error_message .. table.concat(result.stderr, "\n") + elseif type(result.stderr) == "string" then + error_message = error_message .. result.stderr + else + error_message = error_message .. "Unknown stderr format." + end + elseif result.stdout and #result.stdout > 0 then + if type(result.stdout) == "table" then + error_message = error_message .. table.concat(result.stdout, "\n") + elseif type(result.stdout) == "string" then + error_message = error_message .. result.stdout + else + error_message = error_message .. "Unknown stdout format." + end + else + error_message = error_message + .. string.format("Command failed with exit code %d", result.code or -1) + end + else + error_message = error_message .. "No result returned from git command." + end + notification.error(error_message) + end + event.send("FetchComplete") + end)() +end + return Repo