Skip to content

Commit

Permalink
refactor: break up git.lua
Browse files Browse the repository at this point in the history
  • Loading branch information
lewis6991 committed Jul 7, 2024
1 parent aa12bb9 commit 220446c
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 307 deletions.
4 changes: 2 additions & 2 deletions lua/gitsigns.lua
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ local function get_gitdir_and_head()
end
end

local info = require('gitsigns.git').get_repo_info(cwd)
local info = require('gitsigns.git').Repo.get_info(cwd)

return info.gitdir, info.abbrev_head
end
Expand Down Expand Up @@ -89,7 +89,7 @@ local update_cwd_head = async.create(function()
100,
async.create(function()
local git = require('gitsigns.git')
local new_head = git.get_repo_info(cwd).abbrev_head
local new_head = git.Repo.get_info(cwd).abbrev_head
async.scheduler()
vim.g.gitsigns_head = new_head
end)
Expand Down
310 changes: 5 additions & 305 deletions lua/gitsigns/git.lua
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
local async = require('gitsigns.async')
local log = require('gitsigns.debug.log')
local util = require('gitsigns.util')

local system = require('gitsigns.system').system
local scheduler = require('gitsigns.async').scheduler

local uv = vim.uv or vim.loop
local Repo = require('gitsigns.git.repo')

local check_version = require('gitsigns.git.version').check

local M = {}

--- @type fun(cmd: string[], opts?: vim.SystemOpts): vim.SystemCompleted
local asystem = async.wrap(3, system)
M.Repo = Repo

--- @param file string
--- @return boolean
Expand Down Expand Up @@ -41,82 +35,7 @@ local Obj = {}

M.Obj = Obj

--- @class Gitsigns.RepoInfo
--- @field gitdir string
--- @field toplevel string
--- @field detached boolean
--- @field abbrev_head string

--- @class Gitsigns.Repo : Gitsigns.RepoInfo
---
--- Username configured for the repo.
--- Needed for to determine "You" in current line blame.
--- @field username string
local Repo = {}
M.Repo = Repo

--- @class Gitsigns.Git.JobSpec : vim.SystemOpts
--- @field ignore_error? boolean

--- @async
--- @param args string[]
--- @param spec? Gitsigns.Git.JobSpec
--- @return string[] stdout, string? stderr
local function git_command(args, spec)
spec = spec or {}

local cmd = {
'git',
'--no-pager',
'--no-optional-locks',
'--literal-pathspecs',
'-c',
'gc.auto=0', -- Disable auto-packing which emits messages to stderr
unpack(args),
}

if spec.text == nil then
spec.text = true
end

-- Fix #895. Only needed for Nvim 0.9 and older
spec.clear_env = true

--- @type vim.SystemCompleted
local obj = asystem(cmd, spec)

if not spec.ignore_error and obj.code > 0 then
log.eprintf(
"Received exit code %d when running command\n'%s':\n%s",
obj.code,
table.concat(cmd, ' '),
obj.stderr
)
end

local stdout_lines = vim.split(obj.stdout or '', '\n')

if spec.text then
-- If stdout ends with a newline, then remove the final empty string after
-- the split
if stdout_lines[#stdout_lines] == '' then
stdout_lines[#stdout_lines] = nil
end
end

if log.verbose then
log.vprintf('%d lines:', #stdout_lines)
for i = 1, math.min(10, #stdout_lines) do
log.vprintf('\t%s', stdout_lines[i])
end
end

if obj.stderr == '' then
obj.stderr = nil
end

return stdout_lines, obj.stderr
end
local git_command = require('gitsigns.git.cmd')

--- @async
--- @param file_cmp string
Expand All @@ -142,194 +61,6 @@ function M.diff(file_cmp, file_buf, indent_heuristic, diff_algo)
})
end

--- @async
--- @param gitdir? string
--- @param head_str string
--- @param cwd string
--- @return string
local function process_abbrev_head(gitdir, head_str, cwd)
if not gitdir then
return head_str
end
if head_str == 'HEAD' then
local short_sha = git_command({ 'rev-parse', '--short', 'HEAD' }, {
ignore_error = true,
cwd = cwd,
})[1] or ''
if log.debug_mode and short_sha ~= '' then
short_sha = 'HEAD'
end
if
util.path_exists(gitdir .. '/rebase-merge')
or util.path_exists(gitdir .. '/rebase-apply')
then
return short_sha .. '(rebasing)'
end
return short_sha
end
return head_str
end

local has_cygpath = jit and jit.os == 'Windows' and vim.fn.executable('cygpath') == 1

--- @param path? string
--- @return string?
local function normalize_path(path)
if path and has_cygpath and not uv.fs_stat(path) then
-- If on windows and path isn't recognizable as a file, try passing it
-- through cygpath
path = asystem({ 'cygpath', '-aw', path }).stdout
end
return path
end

--- @async
--- @param cwd string
--- @param gitdir? string
--- @param toplevel? string
--- @return Gitsigns.RepoInfo
function M.get_repo_info(cwd, gitdir, toplevel)
-- 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 })

-- Wait for internal scheduler to settle before running command (#215)
scheduler()

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',
has_abs_gd and '--absolute-git-dir' or '--git-dir',
'--abbrev-ref',
'HEAD',
})

