diff --git a/lua/tabi/storage/local.lua b/lua/tabi/storage/local.lua index 67ab3bf..b7fbc83 100644 --- a/lua/tabi/storage/local.lua +++ b/lua/tabi/storage/local.lua @@ -4,13 +4,14 @@ local M = {} local utils = require("tabi.utils") --- Get storage directory path +--- Uses git common directory to support git worktrees, where .git is a file rather than a directory. ---@return string|nil function M.get_storage_dir() - local git_root = utils.get_git_root() - if not git_root then + local git_common_dir = utils.get_git_common_dir() + if not git_common_dir then return nil end - return git_root .. "/.git/tabi" + return git_common_dir .. "/tabi" end --- Get sessions directory path diff --git a/lua/tabi/utils.lua b/lua/tabi/utils.lua index 1f401a6..be56f09 100644 --- a/lua/tabi/utils.lua +++ b/lua/tabi/utils.lua @@ -53,6 +53,29 @@ function M.get_git_root() return root and vim.trim(root) or nil end +--- Get git common directory (resolves git worktree gitfiles to the actual .git directory) +--- In a normal repo, returns the same as .git directory. +--- In a worktree, returns the main repository's .git directory instead of the worktree-specific one. +---@return string|nil +function M.get_git_common_dir() + local handle = io.popen("git rev-parse --git-common-dir 2>/dev/null") + if not handle then + return nil + end + local dir = handle:read("*a") + handle:close() + if not dir or vim.trim(dir) == "" then + return nil + end + dir = vim.trim(dir) + -- --git-common-dir may return a relative path; convert to absolute + if not vim.startswith(dir, "/") then + local cwd = vim.fn.getcwd() + dir = cwd .. "/" .. dir + end + return dir +end + --- Ensure directory exists ---@param path string function M.ensure_dir(path) diff --git a/test/tabi/storage/local_spec.lua b/test/tabi/storage/local_spec.lua index 33bd8a7..49fc008 100644 --- a/test/tabi/storage/local_spec.lua +++ b/test/tabi/storage/local_spec.lua @@ -3,20 +3,20 @@ local utils = require("tabi.utils") describe("tabi.storage.local", function() local temp_dir - local original_get_git_root + local original_get_git_common_dir local original_is_git_repo before_each(function() - -- Create temporary directory to simulate git repo + -- Create temporary directory to simulate git repo's .git directory temp_dir = vim.fn.tempname() .. "_tabi_local_test" vim.fn.mkdir(temp_dir .. "/.git", "p") -- Mock git functions - original_get_git_root = utils.get_git_root + original_get_git_common_dir = utils.get_git_common_dir original_is_git_repo = utils.is_git_repo - utils.get_git_root = function() - return temp_dir + utils.get_git_common_dir = function() + return temp_dir .. "/.git" end utils.is_git_repo = function() @@ -26,7 +26,7 @@ describe("tabi.storage.local", function() after_each(function() -- Restore original functions - utils.get_git_root = original_get_git_root + utils.get_git_common_dir = original_get_git_common_dir utils.is_git_repo = original_is_git_repo -- Clean up temporary directory @@ -42,12 +42,26 @@ describe("tabi.storage.local", function() end) it("should return nil when not in git repo", function() - utils.get_git_root = function() + utils.get_git_common_dir = function() return nil end local dir = local_storage.get_storage_dir() assert.is_nil(dir) end) + + it("should use the common git dir so worktrees share storage", function() + -- Simulate a worktree: git_common_dir points to the main repo's .git + local main_git_dir = vim.fn.tempname() .. "_main_git" + vim.fn.mkdir(main_git_dir, "p") + utils.get_git_common_dir = function() + return main_git_dir + end + + local dir = local_storage.get_storage_dir() + assert.are.equal(main_git_dir .. "/tabi", dir) + + vim.fn.delete(main_git_dir, "rf") + end) end) describe("get_sessions_dir", function() @@ -57,7 +71,7 @@ describe("tabi.storage.local", function() end) it("should return nil when not in git repo", function() - utils.get_git_root = function() + utils.get_git_common_dir = function() return nil end local dir = local_storage.get_sessions_dir() diff --git a/test/tabi/utils_spec.lua b/test/tabi/utils_spec.lua index 0e591e8..936904b 100644 --- a/test/tabi/utils_spec.lua +++ b/test/tabi/utils_spec.lua @@ -64,6 +64,20 @@ describe("tabi.utils", function() end) end) + describe("get_git_common_dir", function() + it("should return a non-empty string when in a git repo", function() + -- This test runs inside the tabi.nvim git repo + local dir = utils.get_git_common_dir() + -- May be nil in environments without git, so only assert format when non-nil + if dir ~= nil then + assert.is_string(dir) + assert.is_true(#dir > 0) + -- Must be an absolute path + assert.is_true(vim.startswith(dir, "/"), "get_git_common_dir should return an absolute path, got: " .. dir) + end + end) + end) + describe("ensure_dir", function() local temp_dir