Skip to content

Commit 7a61d31

Browse files
authored
Merge pull request #428 from cameronr/watch-git-branch-changes
feat(git): branch tracking with auto-save/restore
2 parents 9725cdd + 4a5fa3b commit 7a61d31

File tree

8 files changed

+273
-50
lines changed

8 files changed

+273
-50
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ Here are the default settings:
6464
suppressed_dirs = nil, -- Suppress session restore/create in certain directories
6565
allowed_dirs = nil, -- Allow session restore/create in certain directories
6666
auto_restore_last_session = false, -- On startup, loads the last saved session if session for cwd does not exist
67-
use_git_branch = false, -- Include git branch name in session name
67+
git_use_branch_name = false, -- Include git branch name in session name
68+
git_auto_restore_on_branch_change = false, -- Should we auto-restore the session when the git branch changes. Requires git_use_branch_name
6869
lazy_support = true, -- Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging
6970
bypass_save_filetypes = nil, -- List of filetypes to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards
7071
close_unsupported_windows = true, -- Close windows that aren't backed by normal file before autosaving a session

doc/auto-session.txt

+39-38
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,45 @@ Config *auto-session.config*
1111
AutoSession.Config *AutoSession.Config*
1212

1313
Fields: ~
14-
{enabled?} (boolean) Enables/disables auto saving and restoring
15-
{root_dir?} (string) root directory for session files, by default is `vim.fn.stdpath('data') .. '/sessions/'`
16-
{auto_save?} (boolean) Enables/disables auto saving session on exit
17-
{auto_restore?} (boolean) Enables/disables auto restoring session on start
18-
{auto_create?} (boolean|function) Enables/disables auto creating new session files. Can take a function that should return true/false if a new session file should be created or not
19-
{auto_delete_empty_sessions?} (boolean) Enables/disables deleting the session if there no named, non-empty buffers when auto-saving
20-
{suppressed_dirs?} (table) Suppress auto session for directories
21-
{allowed_dirs?} (table) Allow auto session for directories, if empty then all directories are allowed except for suppressed ones
22-
{auto_restore_last_session?} (boolean) On startup, loads the last saved session if session for cwd does not exist
23-
{use_git_branch?} (boolean) Include git branch name in session name to differentiate between sessions for different git branches
24-
{lazy_support?} (boolean) Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging
25-
{bypass_save_filetypes?} (table) List of file types to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards
26-
{close_unsupported_windows?} (boolean) Whether to close windows that aren't backed by a real file
27-
{args_allow_single_directory?} (boolean) Follow normal sesion save/load logic if launched with a single directory as the only argument
28-
Argv Handling
29-
{args_allow_files_auto_save?} (boolean|function) Allow saving a session even when launched with a file argument (or multiple files/dirs). It does not load any existing session first. While you can just set this to true, you probably want to set it to a function that decides when to save a session when launched with file args. See documentation for more detail
30-
{continue_restore_on_error?} (boolean) Keep loading the session even if there's an error. Set to false to get the line number of an error when loading a session
31-
{show_auto_restore_notif?} (boolean) Whether to show a notification when auto-restoring
32-
{log_level?} (string|integer) "debug", "info", "warn", "error" or vim.log.levels.DEBUG, vim.log.levels.INFO, vim.log.levels.WARN, vim.log.levels.ERROR
33-
{cwd_change_handling?} (boolean) Follow cwd changes, saving a session before change and restoring after
34-
{lsp_stop_on_restore?} (boolean|function) Should language servers be stopped when restoring a session. Can also be a function that will be called if set. Not called on autorestore from startup
35-
{restore_error_handler?} (restore_error_fn) Called when there's an error restoring. By default, it ignores fold errors otherwise it displays the error and returns false to disable auto_save
36-
37-
{purge_after_minutes?} (number|nil) -- Sessions older than purge_after_minutes will be deleted asynchronously on startup, e.g. set to 14400 to delete sessions that haven't been accessed for more than 10 days, defaults to off (no purging), requires >= nvim 0.10
38-
{session_lens?} (SessionLens) Session lens configuration options
39-
40-
{pre_save_cmds?} (table) executes before a session is saved
41-
42-
Hooks
43-
{save_extra_cmds?} (table) executes before a session is saved
44-
{post_save_cmds?} (table) executes after a session is saved
45-
{pre_restore_cmds?} (table) executes before a session is restored
46-
{post_restore_cmds?} (table) executes after a session is restored
47-
{pre_delete_cmds?} (table) executes before a session is deleted
48-
{post_delete_cmds?} (table) executes after a session is deleted
49-
{no_restore_cmds?} (table) executes at VimEnter when no session is restored
50-
{pre_cwd_changed_cmds?} (table) executes before cwd is changed if cwd_change_handling is true
51-
{post_cwd_changed_cmds?} (table) executes after cwd is changed if cwd_change_handling is true
14+
{enabled?} (boolean) Enables/disables auto saving and restoring
15+
{root_dir?} (string) root directory for session files, by default is `vim.fn.stdpath('data') .. '/sessions/'`
16+
{auto_save?} (boolean) Enables/disables auto saving session on exit
17+
{auto_restore?} (boolean) Enables/disables auto restoring session on start
18+
{auto_create?} (boolean|function) Enables/disables auto creating new session files. Can take a function that should return true/false if a new session file should be created or not
19+
{auto_delete_empty_sessions?} (boolean) Enables/disables deleting the session if there no named, non-empty buffers when auto-saving
20+
{suppressed_dirs?} (table) Suppress auto session for directories
21+
{allowed_dirs?} (table) Allow auto session for directories, if empty then all directories are allowed except for suppressed ones
22+
{auto_restore_last_session?} (boolean) On startup, loads the last saved session if session for cwd does not exist
23+
{git_use_branch_name?} (boolean) Include git branch name in session name to differentiate between sessions for different git branches
24+
{git_auto_restore_on_branch_change?} (boolean) Should we auto-restore the session when the git branch changes. Requires git_use_branch_name
25+
{lazy_support?} (boolean) Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging
26+
{bypass_save_filetypes?} (table) List of file types to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards
27+
{close_unsupported_windows?} (boolean) Whether to close windows that aren't backed by a real file
28+
{args_allow_single_directory?} (boolean) Follow normal sesion save/load logic if launched with a single directory as the only argument
29+
Argv Handling
30+
{args_allow_files_auto_save?} (boolean|function) Allow saving a session even when launched with a file argument (or multiple files/dirs). It does not load any existing session first. While you can just set this to true, you probably want to set it to a function that decides when to save a session when launched with file args. See documentation for more detail
31+
{continue_restore_on_error?} (boolean) Keep loading the session even if there's an error. Set to false to get the line number of an error when loading a session
32+
{show_auto_restore_notif?} (boolean) Whether to show a notification when auto-restoring
33+
{log_level?} (string|integer) "debug", "info", "warn", "error" or vim.log.levels.DEBUG, vim.log.levels.INFO, vim.log.levels.WARN, vim.log.levels.ERROR
34+
{cwd_change_handling?} (boolean) Follow cwd changes, saving a session before change and restoring after
35+
{lsp_stop_on_restore?} (boolean|function) Should language servers be stopped when restoring a session. Can also be a function that will be called if set. Not called on autorestore from startup
36+
{restore_error_handler?} (restore_error_fn) Called when there's an error restoring. By default, it ignores fold errors otherwise it displays the error and returns false to disable auto_save
37+
38+
{purge_after_minutes?} (number|nil) -- Sessions older than purge_after_minutes will be deleted asynchronously on startup, e.g. set to 14400 to delete sessions that haven't been accessed for more than 10 days, defaults to off (no purging), requires >= nvim 0.10
39+
{session_lens?} (SessionLens) Session lens configuration options
40+
41+
{pre_save_cmds?} (table) executes before a session is saved
42+
43+
Hooks
44+
{save_extra_cmds?} (table) executes before a session is saved
45+
{post_save_cmds?} (table) executes after a session is saved
46+
{pre_restore_cmds?} (table) executes before a session is restored
47+
{post_restore_cmds?} (table) executes after a session is restored
48+
{pre_delete_cmds?} (table) executes before a session is deleted
49+
{post_delete_cmds?} (table) executes after a session is deleted
50+
{no_restore_cmds?} (table) executes at VimEnter when no session is restored
51+
{pre_cwd_changed_cmds?} (table) executes before cwd is changed if cwd_change_handling is true
52+
{post_cwd_changed_cmds?} (table) executes after cwd is changed if cwd_change_handling is true
5253