local results = git_command(args, {
ignore_error = true,
cwd = toplevel or cwd,
})

local toplevel_r = normalize_path(results[1])
local gitdir_r = normalize_path(results[2])

if gitdir_r and not has_abs_gd then
gitdir_r = assert(uv.fs_realpath(gitdir_r))
end

return {
toplevel = toplevel_r,
gitdir = gitdir_r,
abbrev_head = process_abbrev_head(gitdir_r, results[3], cwd),
detached = toplevel_r and gitdir_r ~= toplevel_r .. '/.git',
}
end

--------------------------------------------------------------------------------
-- Git repo object methods
--------------------------------------------------------------------------------

--- Run git command the with the objects gitdir and toplevel
--- @async
--- @param args string[]
--- @param spec? Gitsigns.Git.JobSpec
--- @return string[] stdout, string? stderr
function Repo:command(args, spec)
spec = spec or {}
spec.cwd = self.toplevel

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 git_command(args1, spec)
end

--- @return string[]
function Repo:files_changed()
--- @type string[]
local results = self:command({ 'status', '--porcelain', '--ignore-submodules' })

local ret = {} --- @type string[]
for _, line in ipairs(results) do
if line:sub(1, 2):match('^.M') then
ret[#ret + 1] = line:sub(4, -1)
end
end
return ret
end

--- @param encoding string
--- @return boolean
local function iconv_supported(encoding)
-- TODO(lewis6991): needs https://github.com/neovim/neovim/pull/21924
if vim.startswith(encoding, 'utf-16') then
return false
elseif vim.startswith(encoding, 'utf-32') then
return false
end
return true
end

--- Get version of file in the index, return array lines
--- @param object string
--- @param encoding? string
--- @return string[] stdout, string? stderr
function Repo:get_show_text(object, encoding)
local stdout, stderr = self:command({ 'show', object }, { text = false, ignore_error = true })

if encoding and encoding ~= 'utf-8' and iconv_supported(encoding) then
for i, l in ipairs(stdout) do
stdout[i] = vim.iconv(l, encoding, 'utf-8')
end
end

return stdout, stderr
end

--- @async
function Repo:update_abbrev_head()
self.abbrev_head = M.get_repo_info(self.toplevel).abbrev_head
end

--- @async
--- @param dir string
--- @param gitdir? string
--- @param toplevel? string
--- @return Gitsigns.Repo
function Repo.new(dir, gitdir, toplevel)
local self = setmetatable({}, { __index = Repo })

local info = M.get_repo_info(dir, gitdir, toplevel)
for k, v in
pairs(info --[[@as table<string,any>]])
do
---@diagnostic disable-next-line:no-unknown
self[k] = v
end

self.username = self:command({ 'config', 'user.name' }, { ignore_error = true })[1]

return self
end

--------------------------------------------------------------------------------
-- Git object methods
--------------------------------------------------------------------------------

--- @param revision? string
function Obj:update_revision(revision)
self.revision = util.norm_base(revision)
Expand Down Expand Up @@ -525,35 +256,6 @@ function Obj:unstage_file()
autocmd_changed(self.file)
end

--- @class Gitsigns.CommitInfo
--- @field author string
--- @field author_mail string
--- @field author_time integer
--- @field author_tz string
--- @field committer string
--- @field committer_mail string
--- @field committer_time integer
--- @field committer_tz string
--- @field summary string
--- @field sha string
--- @field abbrev_sha string
--- @field boundary? true

--- @class Gitsigns.BlameInfoPublic: Gitsigns.BlameInfo, Gitsigns.CommitInfo
--- @field body? string[]
--- @field hunk_no? integer
--- @field num_hunks? integer
--- @field hunk? string[]
--- @field hunk_head? string

--- @class Gitsigns.BlameInfo
--- @field orig_lnum integer
--- @field final_lnum integer
--- @field commit Gitsigns.CommitInfo
--- @field filename string
--- @field previous_filename? string
--- @field previous_sha? string

--- @param lines string[]
--- @param lnum? integer
--- @param revision? string
Expand Down Expand Up @@ -585,15 +287,13 @@ end
--- Stage 'lines' as the entire contents of the file
--- @param lines string[]
function Obj:stage_lines(lines)
local stdout = self.repo:command({
local new_object = self.repo:command({
'hash-object',
'-w',
'--path',
self.relpath,
'--stdin',
}, { stdin = lines })

local new_object = stdout[1]
}, { stdin = lines })[1]

self.repo:command({
'update-index',
Expand Down
Loading

0 comments on commit 220446c

Please sign in to comment.