Repository: rmagatti/auto-session (lua)

Issue:
[BUG] `auto_restore_session_at_vim_enter` with `vim .` with use_git_branch never restores session
**Describe the bug**
I think ive found a bug in the `use_git_branch` implementation, when used with auto restore sessions and launching nvim with a single directory parameter (`nvim .`)


1. When the session is saved, it is saved as `<cwd>|<branch_name>` as the result of [get_session_file_name git handling](https://github.com/rmagatti/auto-session/blob/317412742990371f8e4709074da5c378456a27ff/lua/auto-session/init.lua#L211-L232)
2. this block only runs when `session_name` is nil or empty string
3. since when creating the initial session with [AutoSession.AutoSaveSession](https://github.com/rmagatti/auto-session/blob/317412742990371f8e4709074da5c378456a27ff/lua/auto-session/init.lua#L302), the session name is nil, and calls `get_session_file_name` with nil as the session_name, which returns cwd + the git branch name
4. close nvim
5. reopen with `nvim .`
6. [AutoSession.auto_restore_session_at_vim_enter()](https://github.com/rmagatti/auto-session/blob/317412742990371f8e4709074da5c378456a27ff/lua/auto-session/init.lua#L462) gets kicked off to try and restore.
7. it then sets the session name to the directory it was launched with and calls `AutoSession.AutoRestoreSession(session_name, true)`
8. this time, session_name is not null, so the git branch does not get added and it returns the expanded path of what nvim was launched with
9. it tries to find this session, but the only one that exists has the branch name
10. session restore fails


**To Reproduce**
Steps to reproduce the behavior:
1. set `args_allow_single_directory = true`
2. open nvim in a new directory
3. open some tabs or buffers or something for a visual indicator that autosession restored
4. close nvim (kicking off the autosave)
5. reopen nvim with `nvim .` in the same directory
6. you will get an error to the effect of `Could not restore session: <your_cwd>` 


**Expected behavior**
Autosession restores the session associated with your cwd and branch name (with an additional buffer if you opened nvim with a file arg, or with filetree/netrw open if you opened with a dir arg)

**Checkhealth**
```
==============================================================================
auto-session: require("auto-session.health").check()

vim options ~
- OK vim.o.sessionoptions

Lazy.nvim settings ~
- OK Lazy.nvim support is enabled
- OK auto-session is not lazy loaded

Config ~
- OK 
  {
    auto_create = true,
    auto_restore = true,
    auto_save = true,
    bypass_save_filetypes = { "neo-tree", "snacks_dashboard" },
    close_unsupported_windows = true,
    log_level = "debug",
    previewer = true,
    root_dir = "/Users/derekbunch/.local/share/nvim/auto-session/",
    session_lens = {
      load_on_setup = true,
      mappings = {
        alternate_session = { "i", "<C-S>" },
        copy_session = { "i", "<C-Y>" },
        delete_session = { "i", "<C-D>" }
      },
      previewer = true,
      session_control = {
        control_dir = "/Users/derekbunch/.local/state/nvim/auto-session-control/",
        control_filename = "session_control.json"
      },
      theme_conf = {}
    },
    show_auto_restore_notif = true,
    suppressed_dirs = { "~/", "~/Downloads", "/" },
    use_git_branch = true
  }

General Info ~
- Session directory: /Users/derekbunch/.local/share/nvim/auto-session/
- Current session: 
- Current session file: 
```

**Baseline (please complete the following information):**
- Result of `set sessionoptions?`:
`sessionoptions=buffers,curdir,help,tabpages,winsize,winpos,terminal,localoptions,globals,skiprtp`
- OS. e.g `uname -a`:
```
Darwin Dereks-MacBook 24.3.0 Darwin Kernel Version 24.3.0: Thu Jan  2 20:24:24 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6030 arm64 arm Darwin
``` 
- Neovim version `nvim --version`
```
NVIM v0.10.4
Build type: Release
LuaJIT 2.1.1713773202
Run "nvim -V1 -v" for more info
```

Pull request description:
When `use_git_branch=true, args_allow_single_directory=true`, the code wasn't adding the git branch name to the launch directory argument.

Also refactored some of the git functions into Lib and added a unit test for both `nvim .` and `nvim git_proj`.

Fixes #419

Patch:
diff --git a/lua/auto-session/init.lua b/lua/auto-session/init.lua
index 78e3701c..15499499 100644
--- a/lua/auto-session/init.lua
+++ b/lua/auto-session/init.lua
@@ -40,21 +40,6 @@ local function is_auto_create_enabled()
   return result
 end
 
--- get the current git branch name, if any, and only if configured to do so
-local function get_git_branch_name()
-  if not Config.use_git_branch then
-    return ""
-  end
-
-  -- WARN: this assumes you want the branch of the cwd
-  local out = vim.fn.systemlist "git rev-parse --abbrev-ref HEAD"
-  if vim.v.shell_error ~= 0 then
-    Lib.logger.debug(string.format("git failed with: %s", table.concat(out, "\n")))
-    return ""
-  end
-  return out[1]
-end
-
 local in_pager_mode = function()
   return vim.g.in_pager_mode == Lib._VIM_TRUE
 end
@@ -212,22 +197,9 @@ local function get_session_file_name(session_name, legacy)
     session_name = vim.fn.getcwd(-1, -1)
     Lib.logger.debug("get_session_file_name no session_name, using cwd: " .. session_name)
 
-    local git_branch_name = get_git_branch_name()
-    if git_branch_name and git_branch_name ~= "" then
-      -- NOTE: By including it in the session name, there's the possibility of a collision
-      -- with an actual directory named session_name|branch_name. Meaning, that if someone
-      -- created a session in session_name (while branch_name is checked out) and then also
-      -- went to edit in a directory literally called session_name|branch_name. the sessions
-      -- would collide. Obviously, that's not perfect but I think it's an ok price to pay to
-      -- get branch specific sessions and still have a cwd derived text key to identify sessions
-      -- that can be used everywhere, incuding :SessionRestore
-      if legacy then
-        session_name = session_name .. "_" .. git_branch_name
-      else
-        -- now that we're percent encoding, we can pick a less likely character, even if it doesn't
-        -- avoid the problem entirely
-        session_name = session_name .. "|" .. git_branch_name
-      end
+    if Config.use_git_branch then
+      session_name = Lib.combine_session_name_with_git_branch(session_name, Lib.get_git_branch_name(), legacy)
+      Lib.logger.debug("git enabled, session_name:" .. session_name)
     end
   end
 
@@ -476,6 +448,13 @@ function AutoSession.auto_restore_session_at_vim_enter()
     local session_name = Lib.remove_trailing_separator(vim.fn.fnamemodify(launch_argv[1], ":p"))
     Lib.logger.debug("Launched with single directory, using as session_dir: " .. session_name)
 
+    if Config.use_git_branch then
+      -- Get the git branch for that directory, no legacy git name support
+      session_name =
+        Lib.combine_session_name_with_git_branch(session_name, Lib.get_git_branch_name(session_name), false)
+      Lib.logger.debug("git enabled, launch argument with potential git branch: " .. session_name)
+    end
+
     if AutoSession.AutoRestoreSession(session_name, true) then
       return true
     end
diff --git a/lua/auto-session/lib.lua b/lua/auto-session/lib.lua
index dd226a26..96b62f2e 100644
--- a/lua/auto-session/lib.lua
+++ b/lua/auto-session/lib.lua
@@ -716,4 +716,46 @@ function Lib.get_alternate_session_name(session_control_conf)
   return Lib.escaped_session_name_to_session_name(file_name)
 end
 
+---Returns git branch name, if any, for the path (if passed in) or the cwd
+---@param path string? Optional path to use when checking for git branch
+---@return string # Name of git branch or empty string
+function Lib.get_git_branch_name(path)
+  local git_cmd = string.format("git%s rev-parse --abbrev-ref HEAD", path and (" -C " .. path) or "")
+
+  local out = vim.fn.systemlist(git_cmd)
+  if vim.v.shell_error ~= 0 then
+    Lib.logger.debug(string.format("git failed with: %s", table.concat(out, "\n")))
+    return ""
+  end
+  return out[1]
+end
+
+---Adds the git branch name to the passwed in session name
+---@param session_name string session name to add the git branch to
+---@param git_branch_name string? git branch name to use
+---@param legacy boolean? whether to use current or legacy naming convention
+---@return string # Session name with the git branch name added on (if there is one)
+function Lib.combine_session_name_with_git_branch(session_name, git_branch_name, legacy)
+  legacy = legacy or false
+
+  if not git_branch_name or git_branch_name == "" then
+    return session_name
+  end
+
+  -- NOTE: By including it in the session name, there's the possibility of a collision
+  -- with an actual directory named session_name|branch_name. Meaning, that if someone
+  -- created a session in session_name (while branch_name is checked out) and then also
+  -- went to edit in a directory literally called session_name|branch_name. the sessions
+  -- would collide. Obviously, that's not perfect but I think it's an ok price to pay to
+  -- get branch specific sessions and still have a cwd derived text key to identify sessions
+  -- that can be used everywhere, incuding :SessionRestore
+  if legacy then
+    return session_name .. "_" .. git_branch_name
+  end
+
+  -- now that we're percent encoding, we can pick a less likely character, even if it doesn't
+  -- avoid the problem entirely
+  return session_name .. "|" .. git_branch_name
+end
+
 return Lib


Test patch:
diff --git a/tests/git_spec.lua b/tests/git_spec.lua
index abf8ea85..faa60889 100644
--- a/tests/git_spec.lua
+++ b/tests/git_spec.lua
@@ -1,9 +1,11 @@
 ---@diagnostic disable: undefined-field
 local TL = require "tests/test_lib"
+local stub = require "luassert.stub"
 
 describe("The git config", function()
   local as = require "auto-session"
   local Lib = require "auto-session.lib"
+  local c = require "auto-session.config"
   as.setup {
     auto_session_use_git_branch = true,
     -- log_level = "debug",
@@ -21,7 +23,7 @@ describe("The git config", function()
   -- get a file in that dir
   vim.cmd("e " .. TL.test_file)
   vim.cmd("w! " .. git_test_path .. "/test.txt")
-  vim.cmd "%bw"
+  vim.cmd "silent %bw"
 
   -- change to that dir
   vim.cmd("cd " .. git_test_path)
@@ -72,7 +74,7 @@ describe("The git config", function()
   end)
 
   it("Autorestores a session with the branch name", function()
-    vim.cmd "%bw!"
+    vim.cmd "silent %bw!"
     assert.equals(0, vim.fn.bufexists "test.txt")
 
     as.AutoRestoreSession()
@@ -93,7 +95,7 @@ describe("The git config", function()
     assert.equals(1, vim.fn.filereadable(legacy_branch_session_path))
     assert.equals(0, vim.fn.filereadable(branch_session_path))
 
-    vim.cmd "%bw!"
+    vim.cmd "silent %bw!"
     assert.equals(0, vim.fn.bufexists "test.txt")
 
     as.AutoRestoreSession()
@@ -116,4 +118,48 @@ describe("The git config", function()
     assert.equals(vim.fn.getcwd() .. " (branch: slash/branch)", Lib.current_session_name())
     assert.equals(git_test_dir .. " (branch: slash/branch)", Lib.current_session_name(true))
   end)
+
+  it("load a session named with git branch from . directory", function()
+    c.args_allow_single_directory = true
+    c.log_level = "debug"
+
+    -- delete all buffers
+    vim.cmd "silent %bw"
+
+    local s = stub(vim.fn, "argv")
+    s.returns { "." }
+
+    -- only exported because we set the unit testing env in TL
+    assert.True(as.auto_restore_session_at_vim_enter())
+
+    assert.equals(1, vim.fn.bufexists "test.txt")
+
+    -- Revert the stub
+    vim.fn.argv:revert()
+    c.auto_save = false
+  end)
+
+  it("load a session named with git branch from directory argument", function()
+    c.args_allow_single_directory = true
+    c.log_level = "debug"
+    c.cwd_change_handling = false
+
+    -- delete all buffers
+    vim.cmd "silent %bw"
+
+    -- change to parent directory
+    vim.cmd "cd .."
+
+    local s = stub(vim.fn, "argv")
+    s.returns { git_test_dir }
+
+    -- only exported because we set the unit testing env in TL
+    assert.True(as.auto_restore_session_at_vim_enter())
+
+    assert.equals(1, vim.fn.bufexists "test.txt")
+
+    -- Revert the stub
+    vim.fn.argv:revert()
+    c.auto_save = false
+  end)
 end)