5354

5455
SessionLens *SessionLens*

lua/auto-session/config.lua

+11-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ local M = {}
1616
---@field suppressed_dirs? table Suppress auto session for directories
1717
---@field allowed_dirs? table Allow auto session for directories, if empty then all directories are allowed except for suppressed ones
1818
---@field auto_restore_last_session? boolean On startup, loads the last saved session if session for cwd does not exist
19-
---@field use_git_branch? boolean Include git branch name in session name to differentiate between sessions for different git branches
19+
---@field git_use_branch_name? boolean Include git branch name in session name to differentiate between sessions for different git branches
20+
---@field git_auto_restore_on_branch_change? boolean Should we auto-restore the session when the git branch changes. Requires git_use_branch_name
2021
---@field lazy_support? boolean Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging
2122
---@field bypass_save_filetypes? table List of file types to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards
2223
---@field close_unsupported_windows? boolean Whether to close windows that aren't backed by a real file
@@ -79,7 +80,8 @@ local defaults = {
7980
suppressed_dirs = nil, -- Suppress session restore/create in certain directories
8081
allowed_dirs = nil, -- Allow session restore/create in certain directories
8182
auto_restore_last_session = false, -- On startup, loads the last saved session if session for cwd does not exist
82-
use_git_branch = false, -- Include git branch name in session name
83+
git_use_branch_name = false, -- Include git branch name in session name
84+
git_auto_restore_on_branch_change = false, -- Should we auto-restore the session when the git branch changes. Requires git_use_branch_name
8385
lazy_support = true, -- Automatically detect if Lazy.nvim is being used and wait until Lazy is done to make sure session is restored correctly. Does nothing if Lazy isn't being used. Can be disabled if a problem is suspected or for debugging
8486
bypass_save_filetypes = nil, -- List of filetypes to bypass auto save when the only buffer open is one of the file types listed, useful to ignore dashboards
8587
close_unsupported_windows = true, -- Close windows that aren't backed by normal file before autosaving a session
@@ -142,7 +144,7 @@ local function check_for_vim_globals(config)
142144
auto_session_suppress_dirs = "suppressed_dirs",
143145
auto_session_create_enabled = "auto_create",
144146
auto_session_enable_last_session = "auto_restore_last_session",
145-
auto_session_use_git_branch = "use_git_branch",
147+
auto_session_use_git_branch = "git_use_branch_name",
146148
pre_save_cmds = "pre_save_cmds",
147149
save_extra_cmds = "save_extra_cmds",
148150
post_save_cmds = "post_save_cmds",
@@ -176,7 +178,8 @@ local function check_old_config_names(config)
176178
auto_session_suppress_dirs = "suppressed_dirs",
177179
auto_session_create_enabled = "auto_create",
178180
auto_session_enable_last_session = "auto_restore_last_session",
179-
auto_session_use_git_branch = "use_git_branch",
181+
auto_session_use_git_branch = "git_use_branch_name",
182+
use_git_branch = "git_use_branch_name",
180183
auto_restore_lazy_delay_enabled = "lazy_support",
181184
bypass_session_save_file_types = "bypass_save_filetypes",
182185
silent_restore = "continue_restore_on_error",
@@ -244,6 +247,10 @@ function M.check(logger)
244247
logger.warn "the purge_after_minutes options requires nvim >= 0.10"
245248
end
246249

250+
if not M.git_use_branch_name and M.git_auto_restore_on_branch_change then
251+
logger.error "git_auto_restore_on_branch_change requires git_use_branch_name = true"
252+
end
253+
247254
-- TODO: At some point, we should pop up a warning about old config if
248255
-- M.has_old_config but let's make sure everything is working well before doing that
249256
end

lua/auto-session/git.lua

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
local AutoSession = require "auto-session"
2+
local Config = require "auto-session.config"
3+
local Lib = require "auto-session.lib"
4+
5+
local uv = vim.uv or vim.loop
6+
7+
local M = {}
8+
9+
M.uv_git_watcher = nil
10+
11+
function M.on_git_watch_event(cwd, current_branch)
12+
local new_branch = Lib.get_git_branch_name(cwd)
13+
14+
if new_branch == current_branch then
15+
return
16+
end
17+
18+
Lib.logger.debug("Git: branch changed, cur: " .. current_branch .. ", new: " .. new_branch)
19+
20+
-- need to save session for existing branch but can't use normal flow since
21+
-- the branch name has already changed so we make the session name here and pass it in
22+
23+
-- NOTE: Generating the session name this way won't work with named sessions but we
24+
-- don't support named sessions + git branch names together anyway
25+
26+
if Config.auto_save then
27+
local session_name = Lib.combine_session_name_with_git_branch(cwd, current_branch)
28+
AutoSession.SaveSession(session_name)
29+
end
30+
31+
if Lib.has_modified_buffers() then
32+
vim.ui.select({ "Yes", "No" }, {
33+
prompt = 'Unsaved changes! Really restore session for branch: "' .. new_branch .. '"?',
34+
format_item = function(item)
35+
return item
36+
end,
37+
}, function(choice)
38+
if choice == "Yes" then
39+
AutoSession.AutoRestoreSession()
40+
else
41+
AutoSession.DisableAutoSave()
42+
vim.notify(
43+
"Session restore cancelled. Auto-save disabled.\nAfter saving your changes, run :SessionRestore\nto load the session for branch: "
44+
.. new_branch
45+
)
46+
end
47+
end)
48+
else
49+
-- No modified buffers, proceed with auto-restore
50+
AutoSession.AutoRestoreSession()
51+
end
52+
end
53+
54+
---Watch for git branch changes
55+
---@param cwd string current working directory
56+
---@param towatch string file to watch, should be something like .git/HEAD
57+
function M.start_watcher(cwd, towatch)
58+
if M.uv_git_watcher then
59+
M.uv_git_watcher:stop()
60+
Lib.logger.debug "Git: stopped old watcher so we can start a new one"
61+
end
62+
63+
M.uv_git_watcher = assert(uv.new_fs_event())
64+
local current_branch = Lib.get_git_branch_name(cwd)
65+
66+
Lib.logger.debug("Git: starting watcher", { cwd, current_branch })
67+
68+
-- Watch .git/HEAD to detect branch changes
69+
M.uv_git_watcher:start(
70+
towatch,
71+
{},
72+
Lib.debounce(function(err)
73+
if err then
74+
vim.schedule(function()
75+
Lib.logger.err "Error watching for git branch changes"
76+
end)
77+
return
78+
end
79+
80+
vim.schedule(function()
81+
M.on_git_watch_event(cwd, current_branch)
82+
83+
-- git often (always?) replaces .git/HEAD which can change the inode being
84+
-- watched so we need to stop the current watcher and start another one to
85+
-- make sure we keep getting future events
86+
M.start_watcher(cwd, towatch)
87+
end)
88+
end, { ms = 100 })
89+
)
90+
end
91+
92+
function M.stop_watcher()
93+
if not M.uv_git_watcher then
94+
return
95+
end
96+
97+
M.uv_git_watcher:stop()
98+
Lib.logger.debug "Git: stopped watcher"
99+
M.uv_git_watcher = nil
100+
end
101+
102+
return M

lua/auto-session/health.lua

+8-4
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,14 @@ local function check_lazy_settings()
7373
end
7474
end
7575

76-
function check_features()
77-
if Config.purge_after_minutes and vim.fn.has "nvim-0.10" ~= 1 then
78-
warn "The purge_after_minutes config option requires nvim 0.10 or greater to work"
79-
end
76+
local function check_features()
77+
local loggerObj = {
78+
error = error,
79+
info = info,
80+
warn = warn,
81+
}
82+
83+
Config.check(loggerObj)
8084
end
8185

8286
function M.check()

0 commit comments

Comments
 (0)