feat: add staging and update locks

This commit is contained in:
Lewis Russell 2024-08-07 10:44:34 +01:00 committed by Lewis Russell
parent 58bd9e98d8
commit e6e3c3a139
5 changed files with 71 additions and 11 deletions

View file

@ -275,6 +275,11 @@ M.stage_hunk = mk_repeatable(async.create(2, function(range, opts)
return
end
if bcache:locked() then
print('Error: busy')
return
end
if not util.path_exists(bcache.file) then
print('Error: Cannot stage lines. Please add the file to the working tree.')
return
@ -294,7 +299,6 @@ M.stage_hunk = mk_repeatable(async.create(2, function(range, opts)
end
bcache.git_obj:stage_hunks({ hunk }, invert)
table.insert(bcache.staged_diffs, hunk)
bcache:invalidate(true)
@ -397,6 +401,11 @@ M.undo_stage_hunk = async.create(function()
return
end
if bcache:locked() then
print('Error: busy')
return
end
local hunk = table.remove(bcache.staged_diffs)
if not hunk then
print('No hunks to undo')
@ -419,6 +428,11 @@ M.stage_buffer = async.create(function()
return
end
if bcache:locked() then
print('Error: busy')
return
end
-- Only process files with existing hunks
local hunks = bcache.hunks
if not hunks or #hunks == 0 then
@ -454,6 +468,11 @@ M.reset_buffer_index = async.create(function()
return
end
if bcache:locked() then
print('Error: busy')
return
end
-- `bcache.staged_diffs` won't contain staged changes outside of current
-- neovim session so signs added from this unstage won't be complete They will
-- however be fixed by gitdir watcher and properly updated We should implement
@ -463,7 +482,6 @@ M.reset_buffer_index = async.create(function()
bcache.staged_diffs = {}
bcache.git_obj:unstage_file()
bcache:invalidate(true)
update(bufnr)
end)

View file

@ -20,6 +20,8 @@ local M = {
--- @field gitdir_watcher? uv.uv_fs_event_t
--- @field git_obj Gitsigns.GitObj
--- @field blame? table<integer,Gitsigns.BlameInfo?>
---
--- @field update_lock? true Update in progress
local CacheEntry = M.CacheEntry
function CacheEntry:get_rev_bufname(rev, nofile)
@ -30,6 +32,10 @@ function CacheEntry:get_rev_bufname(rev, nofile)
return string.format('gitsigns://%s//%s:%s', self.git_obj.repo.gitdir, rev, self.git_obj.relpath)
end
function CacheEntry:locked()
return self.git_obj.lock or self.update_lock or false
end
--- Invalidate any state dependent on the buffer content.
--- If 'all' is passed, then invalidate everything.
--- @param all? boolean

View file

@ -1,4 +1,5 @@
local log = require('gitsigns.debug.log')
local async = require('gitsigns.async')
local util = require('gitsigns.util')
local Repo = require('gitsigns.git.repo')
@ -31,6 +32,8 @@ end
--- @field orig_relpath? string Use for tracking moved files
--- @field repo Gitsigns.Repo
--- @field has_conflicts? boolean
---
--- @field lock? true
local Obj = {}
M.Obj = Obj
@ -61,12 +64,14 @@ function M.diff(file_cmp, file_buf, indent_heuristic, diff_algo)
})
end
--- @async
--- @param revision? string
function Obj:update_revision(revision)
self.revision = util.norm_base(revision)
self:update()
end
--- @async
--- @param update_relpath? boolean
--- @param silent? boolean
--- @return boolean
@ -98,6 +103,7 @@ function Obj:from_tree()
return self.revision and not vim.startswith(self.revision, ':')
end
--- @async
--- @param file? string
--- @param silent? boolean
--- @return Gitsigns.FileInfo
@ -110,6 +116,7 @@ function Obj:file_info(file, silent)
end
--- @private
--- @async
--- Get information about files in the index and the working tree
--- @param file? string
--- @param silent? boolean
@ -177,6 +184,7 @@ function Obj:file_info_index(file, silent)
end
--- @private
--- @async
--- Get information about files in a certain revision
--- @param file? string
--- @param silent? boolean
@ -213,6 +221,7 @@ function Obj:file_info_tree(file, silent)
}
end
--- @async
--- @param revision? string
--- @return string[] stdout, string? stderr
function Obj:get_show_text(revision)
@ -251,11 +260,15 @@ local function autocmd_changed(file)
end)
end
--- @async
function Obj:unstage_file()
self.lock = true
self.repo:command({ 'reset', self.file })
self.lock = nil
autocmd_changed(self.file)
end
--- @async
--- @param lines string[]
--- @param lnum? integer
--- @param revision? string
@ -265,28 +278,33 @@ function Obj:run_blame(lines, lnum, revision, opts)
return require('gitsigns.git.blame').run_blame(self, lines, lnum, revision, opts)
end
--- @param obj Gitsigns.GitObj
local function ensure_file_in_index(obj)
if obj.object_name and not obj.has_conflicts then
--- @async
--- @private
function Obj:ensure_file_in_index()
self.lock = true
if self.object_name and not self.has_conflicts then
return
end
if not obj.object_name then
if not self.object_name then
-- If there is no object_name then it is not yet in the index so add it
obj.repo:command({ 'add', '--intent-to-add', obj.file })
self.repo:command({ 'add', '--intent-to-add', self.file })
else
-- Update the index with the common ancestor (stage 1) which is what bcache
-- stores
local info = string.format('%s,%s,%s', obj.mode_bits, obj.object_name, obj.relpath)
obj.repo:command({ 'update-index', '--add', '--cacheinfo', info })
local info = string.format('%s,%s,%s', self.mode_bits, self.object_name, self.relpath)
self.repo:command({ 'update-index', '--add', '--cacheinfo', info })
end
obj:update()
self:update()
self.lock = nil
end
--- @async
--- Stage 'lines' as the entire contents of the file
--- @param lines string[]
function Obj:stage_lines(lines)
self.lock = true
local new_object = self.repo:command({
'hash-object',
'-w',
@ -301,13 +319,20 @@ function Obj:stage_lines(lines)
string.format('%s,%s,%s', self.mode_bits, new_object, self.relpath),
})
self.lock = nil
autocmd_changed(self.file)
end
local sleep = async.wrap(2, function(duration, cb)
vim.defer_fn(cb, duration)
end)
--- @async
--- @param hunks Gitsigns.Hunk.Hunk[]
--- @param invert? boolean
function Obj:stage_hunks(hunks, invert)
ensure_file_in_index(self)
self.lock = true
self:ensure_file_in_index()
local patch = require('gitsigns.hunks').create_patch(self.relpath, hunks, self.mode_bits, invert)
@ -328,9 +353,15 @@ function Obj:stage_hunks(hunks, invert)
stdin = patch,
})
-- Staging operations cause IO of the git directory so wait some time
-- for the changes to settle.
sleep(100)
self.lock = nil
autocmd_changed(self.file)
end
--- @async
--- @return string?
function Obj:has_moved()
local out = self.repo:command({ 'diff', '--name-status', '-C', '--cached' })
@ -349,6 +380,7 @@ function Obj:has_moved()
end
end
--- @async
--- @param file string
--- @param revision string?
--- @param encoding string

View file

@ -81,6 +81,7 @@ local function iconv_supported(encoding)
return true
end
--- @async
--- Get version of file in the index, return array lines
--- @param object string
--- @param encoding? string

View file

@ -448,6 +448,8 @@ M.update = throttle_by_id(function(bufnr)
return
end
local bcache = assert(cache[bufnr])
bcache.update_lock = true
local old_hunks, old_hunks_staged = bcache.hunks, bcache.hunks_staged
bcache.hunks, bcache.hunks_staged = nil, nil
@ -505,6 +507,7 @@ M.update = throttle_by_id(function(bufnr)
summary.head = git_obj.repo.abbrev_head
Status:update(bufnr, summary)
end
bcache.update_lock = nil
end, true)
--- @param bufnr integer