Skip to content

Revert "Add ability to revert hunk" #1611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lua/neogit/buffers/commit_view/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ function M:open(kind)
end),
[popups.mapping_for("RemotePopup")] = popups.open("remote"),
[popups.mapping_for("RevertPopup")] = popups.open("revert", function(p)
p { commits = { self.commit_info.oid }, item = self.buffer.ui:get_hunk_or_filename_under_cursor() }
p { commits = { self.commit_info.oid } }
end),
[popups.mapping_for("ResetPopup")] = popups.open("reset", function(p)
p { commit = self.commit_info.oid }
Expand Down
25 changes: 10 additions & 15 deletions lua/neogit/buffers/status/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ M.v_discard = function(self)
for _, hunk in ipairs(hunks) do
table.insert(invalidated_diffs, "*:" .. item.name)
table.insert(patches, function()
local patch =
git.index.generate_patch(hunk, { from = hunk.from, to = hunk.to, reverse = true })
local patch = git.index.generate_patch(item, hunk, hunk.from, hunk.to, true)

logger.debug(("Discarding Patch: %s"):format(patch))

Expand Down Expand Up @@ -232,7 +231,7 @@ M.v_stage = function(self)

if #hunks > 0 then
for _, hunk in ipairs(hunks) do
table.insert(patches, git.index.generate_patch(hunk.hunk, { from = hunk.from, to = hunk.to }))
table.insert(patches, git.index.generate_patch(item, hunk, hunk.from, hunk.to))
end
else
if section.name == "unstaged" then
Expand Down Expand Up @@ -282,10 +281,7 @@ M.v_unstage = function(self)

if #hunks > 0 then
for _, hunk in ipairs(hunks) do
table.insert(
patches,
git.index.generate_patch(hunk, { from = hunk.from, to = hunk.to, reverse = true })
)
table.insert(patches, git.index.generate_patch(item, hunk, hunk.from, hunk.to, true))
end
else
table.insert(files, item.escaped_path)
Expand Down Expand Up @@ -785,16 +781,17 @@ M.n_discard = function(self)
local hunk =
self.buffer.ui:item_hunks(selection.item, selection.first_line, selection.last_line, false)[1]

local patch = git.index.generate_patch(hunk, { from = hunk.from, to = hunk.to, reverse = true })
local patch = git.index.generate_patch(selection.item, hunk, hunk.from, hunk.to, true)

if section == "untracked" then
message = "Discard hunk?"
action = function()
local hunks =
self.buffer.ui:item_hunks(selection.item, selection.first_line, selection.last_line, false)

local patch =
git.index.generate_patch(hunks[1], { from = hunks[1].from, to = hunks[1].to, reverse = true })
local patch = git.index.generate_patch(selection.item, hunks[1], hunks[1].from, hunks[1].to, true)

git.index.apply(patch, { reverse = true })
git.index.apply(patch, { reverse = true })
end
refresh = { update_diffs = { "untracked:" .. selection.item.name } }
Expand Down Expand Up @@ -1095,7 +1092,7 @@ M.n_stage = function(self)
local item = self.buffer.ui:get_item_under_cursor()
assert(item, "Item cannot be nil")

local patch = git.index.generate_patch(stagable.hunk)
local patch = git.index.generate_patch(item, stagable.hunk, stagable.hunk.from, stagable.hunk.to)
git.index.apply(patch, { cached = true })
self:dispatch_refresh({ update_diffs = { "*:" .. item.escaped_path } }, "n_stage")
elseif stagable.filename then
Expand Down Expand Up @@ -1169,10 +1166,8 @@ M.n_unstage = function(self)
if unstagable.hunk then
local item = self.buffer.ui:get_item_under_cursor()
assert(item, "Item cannot be nil")
local patch = git.index.generate_patch(
unstagable.hunk,
{ from = unstagable.hunk.from, to = unstagable.hunk.to, reverse = true }
)
local patch =
git.index.generate_patch(item, unstagable.hunk, unstagable.hunk.from, unstagable.hunk.to, true)

git.index.apply(patch, { cached = true, reverse = true })
self:dispatch_refresh({ update_diffs = { "*:" .. item.escaped_path } }, "n_unstage")
Expand Down
7 changes: 0 additions & 7 deletions lua/neogit/lib/git/diff.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@ local sha256 = vim.fn.sha256
---@field deletions number
---
---@class Hunk
---@field file string
---@field index_from number
---@field index_len number
---@field diff_from number
---@field diff_to number
---@field first number First line number in buffer
---@field last number Last line number in buffer
---@field lines string[]
---
---@class DiffStagedStats
---@field summary string
Expand Down Expand Up @@ -226,11 +224,6 @@ local function parse_diff(raw_diff, raw_stats)
local file = build_file(header, kind)
local stats = parse_diff_stats(raw_stats or {})

util.map(hunks, function(hunk)
hunk.file = file
return hunk
end)

return { ---@type Diff
kind = kind,
lines = lines,
Expand Down
36 changes: 23 additions & 13 deletions lua/neogit/lib/git/index.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ local util = require("neogit.lib.util")
local M = {}

---Generates a patch that can be applied to index
---@param item any
---@param hunk Hunk
---@param opts table|nil
---@param from number
---@param to number
---@param reverse boolean|nil
---@return string
function M.generate_patch(hunk, opts)
opts = opts or { reverse = false, cached = false, index = false }
local reverse = opts.reverse
function M.generate_patch(item, hunk, from, to, reverse)
reverse = reverse or false

local from = opts.from or 1
local to = opts.to or (hunk.diff_to - hunk.diff_from)
if not from and not to then
from = hunk.diff_from + 1
to = hunk.diff_to
end

assert(from <= to, string.format("from must be less than or equal to to %d %d", from, to))
if from > to then
Expand All @@ -25,31 +29,35 @@ function M.generate_patch(hunk, opts)
local len_start = hunk.index_len
local len_offset = 0

for k, line in pairs(hunk.lines) do
local operand, l = line:match("^([+ -])(.*)")
-- + 1 skips the hunk header, since we construct that manually afterwards
-- TODO: could use `hunk.lines` instead if this is only called with the `SelectedHunk` type
for k = hunk.diff_from + 1, hunk.diff_to do
local v = item.diff.lines[k]
local operand, line = v:match("^([+ -])(.*)")

if operand == "+" or operand == "-" then
if from <= k and k <= to then
len_offset = len_offset + (operand == "+" and 1 or -1)
table.insert(diff_content, line)
table.insert(diff_content, v)
else
-- If we want to apply the patch normally, we need to include every `-` line we skip as a normal line,
-- since we want to keep that line.
if not reverse then
if operand == "-" then
table.insert(diff_content, " " .. l)
table.insert(diff_content, " " .. line)
end
-- If we want to apply the patch in reverse, we need to include every `+` line we skip as a normal line, since
-- it's unchanged as far as the diff is concerned and should not be reversed.
-- We also need to adapt the original line offset based on if we skip or not
elseif reverse then
if operand == "+" then
table.insert(diff_content, " " .. l)
table.insert(diff_content, " " .. line)
end
len_start = len_start + (operand == "-" and -1 or 1)
end
end
else
table.insert(diff_content, line)
table.insert(diff_content, v)
end
end

Expand All @@ -60,7 +68,9 @@ function M.generate_patch(hunk, opts)
)

local worktree_root = git.repo.worktree_root
local path = Path:new(hunk.file):make_relative(worktree_root)

assert(item.absolute_path, "Item is not a path")
local path = Path:new(item.absolute_path):make_relative(worktree_root)

table.insert(diff_content, 1, string.format("+++ b/%s", path))
table.insert(diff_content, 1, string.format("--- a/%s", path))
Expand Down
5 changes: 0 additions & 5 deletions lua/neogit/lib/git/revert.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ function M.commits(commits, args)
end
end

function M.hunk(hunk, _)
local patch = git.index.generate_patch(hunk, { reverse = true })
git.index.apply(patch, { reverse = true })
end

function M.continue()
git.cli.revert.continue.no_edit.call { pty = true }
end
Expand Down
11 changes: 9 additions & 2 deletions lua/neogit/lib/ui/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,25 @@ function Ui:item_hunks(item, first_line, last_line, partial)

if not item.folded and item.diff.hunks then
for _, h in ipairs(item.diff.hunks) do
if h.first <= first_line and h.last >= last_line then
if h.first <= last_line and h.last >= first_line then
local from, to

if partial then
local cursor_offset = first_line - h.first
local length = last_line - first_line

from = first_line - h.first
from = h.diff_from + cursor_offset
to = from + length
else
from = h.diff_from + 1
to = h.diff_to
end

local hunk_lines = {}
for i = from, to do
table.insert(hunk_lines, item.diff.lines[i])
end

-- local conflict = false
-- for _, n in ipairs(conflict_markers) do
-- if from <= n and n <= to then
Expand All @@ -208,6 +214,7 @@ function Ui:item_hunks(item, first_line, last_line, partial)
to = to,
__index = h,
hunk = h,
lines = hunk_lines,
-- conflict = conflict,
}

Expand Down
4 changes: 0 additions & 4 deletions lua/neogit/popups/revert/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ function M.changes(popup)
end
end

function M.hunk(popup)
git.revert.hunk(popup.state.env.item.hunk, popup:get_arguments())
end

function M.continue()
git.revert.continue()
end
Expand Down
1 change: 0 additions & 1 deletion lua/neogit/popups/revert/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ function M.create(env)
:group_heading("Revert")
:action_if(not in_progress, "v", "Commit(s)", actions.commits)
:action_if(not in_progress, "V", "Changes", actions.changes)
:action_if(((not in_progress) and env.item ~= nil), "h", "Hunk", actions.hunk)
:action_if(in_progress, "v", "continue", actions.continue)
:action_if(in_progress, "s", "skip", actions.skip)
:action_if(in_progress, "a", "abort", actions.abort)
Expand Down
8 changes: 5 additions & 3 deletions tests/specs/neogit/lib/git/index_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ local function run_with_hunk(hunk, from, to, reverse)
local header_matches =
vim.fn.matchlist(lines[1], "@@ -\\(\\d\\+\\),\\(\\d\\+\\) +\\(\\d\\+\\),\\(\\d\\+\\) @@")
return generate_patch_from_selection({
name = "test.txt",
absolute_path = "test.txt",
diff = { lines = lines },
}, {
first = 1,
last = #lines,
index_from = header_matches[2],
index_len = header_matches[3],
diff_from = diff_from,
diff_to = #lines,
lines = vim.list_slice(lines, 2),
file = "test.txt",
}, { from = from, to = to, reverse = reverse })
}, diff_from + from, diff_from + to, reverse)
end

describe("patch creation", function()
Expand Down
3 changes: 0 additions & 3 deletions tests/specs/neogit/lib/git/log_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ describe("lib.git.log.parse", function()
index_from = 692,
index_len = 33,
length = 40,
file = "lua/neogit/status.lua",
line = "@@ -692,33 +692,28 @@ end",
lines = {
" ---@param first_line number",
Expand Down Expand Up @@ -150,7 +149,6 @@ describe("lib.git.log.parse", function()
index_from = 734,
index_len = 14,
length = 15,
file = "lua/neogit/status.lua",
line = "@@ -734,14 +729,10 @@ function M.get_item_hunks(item, first_line, last_line, partial)",
lines = {
" setmetatable(o, o)",
Expand Down Expand Up @@ -292,7 +290,6 @@ describe("lib.git.log.parse", function()
index_len = 7,
length = 9,
line = "@@ -1,7 +1,9 @@",
file = "LICENSE",
lines = {
" MIT License",
" ",
Expand Down
Loading