Skip to content
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Fixed

- `X` on a staged rename overwrote a file recreated at the old path with its `HEAD` content. It now undoes the rename and leaves that file as it is

## [0.2.1] — 2026-10-09

### Added

- The diff view highlights code embedded in another language with that language's parser, not only the file's own grammar
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ PANDOC_VERSION := 3.10.1
PANDOC_DIR := .tools/pandoc-$(PANDOC_VERSION)
PANDOC_BIN := $(PANDOC_DIR)/bin/pandoc

GOLANGCI_VERSION := 2.13.1
GOLANGCI_VERSION := 2.14.0
GOLANGCI_DIR := .tools/golangci-lint-$(GOLANGCI_VERSION)
GOLANGCI_BIN := $(GOLANGCI_DIR)/golangci-lint

Expand Down
14 changes: 10 additions & 4 deletions lua/differ/git/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -989,10 +989,16 @@ function M.discard(root, entry, preview)
-- untouched, so its new path is an add
ok = git_ok({ "reset", "-q", "HEAD", "--", entry.path }, root, "discard") and remove_ok(abs)
elseif kind == "R" and entry.previous_path then
-- undoing a move: the old path comes back from HEAD, the new one goes like an add
ok = git_ok({ "checkout", "HEAD", "--", entry.previous_path }, root, "discard")
and git_ok({ "reset", "-q", "HEAD", "--", entry.path }, root, "discard")
and remove_ok(abs)
-- undoing a move: the old path comes back from HEAD, the new one goes like an add.
-- a file since recreated at the old path stays as it is, against HEAD in the index
if (vim.uv or vim.loop).fs_lstat(root .. "/" .. entry.previous_path) then
local unstage = { "reset", "-q", "HEAD", "--", entry.previous_path, entry.path }
ok = git_ok(unstage, root, "discard") and remove_ok(abs)
else
ok = git_ok({ "checkout", "HEAD", "--", entry.previous_path }, root, "discard")
and git_ok({ "reset", "-q", "HEAD", "--", entry.path }, root, "discard")
and remove_ok(abs)
end
else
ok = git_ok({ "checkout", "HEAD", "--", entry.path }, root, "discard") -- index + worktree
end
Expand Down
36 changes: 36 additions & 0 deletions test/nvim/git_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7537,6 +7537,22 @@ describe(":Differ diff rename staging", function()
assert.are.equal("R old/big.lua -> new/big.lua\n", status)
end)

it("leaves a file back at a staged rename's old path untracked on s", function()
local root = renamed_repo({ 5 })
write(root .. "/old/big.lua", "new file\n")
vim.cmd.edit(root .. "/new/big.lua")

git_src.panel({ rev = {} })
local p = Panel.current()
assert.is_true(p:focus_file("new/big.lua"))
p:stage_op("stage")

local status = git(root, "status", "--porcelain=v1", "-uall")
p:close()
assert.are.equal("R old/big.lua -> new/big.lua\n?? old/big.lua\n", status)
assert.are.same({ "new file" }, vim.fn.readfile(root .. "/old/big.lua"))
end)

it("unstages both of a rename's paths from the panel row, as the diff keys do", function()
local root = renamed_repo({ 5 })
vim.cmd.edit(root .. "/new/big.lua")
Expand Down Expand Up @@ -7572,6 +7588,26 @@ describe(":Differ diff rename staging", function()
p:close()
end)

it("undoes a rename with X in the index only when a file is back at the old path", function()
local root = renamed_repo({ 5 })
write(root .. "/old/big.lua", "new file\n")
vim.cmd.edit(root .. "/new/big.lua")

git_src.panel({ rev = {} })
local p = Panel.current()
assert.is_true(p:focus_file("new/big.lua"))

confirming(1, function()
p:discard()
end)

local status = git(root, "status", "--porcelain=v1")
p:close()
assert.are.equal(" M old/big.lua\n", status)
assert.are.same({ "new file" }, vim.fn.readfile(root .. "/old/big.lua"))
assert.are.equal(0, vim.fn.filereadable(root .. "/new/big.lua"))
end)

it("names the path X will restore in its confirm", function()
local root = renamed_repo()
vim.cmd.edit(root .. "/new/big.lua")
Expand Down
Loading