local t = require("test") local helpers = require("test.git.helpers") require("git").init() local git = helpers.git local make_repo = helpers.make_repo ---@param r ow.Git.Repo ---@param key string ---@param timeout integer? local function wait_cleared(r, key, timeout) vim.wait(timeout or 2000, function() return r._cache[key] == nil end) end t.test("list_refs returns heads, tags, remotes (no HEAD)", function() local dir = make_repo({ a = "x" }) git(dir, "branch", "feature") git(dir, "tag", "v1") local r = assert(require("git.repo").resolve(dir)) local refs = r:list_refs() table.sort(refs) t.eq(refs, { "feature", "main", "v1" }) end) t.test("list_pseudo_refs always includes HEAD", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) t.eq(r:list_pseudo_refs(), { "HEAD" }) end) t.test("list_pseudo_refs picks up MERGE_HEAD when present", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) t.write(dir .. "/.git", "MERGE_HEAD", "deadbeef\n") -- Bypass cache (file appeared after first scan). r._cache = {} local refs = r:list_pseudo_refs() table.sort(refs) t.eq(refs, { "HEAD", "MERGE_HEAD" }) end) t.test("list_stash_refs is empty when no stash", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) t.eq(r:list_stash_refs(), {}) end) t.test("list_stash_refs lists stash + entries when stash exists", function() local dir = make_repo({ a = "x" }) t.write(dir, "a", "modified") git(dir, "stash") local r = assert(require("git.repo").resolve(dir)) local refs = r:list_stash_refs() t.eq(#refs, 2) t.eq(refs[1], "stash") t.eq(refs[2], "stash@{0}") end) t.test("get_cached memoizes by key", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) local calls = 0 local v1 = r:get_cached("k", function() calls = calls + 1 return { "first" } end) local v2 = r:get_cached("k", function() calls = calls + 1 return { "second" } end) t.eq(calls, 1) t.truthy(v1 == v2, "second call should return cached table") end) t.test("cache clears after top-level .git change (commit)", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) local _ = r:list_refs() t.truthy(r._cache.refs) t.write(dir, "b", "y") git(dir, "add", "b") git(dir, "commit", "-q", "-m", "two") wait_cleared(r, "refs") t.falsy(r._cache.refs, "cache should be cleared after commit") end) t.test("cache clears after slash-branch creation (polyfill)", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) local _ = r:list_refs() t.truthy(r._cache.refs) git(dir, "branch", "feat/foo") wait_cleared(r, "refs") t.falsy(r._cache.refs, "cache should clear via polyfilled subdir watcher") local refs = r:list_refs() table.sort(refs) t.eq(refs, { "feat/foo", "main" }) end) t.test("cache clears after deeply nested slash branch", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) local _ = r:list_refs() git(dir, "branch", "deep/a/b/c") wait_cleared(r, "refs") local refs = r:list_refs() table.sort(refs) t.eq(refs, { "deep/a/b/c", "main" }) end) t.test("watcher cleans up after a slash-branch dir is removed", function() local dir = make_repo({ a = "x" }) local r = assert(require("git.repo").resolve(dir)) git(dir, "branch", "feat/foo") -- Wait for the dynamic watcher on .git/refs/heads/feat to be added. local feat_path = dir .. "/.git/refs/heads/feat" vim.wait(2000, function() return r._watchers[feat_path] ~= nil end) t.truthy(r._watchers[feat_path], "feat/ subdir should be watched") -- Remove the branch; the feat/ directory becomes empty and is -- pruned by git, triggering the deleted-self event. git(dir, "branch", "-D", "feat/foo") vim.wait(2000, function() return r._watchers[feat_path] == nil end) t.falsy(r._watchers[feat_path], "watcher should self-close") end)