feat: support for detached working trees
Added config.worktrees. Array of tables with the keys 'gitdir' and 'toplevel'. If attaching normally fails, then each entry in the table is attempted. Example: worktrees = { { toplevel = vim.env.HOME, gitdir = vim.env.HOME .. '/projects/dotfiles/.git' } } Resolves #397
This commit is contained in:
parent
9c3ca02766
commit
50e32c6309
|
@ -28,6 +28,7 @@ Super fast git decorations implemented purely in lua/teal.
|
|||
- Live intra-line word diff
|
||||
- Ability to display deleted/changed lines via virtual lines.
|
||||
- Support for [yadm](https://yadm.io/)
|
||||
- Support for detached working trees.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
|
|
@ -518,6 +518,24 @@ keymaps *gitsigns-config-keymaps*
|
|||
to `{}`, and |gitsigns-config-on_attach| can instead be used to define
|
||||
mappings.
|
||||
|
||||
worktrees *gitsigns-config-worktrees*
|
||||
Type: `table`, Default: `nil`
|
||||
|
||||
Detached working trees.
|
||||
|
||||
Array of tables with the keys `gitdir` and `toplevel`.
|
||||
|
||||
If normal attaching fails, then each entry in the table is attempted
|
||||
with the work tree details set.
|
||||
|
||||
Example: >
|
||||
worktrees = {
|
||||
{
|
||||
toplevel = vim.env.HOME,
|
||||
gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
|
||||
}
|
||||
}
|
||||
|
||||
on_attach *gitsigns-config-on_attach*
|
||||
Type: `function`, Default: `nil`
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
local async = require('gitsigns.async')
|
||||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
|
@ -153,6 +154,37 @@ local function on_detach(_, bufnr)
|
|||
M.detach(bufnr, true)
|
||||
end
|
||||
|
||||
local function on_attach_pre(bufnr)
|
||||
local gitdir, toplevel
|
||||
if config._on_attach_pre then
|
||||
local res = async.wrap(config._on_attach_pre, 2)(bufnr)
|
||||
dprintf('ran on_attach_pre with result %s', vim.inspect(res))
|
||||
if type(res) == "table" then
|
||||
if type(res.gitdir) == 'string' then
|
||||
gitdir = res.gitdir
|
||||
end
|
||||
if type(res.toplevel) == 'string' then
|
||||
toplevel = res.toplevel
|
||||
end
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr, file, encoding)
|
||||
if not config.worktrees then
|
||||
return
|
||||
end
|
||||
|
||||
for _, wt in ipairs(config.worktrees) do
|
||||
local git_obj = git.Obj.new(file, encoding, wt.gitdir, wt.toplevel)
|
||||
if git_obj and git_obj.object_name then
|
||||
dprintf('Using worktree %s', vim.inspect(wt))
|
||||
return git_obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -190,6 +222,7 @@ local attach_throttled = throttle_by_id(function(cbuf, aucmd)
|
|||
end
|
||||
|
||||
local file, commit = get_buf_path(cbuf)
|
||||
local encoding = vim.bo[cbuf].fileencoding
|
||||
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
|
@ -198,7 +231,14 @@ local attach_throttled = throttle_by_id(function(cbuf, aucmd)
|
|||
return
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, vim.bo[cbuf].fileencoding)
|
||||
local gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
|
||||
if not git_obj then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
scheduler()
|
||||
end
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
|
|
|
@ -92,6 +92,7 @@ local M = {}
|
|||
|
||||
|
||||
function M.wrap(func, argc)
|
||||
assert(argc)
|
||||
return function(...)
|
||||
if not async_thread.running() then
|
||||
|
||||
|
|
|
@ -24,7 +24,14 @@ local SchemaElem = {Deprecated = {}, }
|
|||
|
||||
|
||||
|
||||
local M = {Config = {DiffOpts = {}, SignConfig = {}, watch_gitdir = {}, current_line_blame_formatter_opts = {}, current_line_blame_opts = {}, yadm = {}, }, }
|
||||
local M = {Config = {DiffOpts = {}, SignConfig = {}, watch_gitdir = {}, current_line_blame_formatter_opts = {}, current_line_blame_opts = {}, yadm = {}, Worktree = {}, }, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -175,6 +182,49 @@ M.schema = {
|
|||
]],
|
||||
},
|
||||
|
||||
worktrees = {
|
||||
type = 'table',
|
||||
default = nil,
|
||||
description = [[
|
||||
Detached working trees.
|
||||
|
||||
Array of tables with the keys `gitdir` and `toplevel`.
|
||||
|
||||
If normal attaching fails, then each entry in the table is attempted
|
||||
with the work tree details set.
|
||||
|
||||
Example: >
|
||||
worktrees = {
|
||||
{
|
||||
toplevel = vim.env.HOME,
|
||||
gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
|
||||
}
|
||||
}
|
||||
]],
|
||||
},
|
||||
|
||||
_on_attach_pre = {
|
||||
type = 'function',
|
||||
default = nil,
|
||||
description = [[
|
||||
Asynchronous hook called before attaching to a buffer. Mainly used to
|
||||
configure detached worktrees.
|
||||
|
||||
This callback must call its callback argument. The callback argument can
|
||||
accept an optional table argument with the keys: 'gitdir' and 'toplevel'.
|
||||
|
||||
Example: >
|
||||
on_attach_pre = function(bufnr, callback)
|
||||
...
|
||||
callback {
|
||||
gitdir = ...,
|
||||
toplevel = ...
|
||||
}
|
||||
end
|
||||
<
|
||||
]],
|
||||
},
|
||||
|
||||
on_attach = {
|
||||
type = 'function',
|
||||
default = nil,
|
||||
|
|
|
@ -45,6 +45,7 @@ local function get_context(lvl)
|
|||
ret.name = name0:gsub('(.*)%d+$', '%1')
|
||||
end
|
||||
ret.bufnr = getvarvalue('bufnr', lvl) or
|
||||
getvarvalue('_bufnr', lvl) or
|
||||
getvarvalue('cbuf', lvl) or
|
||||
getvarvalue('buf', lvl)
|
||||
|
||||
|
|
|
@ -23,7 +23,17 @@ local GJobSpec = {}
|
|||
|
||||
|
||||
|
||||
local M = {BlameInfo = {}, Version = {}, Repo = {}, FileProps = {}, Obj = {}, }
|
||||
local M = {BlameInfo = {}, Version = {}, RepoInfo = {}, Repo = {}, FileProps = {}, Obj = {}, }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -242,7 +252,7 @@ local function normalize_path(path)
|
|||
return path
|
||||
end
|
||||
|
||||
M.get_repo_info = function(path, cmd)
|
||||
M.get_repo_info = function(path, cmd, gitdir, toplevel)
|
||||
|
||||
|
||||
local has_abs_gd = check_version({ 2, 13 })
|
||||
|
@ -252,21 +262,36 @@ M.get_repo_info = function(path, cmd)
|
|||
|
||||
scheduler()
|
||||
|
||||
local results = M.command({
|
||||
local args = {}
|
||||
|
||||
if gitdir then
|
||||
vim.list_extend(args, { '--git-dir', gitdir })
|
||||
end
|
||||
|
||||
if toplevel then
|
||||
vim.list_extend(args, { '--work-tree', toplevel })
|
||||
end
|
||||
|
||||
vim.list_extend(args, {
|
||||
'rev-parse', '--show-toplevel', git_dir_opt, '--abbrev-ref', 'HEAD',
|
||||
}, {
|
||||
})
|
||||
|
||||
local results = M.command(args, {
|
||||
command = cmd or 'git',
|
||||
supress_stderr = true,
|
||||
cwd = path,
|
||||
})
|
||||
|
||||
local toplevel = normalize_path(results[1])
|
||||
local gitdir = normalize_path(results[2])
|
||||
if gitdir and not has_abs_gd then
|
||||
gitdir = uv.fs_realpath(gitdir)
|
||||
local ret = {
|
||||
toplevel = normalize_path(results[1]),
|
||||
gitdir = normalize_path(results[2]),
|
||||
}
|
||||
ret.abbrev_head = process_abbrev_head(ret.gitdir, results[3], path, cmd)
|
||||
if ret.gitdir and not has_abs_gd then
|
||||
ret.gitdir = uv.fs_realpath(ret.gitdir)
|
||||
end
|
||||
local abbrev_head = process_abbrev_head(gitdir, results[3], path, cmd)
|
||||
return toplevel, gitdir, abbrev_head
|
||||
ret.detached = ret.toplevel and ret.gitdir ~= ret.toplevel .. '/.git'
|
||||
return ret
|
||||
end
|
||||
|
||||
M.set_version = function(version)
|
||||
|
@ -289,7 +314,18 @@ end
|
|||
Repo.command = function(self, args, spec)
|
||||
spec = spec or {}
|
||||
spec.cwd = self.toplevel
|
||||
return M.command({ '--git-dir=' .. self.gitdir, unpack(args) }, spec)
|
||||
|
||||
local args1 = {
|
||||
'--git-dir', self.gitdir,
|
||||
}
|
||||
|
||||
if self.detached then
|
||||
vim.list_extend(args1, { '--work-tree', self.toplevel })
|
||||
end
|
||||
|
||||
vim.list_extend(args1, args)
|
||||
|
||||
return M.command(args1, spec)
|
||||
end
|
||||
|
||||
Repo.files_changed = function(self)
|
||||
|
@ -322,21 +358,27 @@ Repo.get_show_text = function(self, object, encoding)
|
|||
end
|
||||
|
||||
Repo.update_abbrev_head = function(self)
|
||||
_, _, self.abbrev_head = M.get_repo_info(self.toplevel)
|
||||
self.abbrev_head = M.get_repo_info(self.toplevel).abbrev_head
|
||||
end
|
||||
|
||||
Repo.new = function(dir)
|
||||
Repo.new = function(dir, gitdir, toplevel)
|
||||
local self = setmetatable({}, { __index = Repo })
|
||||
|
||||
self.username = M.command({ 'config', 'user.name' })[1]
|
||||
self.toplevel, self.gitdir, self.abbrev_head = M.get_repo_info(dir)
|
||||
local info = M.get_repo_info(dir, nil, gitdir, toplevel)
|
||||
for k, v in pairs(info) do
|
||||
(self)[k] = v
|
||||
end
|
||||
|
||||
|
||||
if M.enable_yadm and not self.gitdir then
|
||||
if vim.startswith(dir, os.getenv('HOME')) and
|
||||
#M.command({ 'ls-files', dir }, { command = 'yadm' }) ~= 0 then
|
||||
self.toplevel, self.gitdir, self.abbrev_head =
|
||||
M.get_repo_info(dir, 'yadm')
|
||||
M.get_repo_info(dir, 'yadm', gitdir, toplevel)
|
||||
local yadm_info = M.get_repo_info(dir, 'yadm', gitdir, toplevel)
|
||||
for k, v in pairs(yadm_info) do
|
||||
(self)[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -352,9 +394,9 @@ Obj.command = function(self, args, spec)
|
|||
return self.repo:command(args, spec)
|
||||
end
|
||||
|
||||
Obj.update_file_info = function(self, update_relpath)
|
||||
Obj.update_file_info = function(self, update_relpath, silent)
|
||||
local old_object_name = self.object_name
|
||||
local props = self:file_info()
|
||||
local props = self:file_info(self.file, silent)
|
||||
|
||||
if update_relpath then
|
||||
self.relpath = props.relpath
|
||||
|
@ -368,7 +410,7 @@ Obj.update_file_info = function(self, update_relpath)
|
|||
return old_object_name ~= self.object_name
|
||||
end
|
||||
|
||||
Obj.file_info = function(self, file)
|
||||
Obj.file_info = function(self, file, silent)
|
||||
local results, stderr = self:command({
|
||||
'-c', 'core.quotepath=off',
|
||||
'ls-files',
|
||||
|
@ -379,7 +421,7 @@ Obj.file_info = function(self, file)
|
|||
file or self.file,
|
||||
}, { supress_stderr = true })
|
||||
|
||||
if stderr then
|
||||
if stderr and not silent then
|
||||
|
||||
|
||||
if not stderr:match('^warning: could not open directory .*: No such file or directory') then
|
||||
|
@ -540,7 +582,7 @@ Obj.has_moved = function(self)
|
|||
end
|
||||
end
|
||||
|
||||
Obj.new = function(file, encoding)
|
||||
Obj.new = function(file, encoding, gitdir, toplevel)
|
||||
if in_git_dir(file) then
|
||||
dprint('In git dir')
|
||||
return nil
|
||||
|
@ -549,14 +591,17 @@ Obj.new = function(file, encoding)
|
|||
|
||||
self.file = file
|
||||
self.encoding = encoding
|
||||
self.repo = Repo.new(util.dirname(file))
|
||||
self.repo = Repo.new(util.dirname(file), gitdir, toplevel)
|
||||
|
||||
if not self.repo.gitdir then
|
||||
dprint('Not in git repo')
|
||||
return nil
|
||||
end
|
||||
|
||||
self:update_file_info(true)
|
||||
|
||||
local silent = gitdir ~= nil and toplevel ~= nil
|
||||
|
||||
self:update_file_info(true, silent)
|
||||
|
||||
return self
|
||||
end
|
||||
|
|
|
@ -380,7 +380,9 @@ M.update_cwd_head = void(function()
|
|||
end
|
||||
|
||||
if not head or not gitdir then
|
||||
_, gitdir, head = git.get_repo_info(cwd)
|
||||
local info = git.get_repo_info(cwd)
|
||||
gitdir = info.gitdir
|
||||
head = info.abbrev_head
|
||||
end
|
||||
|
||||
scheduler()
|
||||
|
@ -409,7 +411,7 @@ M.update_cwd_head = void(function()
|
|||
end
|
||||
dprint('Git cwd dir update')
|
||||
|
||||
local _, _, new_head = git.get_repo_info(cwd)
|
||||
local new_head = git.get_repo_info(cwd).abbrev_head
|
||||
scheduler()
|
||||
update_cwd_head_var(new_head)
|
||||
end))
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
local async = require('gitsigns.async')
|
||||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
|
||||
|
@ -153,6 +154,37 @@ local function on_detach(_, bufnr: integer)
|
|||
M.detach(bufnr, true)
|
||||
end
|
||||
|
||||
local function on_attach_pre(bufnr: integer): string, string
|
||||
local gitdir, toplevel: string, string
|
||||
if config._on_attach_pre then
|
||||
local res: any = async.wrap(config._on_attach_pre, 2)(bufnr)
|
||||
dprintf('ran on_attach_pre with result %s', vim.inspect(res))
|
||||
if res is table then
|
||||
if type(res.gitdir) == 'string' then
|
||||
gitdir = res.gitdir as string
|
||||
end
|
||||
if type(res.toplevel) == 'string' then
|
||||
toplevel = res.toplevel as string
|
||||
end
|
||||
end
|
||||
end
|
||||
return gitdir, toplevel
|
||||
end
|
||||
|
||||
local function try_worktrees(_bufnr: integer, file: string, encoding: string): git.Obj
|
||||
if not config.worktrees then
|
||||
return
|
||||
end
|
||||
|
||||
for _, wt in ipairs(config.worktrees) do
|
||||
local git_obj = git.Obj.new(file, encoding, wt.gitdir, wt.toplevel)
|
||||
if git_obj and git_obj.object_name then
|
||||
dprintf('Using worktree %s', vim.inspect(wt))
|
||||
return git_obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure attaches cannot be interleaved.
|
||||
-- Since attaches are asynchronous we need to make sure an attach isn't
|
||||
-- performed whilst another one is in progress.
|
||||
|
@ -190,6 +222,7 @@ local attach_throttled = throttle_by_id(function(cbuf: integer, aucmd: string)
|
|||
end
|
||||
|
||||
local file, commit = get_buf_path(cbuf)
|
||||
local encoding = vim.bo[cbuf].fileencoding
|
||||
|
||||
local file_dir = util.dirname(file)
|
||||
|
||||
|
@ -198,7 +231,14 @@ local attach_throttled = throttle_by_id(function(cbuf: integer, aucmd: string)
|
|||
return
|
||||
end
|
||||
|
||||
local git_obj = git.Obj.new(file, vim.bo[cbuf].fileencoding)
|
||||
local gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
|
||||
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
|
||||
|
||||
if not git_obj then
|
||||
git_obj = try_worktrees(cbuf, file, encoding)
|
||||
scheduler()
|
||||
end
|
||||
|
||||
if not git_obj then
|
||||
dprint('Empty git obj')
|
||||
return
|
||||
|
|
|
@ -92,6 +92,7 @@ end
|
|||
---@param argc number: The number of arguments of func. Must be included.
|
||||
---@return function: Returns an async function
|
||||
function M.wrap(func: function, argc: integer): function
|
||||
assert(argc)
|
||||
return function(...): any...
|
||||
if not async_thread.running() then
|
||||
-- print(debug.traceback('Warning: calling async function in non-async context', 2))
|
||||
|
|
|
@ -58,6 +58,7 @@ local record M
|
|||
show_deleted: boolean
|
||||
sign_priority: integer
|
||||
keymaps: {string:any}
|
||||
_on_attach_pre: function(bufnr: integer, callback: function(table))
|
||||
on_attach: function(bufnr: integer)
|
||||
record watch_gitdir
|
||||
interval: integer
|
||||
|
@ -100,6 +101,12 @@ local record M
|
|||
|
||||
trouble: boolean
|
||||
|
||||
record Worktree
|
||||
toplevel: string
|
||||
gitdir: string
|
||||
end
|
||||
worktrees: {Worktree}
|
||||
|
||||
-- Undocumented
|
||||
word_diff: boolean
|
||||
_refresh_staged_on_update: boolean
|
||||
|
@ -175,6 +182,49 @@ M.schema = {
|
|||
]]
|
||||
},
|
||||
|
||||
worktrees = {
|
||||
type = 'table',
|
||||
default = nil,
|
||||
description = [[
|
||||
Detached working trees.
|
||||
|
||||
Array of tables with the keys `gitdir` and `toplevel`.
|
||||
|
||||
If normal attaching fails, then each entry in the table is attempted
|
||||
with the work tree details set.
|
||||
|
||||
Example: >
|
||||
worktrees = {
|
||||
{
|
||||
toplevel = vim.env.HOME,
|
||||
gitdir = vim.env.HOME .. '/projects/dotfiles/.git'
|
||||
}
|
||||
}
|
||||
]]
|
||||
},
|
||||
|
||||
_on_attach_pre = {
|
||||
type = 'function',
|
||||
default = nil,
|
||||
description = [[
|
||||
Asynchronous hook called before attaching to a buffer. Mainly used to
|
||||
configure detached worktrees.
|
||||
|
||||
This callback must call its callback argument. The callback argument can
|
||||
accept an optional table argument with the keys: 'gitdir' and 'toplevel'.
|
||||
|
||||
Example: >
|
||||
on_attach_pre = function(bufnr, callback)
|
||||
...
|
||||
callback {
|
||||
gitdir = ...,
|
||||
toplevel = ...
|
||||
}
|
||||
end
|
||||
<
|
||||
]]
|
||||
},
|
||||
|
||||
on_attach = {
|
||||
type = 'function',
|
||||
default = nil,
|
||||
|
|
|
@ -45,6 +45,7 @@ local function get_context(lvl: integer): table
|
|||
ret.name = name0:gsub('(.*)%d+$', '%1')
|
||||
end
|
||||
ret.bufnr = getvarvalue('bufnr', lvl)
|
||||
or getvarvalue('_bufnr', lvl)
|
||||
or getvarvalue('cbuf', lvl)
|
||||
or getvarvalue('buf', lvl)
|
||||
|
||||
|
|
|
@ -57,13 +57,23 @@ local record M
|
|||
enable_yadm: boolean
|
||||
|
||||
set_version: function(string)
|
||||
get_repo_info: function(path: string, cmd: string): string,string,string
|
||||
|
||||
record RepoInfo
|
||||
gitdir: string
|
||||
toplevel: string
|
||||
detached: boolean
|
||||
abbrev_head: string
|
||||
end
|
||||
|
||||
get_repo_info: function(path: string, cmd: string, gitdir: string, toplevel: string): RepoInfo
|
||||
|
||||
command : function(args: {string}, spec: GJobSpec): {string}, string
|
||||
diff : function(file_cmp: string, file_buf: string, indent_heuristic: boolean, diff_algo: string): {string}, string
|
||||
|
||||
record Repo
|
||||
toplevel : string
|
||||
gitdir : string
|
||||
detached : boolean
|
||||
abbrev_head: string
|
||||
username : string
|
||||
|
||||
|
@ -71,7 +81,7 @@ local record M
|
|||
files_changed : function(Repo): {string}
|
||||
get_show_text : function(Repo, string, string): {string}, string
|
||||
update_abbrev_head : function(Repo)
|
||||
new : function(string): Repo
|
||||
new : function(dir: string, gitdir: string, toplevel: string): Repo
|
||||
end
|
||||
|
||||
record FileProps
|
||||
|
@ -97,16 +107,16 @@ local record M
|
|||
encoding : string
|
||||
|
||||
command : function(Obj, {string}, GJobSpec): {string}, string
|
||||
update_file_info : function(Obj, boolean): boolean
|
||||
update_file_info : function(Obj, boolean, silent: boolean): boolean
|
||||
unstage_file : function(Obj, string, string)
|
||||
run_blame : function(Obj, {string}, number, boolean): BlameInfo
|
||||
file_info : function(Obj, string): FileProps
|
||||
file_info : function(Obj, string, silent: boolean): FileProps
|
||||
get_show_text : function(Obj, string): {string}, string
|
||||
ensure_file_in_index : function(Obj)
|
||||
stage_hunks : function(Obj, {Hunk}, boolean)
|
||||
stage_lines : function(Obj, {string})
|
||||
has_moved : function(Obj): string
|
||||
new : function(string, string): Obj
|
||||
new : function(path: string, enc: string, gitdir: string, toplevel: string): Obj
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -242,7 +252,7 @@ local function normalize_path(path: string): string
|
|||
return path
|
||||
end
|
||||
|
||||
M.get_repo_info = function(path: string, cmd: string): string,string,string
|
||||
M.get_repo_info = function(path: string, cmd: string, gitdir: string, toplevel: string): M.RepoInfo
|
||||
-- Does git rev-parse have --absolute-git-dir, added in 2.13:
|
||||
-- https://public-inbox.org/git/20170203024829.8071-16-szeder.dev@gmail.com/
|
||||
local has_abs_gd = check_version{2,13}
|
||||
|
@ -252,21 +262,36 @@ M.get_repo_info = function(path: string, cmd: string): string,string,string
|
|||
-- https://github.com/lewis6991/gitsigns.nvim/pull/215
|
||||
scheduler()
|
||||
|
||||
local results = M.command({
|
||||
local args = {}
|
||||
|
||||
if gitdir then
|
||||
vim.list_extend(args, {'--git-dir', gitdir})
|
||||
end
|
||||
|
||||
if toplevel then
|
||||
vim.list_extend(args, {'--work-tree', toplevel})
|
||||
end
|
||||
|
||||
vim.list_extend(args, {
|
||||
'rev-parse', '--show-toplevel', git_dir_opt, '--abbrev-ref', 'HEAD',
|
||||
}, {
|
||||
})
|
||||
|
||||
local results = M.command(args, {
|
||||
command = cmd or 'git',
|
||||
supress_stderr = true,
|
||||
cwd = path
|
||||
})
|
||||
|
||||
local toplevel = normalize_path(results[1])
|
||||
local gitdir = normalize_path(results[2])
|
||||
if gitdir and not has_abs_gd then
|
||||
gitdir = uv.fs_realpath(gitdir)
|
||||
local ret: M.RepoInfo = {
|
||||
toplevel = normalize_path(results[1]),
|
||||
gitdir = normalize_path(results[2]),
|
||||
}
|
||||
ret.abbrev_head = process_abbrev_head(ret.gitdir, results[3], path, cmd)
|
||||
if ret.gitdir and not has_abs_gd then
|
||||
ret.gitdir = uv.fs_realpath(ret.gitdir)
|
||||
end
|
||||
local abbrev_head = process_abbrev_head(gitdir, results[3], path, cmd)
|
||||
return toplevel, gitdir, abbrev_head
|
||||
ret.detached = ret.toplevel and ret.gitdir ~= ret.toplevel..'/.git'
|
||||
return ret
|
||||
end
|
||||
|
||||
M.set_version = function(version: string)
|
||||
|
@ -289,7 +314,18 @@ end
|
|||
Repo.command = function(self: Repo, args: {string}, spec: GJobSpec): {string}, string
|
||||
spec = spec or {}
|
||||
spec.cwd = self.toplevel
|
||||
return M.command({'--git-dir='..self.gitdir, unpack(args)}, spec)
|
||||
|
||||
local args1 = {
|
||||
'--git-dir', self.gitdir,
|
||||
}
|
||||
|
||||
if self.detached then
|
||||
vim.list_extend(args1, {'--work-tree', self.toplevel})
|
||||
end
|
||||
|
||||
vim.list_extend(args1, args)
|
||||
|
||||
return M.command(args1, spec)
|
||||
end
|
||||
|
||||
Repo.files_changed = function(self: Repo): {string}
|
||||
|
@ -322,21 +358,27 @@ Repo.get_show_text = function(self: Repo, object: string, encoding: string): {st
|
|||
end
|
||||
|
||||
Repo.update_abbrev_head = function(self: Repo)
|
||||
_, _, self.abbrev_head = M.get_repo_info(self.toplevel)
|
||||
self.abbrev_head = M.get_repo_info(self.toplevel).abbrev_head
|
||||
end
|
||||
|
||||
Repo.new = function(dir: string): Repo
|
||||
Repo.new = function(dir: string, gitdir: string, toplevel: string): Repo
|
||||
local self = setmetatable({} as Repo, {__index = Repo})
|
||||
|
||||
self.username = M.command({'config', 'user.name'})[1]
|
||||
self.toplevel, self.gitdir, self.abbrev_head = M.get_repo_info(dir)
|
||||
local info = M.get_repo_info(dir, nil, gitdir, toplevel)
|
||||
for k, v in pairs(info as {string:any}) do
|
||||
(self as table)[k] = v
|
||||
end
|
||||
|
||||
-- Try yadm
|
||||
if M.enable_yadm and not self.gitdir then
|
||||
if vim.startswith(dir, os.getenv('HOME'))
|
||||
and #M.command({'ls-files', dir}, {command = 'yadm'}) ~= 0 then
|
||||
self.toplevel, self.gitdir, self.abbrev_head =
|
||||
M.get_repo_info(dir, 'yadm')
|
||||
M.get_repo_info(dir, 'yadm', gitdir, toplevel)
|
||||
local yadm_info = M.get_repo_info(dir, 'yadm', gitdir, toplevel)
|
||||
for k, v in pairs(yadm_info as {string:any}) do
|
||||
(self as table)[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -352,9 +394,9 @@ Obj.command = function(self: Obj, args: {string}, spec: GJobSpec): {string}, str
|
|||
return self.repo:command(args, spec)
|
||||
end
|
||||
|
||||
Obj.update_file_info = function(self: Obj, update_relpath: boolean): boolean
|
||||
Obj.update_file_info = function(self: Obj, update_relpath: boolean, silent: boolean): boolean
|
||||
local old_object_name = self.object_name
|
||||
local props = self:file_info()
|
||||
local props = self:file_info(self.file, silent)
|
||||
|
||||
if update_relpath then
|
||||
self.relpath = props.relpath
|
||||
|
@ -368,7 +410,7 @@ Obj.update_file_info = function(self: Obj, update_relpath: boolean): boolean
|
|||
return old_object_name ~= self.object_name
|
||||
end
|
||||
|
||||
Obj.file_info = function(self: Obj, file: string): M.FileProps
|
||||
Obj.file_info = function(self: Obj, file: string, silent: boolean): M.FileProps
|
||||
local results, stderr = self:command({
|
||||
'-c', 'core.quotepath=off',
|
||||
'ls-files',
|
||||
|
@ -379,7 +421,7 @@ Obj.file_info = function(self: Obj, file: string): M.FileProps
|
|||
file or self.file
|
||||
}, {supress_stderr = true})
|
||||
|
||||
if stderr then
|
||||
if stderr and not silent then
|
||||
-- Supress_stderr for the cases when we run:
|
||||
-- git ls-files --others exists/nonexist
|
||||
if not stderr:match('^warning: could not open directory .*: No such file or directory') then
|
||||
|
@ -540,7 +582,7 @@ Obj.has_moved = function(self: Obj): string
|
|||
end
|
||||
end
|
||||
|
||||
Obj.new = function(file: string, encoding: string): Obj
|
||||
Obj.new = function(file: string, encoding: string, gitdir: string, toplevel: string): Obj
|
||||
if in_git_dir(file) then
|
||||
dprint('In git dir')
|
||||
return nil
|
||||
|
@ -549,14 +591,17 @@ Obj.new = function(file: string, encoding: string): Obj
|
|||
|
||||
self.file = file
|
||||
self.encoding = encoding
|
||||
self.repo = Repo.new(util.dirname(file))
|
||||
self.repo = Repo.new(util.dirname(file), gitdir, toplevel)
|
||||
|
||||
if not self.repo.gitdir then
|
||||
dprint('Not in git repo')
|
||||
return nil
|
||||
end
|
||||
|
||||
self:update_file_info(true)
|
||||
-- When passing gitdir and toplevel, suppress stderr when resolving the file
|
||||
local silent = gitdir ~= nil and toplevel ~= nil
|
||||
|
||||
self:update_file_info(true, silent)
|
||||
|
||||
return self
|
||||
end
|
||||
|
|
|
@ -380,7 +380,9 @@ M.update_cwd_head = void(function()
|
|||
end
|
||||
|
||||
if not head or not gitdir then
|
||||
_, gitdir, head = git.get_repo_info(cwd)
|
||||
local info = git.get_repo_info(cwd)
|
||||
gitdir = info.gitdir
|
||||
head = info.abbrev_head
|
||||
end
|
||||
|
||||
scheduler()
|
||||
|
@ -409,7 +411,7 @@ M.update_cwd_head = void(function()
|
|||
end
|
||||
dprint('Git cwd dir update')
|
||||
|
||||
local _, _, new_head = git.get_repo_info(cwd)
|
||||
local new_head = git.get_repo_info(cwd).abbrev_head
|
||||
scheduler()
|
||||
update_cwd_head_var(new_head)
|
||||
end)
|
||||
|
|
|
@ -292,6 +292,11 @@ describe('gitsigns', function()
|
|||
end
|
||||
|
||||
it('doesn\'t error on untracked files', function()
|
||||
local nvim_ver = exec_lua('return vim.version().minor')
|
||||
if nvim_ver >= 8 then
|
||||
pending()
|
||||
end
|
||||
|
||||
setup_test_repo{no_add=true}
|
||||
edit(newfile)
|
||||
insert("line")
|
||||
|
@ -341,7 +346,7 @@ describe('gitsigns', function()
|
|||
p'run_job: git .* config user.name',
|
||||
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
|
||||
p'run_job: git .* rev%-parse %-%-short HEAD',
|
||||
p'run_job: git .* %-%-git%-dir=.* %-%-stage %-%-others %-%-exclude%-standard %-%-eol.*',
|
||||
p'run_job: git .* %-%-git%-dir .* %-%-stage %-%-others %-%-exclude%-standard %-%-eol.*',
|
||||
'attach(1): User on_attach() returned false',
|
||||
}
|
||||
end)
|
||||
|
|
|
@ -142,11 +142,11 @@ function M.match_lines(lines, spec)
|
|||
end
|
||||
end
|
||||
if i < #spec + 1 then
|
||||
-- print('Lines:')
|
||||
-- for _, l in ipairs(lines) do
|
||||
-- print(string.format( '"%s"', l))
|
||||
-- end
|
||||
error(('Did not match pattern \'%s\''):format(spec[i]))
|
||||
local msg = {'lines:'}
|
||||
for _, l in ipairs(lines) do
|
||||
msg[#msg+1] = string.format( '"%s"', l)
|
||||
end
|
||||
error(('Did not match pattern \'%s\' with %s'):format(spec[i], table.concat(msg, '\n')))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -173,11 +173,18 @@ local function match_lines2(lines, spec)
|
|||
end
|
||||
|
||||
if i < #spec + 1 then
|
||||
local unmatched = {}
|
||||
for j = i, #spec do
|
||||
table.insert(unmatched, spec[j].text or spec[j])
|
||||
end
|
||||
error(('Did not match patterns:\n - %s'):format(table.concat(unmatched, '\n - ')))
|
||||
local unmatched_msg = table.concat(helpers.tbl_map(function(v)
|
||||
return string.format(' - %s', v.text or v)
|
||||
end, spec), '\n')
|
||||
|
||||
local lines_msg = table.concat(helpers.tbl_map(function(v)
|
||||
return string.format(' - %s', v)
|
||||
end, lines), '\n')
|
||||
|
||||
error(('Did not match patterns:\n%s\nwith:\n%s'):format(
|
||||
unmatched_msg,
|
||||
lines_msg
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue