mirror of
https://github.com/zoriya/jj.nvim.git
synced 2026-08-15 23:53:18 +00:00
feat(picker): Add a conflict picker (#119)
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
|
||||
- [Current Features](#current-features)
|
||||
- [Enhanced Integrations](#enhanced-integrations)
|
||||
- [View change summary from the log buffer](#view-change-summary-from-the-log-buffer)
|
||||
- [View change summary from the log buffer](view-change-summary-from-the-log-buffer)
|
||||
- [Diff any change](#diff-any-change)
|
||||
- [Diff revision history](#diff-revision-history)
|
||||
- [Change the log revset from the log buffer](#change-the-log-revset-from-the-log-buffer)
|
||||
@@ -86,8 +86,9 @@
|
||||
- `:Jdiff [revision]` - Vertical split diff against a jj revision
|
||||
- `:Jhdiff [revision]` - Horizontal split diff
|
||||
- Picker for [Snacks.nvim](https://github.com/folke/snacks.nvim)
|
||||
- `jj status` Displays the current changes diffs
|
||||
- `jj file_history` Displays a buffer's history changes and allows to edit its change (including immutable changes)
|
||||
- `picker.status()` displays the current changed files with live diff preview
|
||||
- `picker.file_history()` displays the current buffer's revision history and lets you edit the selected change
|
||||
- `picker.conflict()` lists conflicted revisions, previews their changes, and launches conflict resolution (with Snacks, or `vim.ui.select()` as a fallback)
|
||||
|
||||
## Enhanced integrations
|
||||
|
||||
@@ -383,6 +384,35 @@ When using the [Snacks.nvim](https://github.com/folke/snacks.nvim) picker integr
|
||||
- `<Enter>` - Open the selected file
|
||||
- `<C-d>` - Open the selected file and run `Jdiff`
|
||||
|
||||
### Conflict picker (Snacks.nvim)
|
||||
|
||||
`picker.conflict()` opens a picker for `jj log -r 'conflicts()'`, so you can resolve conflicted revisions without first navigating to them in the log buffer.
|
||||
|
||||
When Snacks is enabled, it uses the Snacks picker UI. Otherwise, it falls back to a plain `vim.ui.select()` picker with the same conflicted revision list.
|
||||
|
||||
Available actions in the Snacks picker:
|
||||
|
||||
- `<Enter>` - Resolve the selected conflicted revision
|
||||
- `<C-e>` - Run `jj edit` on the selected conflicted revision and refresh changed buffers
|
||||
|
||||
What it shows:
|
||||
|
||||
- The conflicted revision/change id
|
||||
- The author
|
||||
- The first line of the description
|
||||
- A live `jj show --stat --git` preview for the selected conflict
|
||||
|
||||
What happens on confirm:
|
||||
|
||||
- `<Enter>` resolves the selected conflicted revision
|
||||
- If `cmd.resolve_strategies` contains multiple entries, `jj.nvim` prompts you to choose one
|
||||
- The selected strategy's `args` and `external` options are forwarded to `cmd.resolve(...)`
|
||||
- If no strategies are configured, it falls back to the default interactive `jj resolve`
|
||||
|
||||
The fallback `vim.ui.select()` version supports selecting a conflicted revision to resolve, but does not provide the extra Snacks-only `<C-e>` edit action.
|
||||
|
||||
This makes it easy to keep a dedicated “show me all conflicts” picker bound to a keymap, especially when using the Snacks picker UI.
|
||||
|
||||
## Installation
|
||||
|
||||
Using [lazy.nvim](https://github.com/folke/lazy.nvim):
|
||||
@@ -1247,6 +1277,7 @@ vim.keymap.set("n", "<leader>jA", annotate.line, { desc = "JJ annotate line" })
|
||||
local picker = require("jj.picker")
|
||||
vim.keymap.set("n", "<leader>gj", function() picker.status() end, { desc = "JJ Picker status" })
|
||||
vim.keymap.set("n", "<leader>jgh", function() picker.file_history() end, { desc = "JJ Picker history" })
|
||||
vim.keymap.set("n", "<leader>jgc", function() picker.conflict() end, { desc = "JJ Picker conflicts" })
|
||||
|
||||
-- Some functions like `log` can take parameters
|
||||
vim.keymap.set("n", "<leader>jL", function()
|
||||
|
||||
+130
-2
@@ -8,19 +8,32 @@ local parser = require("jj.core.parser")
|
||||
--- @field snacks table|boolean The snacks config
|
||||
|
||||
--- @class jj.picker.file
|
||||
--- @field text string The text to display in the picker
|
||||
--- @field file string The current path of the file
|
||||
--- @field status string JJ-style status code (e.g. "M ", "R ") for picker formatting
|
||||
--- @field rename? string Previous path when this item is a rename
|
||||
--- @field diff_cmd string The command to get the diff of the file
|
||||
--- @field confirm_action string The default picker action for the item
|
||||
|
||||
--- @class jj.picker.log_line
|
||||
--- @field text string The text to display in the picker
|
||||
--- @field symbol string The symbol of the log entry
|
||||
--- @field rev string The revision of the log entry
|
||||
--- @field author string The author of the log entry
|
||||
--- @field time string The time of the log entry
|
||||
--- @field commit_id string The commit id of the log entry
|
||||
--- @field description string The description of the log entry
|
||||
--- @field diff_cmd string The command to get the diff of the file
|
||||
--- @field preview_cmd string[] The command used to preview the item
|
||||
--- @field confirm_action string The default picker action for the item
|
||||
|
||||
--- @class jj.picker.conflict
|
||||
--- @field text string The text to display in the picker
|
||||
--- @field symbol string The symbol of the conflict entry
|
||||
--- @field rev string The revision of the conflict entry
|
||||
--- @field author string The author of the conflict entry
|
||||
--- @field description string The description of the conflict entry
|
||||
--- @field preview_cmd string[] The command used to preview the item
|
||||
--- @field confirm_action string The default picker action for the item
|
||||
|
||||
local M = {
|
||||
--- @type jj.picker.config
|
||||
@@ -29,6 +42,48 @@ local M = {
|
||||
},
|
||||
}
|
||||
|
||||
--- Resolve a conflicted revision using configured strategies.
|
||||
--- @param item jj.picker.conflict|nil
|
||||
--- @param on_exit? fun(exit_code: number)
|
||||
local function resolve_conflict(item, on_exit)
|
||||
if not item or not item.rev then
|
||||
return
|
||||
end
|
||||
|
||||
local strategies = require("jj.cmd").config.resolve_strategies or nil
|
||||
if strategies and #strategies > 1 then
|
||||
vim.ui.select(strategies, {
|
||||
prompt = "Select a strategy to resolve the conflict",
|
||||
format_item = function(choice)
|
||||
return choice.name
|
||||
end,
|
||||
}, function(choice)
|
||||
if not choice then
|
||||
return
|
||||
end
|
||||
|
||||
require("jj.cmd").resolve({
|
||||
rev = item.rev,
|
||||
args = choice.args,
|
||||
external = choice.external,
|
||||
on_exit = on_exit,
|
||||
})
|
||||
end)
|
||||
elseif strategies and #strategies == 1 then
|
||||
local choice = strategies[1]
|
||||
require("jj.cmd").resolve({
|
||||
rev = item.rev,
|
||||
args = choice.args,
|
||||
external = choice.external,
|
||||
on_exit = on_exit,
|
||||
})
|
||||
else
|
||||
require("jj.cmd.resolve").resolve({ rev = item.rev, on_exit = on_exit })
|
||||
end
|
||||
end
|
||||
|
||||
M.resolve_conflict = resolve_conflict
|
||||
|
||||
--- Initializes the picker
|
||||
--- @param opts jj.picker.config
|
||||
function M.setup(opts)
|
||||
@@ -63,6 +118,7 @@ local function get_files()
|
||||
file = file_path,
|
||||
status = change .. " ",
|
||||
diff_cmd = string.format("jj --no-pager diff %s", vim.fn.shellescape(file_path)),
|
||||
confirm_action = "open_and_diff",
|
||||
}
|
||||
|
||||
if change == "R" and file_info.old_path and file_info.old_path ~= file_info.new_path then
|
||||
@@ -146,7 +202,8 @@ local function log_history(file_path)
|
||||
commit_id = commit_id,
|
||||
description = description or "",
|
||||
text = line,
|
||||
diff_cmd = string.format("jj --no-pager diff %s -r %s --stat --git", file_path, rev),
|
||||
preview_cmd = { "jj", "--no-pager", "diff", file_path, "-r", rev, "--stat", "--git" },
|
||||
confirm_action = "edit_revision",
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -166,6 +223,9 @@ function M.file_history()
|
||||
local file = vim.fn.expand("%:p")
|
||||
|
||||
local log_lines = log_history(file)
|
||||
if not log_lines or #log_lines == 0 then
|
||||
return utils.notify("`Picker`: No file history found", vim.log.levels.INFO)
|
||||
end
|
||||
|
||||
if M.config.snacks then
|
||||
require("jj.picker.snacks").file_log_history(M.config, log_lines)
|
||||
@@ -174,4 +234,72 @@ function M.file_history()
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the list of conflicted revisions
|
||||
--- @return jj.picker.conflict[]|nil A list of conflicted revisions or nil if not in a jj repo
|
||||
local function get_conflicts()
|
||||
local cmd =
|
||||
[[jj log -r 'conflicts()' --no-graph -T 'change_id.shortest() ++ "\t" ++ coalesce(author.name(), "(no author)") ++ "\t" ++ coalesce(description.first_line(), "(no description)") ++ "\n"']]
|
||||
local output, ok = runner.execute_command(cmd)
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
if type(output) ~= "string" then
|
||||
return utils.notify("Could not get conflicts output", vim.log.levels.ERROR)
|
||||
end
|
||||
|
||||
local conflicts = {}
|
||||
local lines = vim.split(output, "\n", { trimempty = true })
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
local rev, author, description = line:match("^(.-)\t(.-)\t(.*)$")
|
||||
if rev and rev ~= "" then
|
||||
local item_author = author or "(no author)"
|
||||
local item_description = description or "(no description)"
|
||||
table.insert(conflicts, {
|
||||
symbol = "!",
|
||||
rev = rev,
|
||||
author = item_author,
|
||||
description = item_description,
|
||||
text = string.format("%s %s %s", rev, item_author, item_description),
|
||||
preview_cmd = { "jj", "--no-pager", "show", "-r", rev, "--stat", "--git" },
|
||||
confirm_action = "resolve_conflict",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return conflicts
|
||||
end
|
||||
|
||||
--- Displays in the configurated picker the list of conflicted revisions
|
||||
function M.conflict()
|
||||
-- Ensure jj is installed
|
||||
if not utils.ensure_jj() then
|
||||
return
|
||||
end
|
||||
|
||||
local conflicts = get_conflicts()
|
||||
if not conflicts or #conflicts == 0 then
|
||||
return utils.notify("`Picker`: No conflicts found", vim.log.levels.INFO)
|
||||
end
|
||||
|
||||
if M.config.snacks then
|
||||
require("jj.picker.snacks").conflict(M.config, conflicts)
|
||||
else
|
||||
-- Otherwise, use the default vim.ui.select to choose a conflicted revision
|
||||
vim.ui.select(conflicts, {
|
||||
prompt = "Select conflicted revision",
|
||||
format_item = function(item)
|
||||
return string.format("%s %s %s", item.rev or "", item.author or "", item.description or "")
|
||||
end,
|
||||
}, function(item)
|
||||
resolve_conflict(item, function(exit_code)
|
||||
if exit_code == 0 and item and item.rev then
|
||||
utils.notify(string.format("Successfully resolved `%s`", item.rev), vim.log.levels.INFO)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
+147
-52
@@ -4,6 +4,79 @@ local runner = require("jj.core.runner")
|
||||
--- @class jj.picker.snacks
|
||||
local M = {}
|
||||
|
||||
local function get_snacks_opts(opts)
|
||||
if opts.snacks == true then
|
||||
return {}
|
||||
end
|
||||
return opts.snacks --[[@as table]]
|
||||
end
|
||||
|
||||
--- Append git-style highlighted description segments to a Snacks highlight array.
|
||||
---
|
||||
--- If the description matches a conventional-commit-like shape such as
|
||||
--- `feat(scope)!: body`, this splits and highlights the type/scope/breaking
|
||||
--- marker separately, then appends the remaining body with `default_hl`.
|
||||
--- Otherwise the whole description is appended as a single segment.
|
||||
---
|
||||
--- @param ret snacks.picker.Highlight[]
|
||||
--- @param desc string|nil
|
||||
--- @param default_hl? string Highlight group used for the description body/fallback text
|
||||
local function append_description_hl(ret, desc, default_hl)
|
||||
desc = desc or ""
|
||||
|
||||
local type, scope, breaking, body = desc:match("^(%S+)%s*(%(.-%))(!?):%s*(.*)$")
|
||||
if not type then
|
||||
type, breaking, body = desc:match("^(%S+)(!?):%s*(.*)$")
|
||||
end
|
||||
|
||||
local msg_hl = default_hl or "SnacksPickerGitMsg"
|
||||
if type and body then
|
||||
local dimmed = vim.tbl_contains({ "chore", "bot", "build", "ci", "style", "test" }, type)
|
||||
msg_hl = dimmed and "SnacksPickerDimmed" or (default_hl or "SnacksPickerGitMsg")
|
||||
ret[#ret + 1] = {
|
||||
type,
|
||||
breaking ~= "" and "SnacksPickerGitBreaking" or dimmed and "SnacksPickerBold" or "SnacksPickerGitType",
|
||||
}
|
||||
if scope and scope ~= "" then
|
||||
ret[#ret + 1] = { scope, "SnacksPickerGitScope" }
|
||||
end
|
||||
if breaking ~= "" then
|
||||
ret[#ret + 1] = { "!", "SnacksPickerGitBreaking" }
|
||||
end
|
||||
ret[#ret + 1] = { ":", "SnacksPickerDelim" }
|
||||
ret[#ret + 1] = { " " }
|
||||
desc = body
|
||||
end
|
||||
|
||||
ret[#ret + 1] = { desc, msg_hl }
|
||||
end
|
||||
|
||||
--- Format a conflict picker entry with git-like colored segments.
|
||||
---
|
||||
--- Layout:
|
||||
--- - change/revision id
|
||||
--- - author
|
||||
--- - first-line description
|
||||
---
|
||||
--- @param item jj.picker.conflict|nil
|
||||
--- @return snacks.picker.Highlight[]
|
||||
local function format_conflict_item(item)
|
||||
if not item then
|
||||
return {}
|
||||
end
|
||||
|
||||
local a = Snacks.picker.util.align
|
||||
local ret = {} ---@type snacks.picker.Highlight[]
|
||||
|
||||
ret[#ret + 1] = { a(item.rev or "unknown", 12, { truncate = true }), "Constant" }
|
||||
ret[#ret + 1] = { " " }
|
||||
ret[#ret + 1] = { a(item.author or "(no author)", 16, { truncate = true }), "Identifier" }
|
||||
ret[#ret + 1] = { " " }
|
||||
append_description_hl(ret, item.description, "Comment")
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Displays the status files in a snacks picker
|
||||
---@param opts jj.picker.config
|
||||
---@param files jj.picker.file[]
|
||||
@@ -13,16 +86,7 @@ function M.status(opts, files)
|
||||
end
|
||||
|
||||
local snacks = require("snacks")
|
||||
|
||||
local snacks_opts
|
||||
-- If its true we default to an empty table
|
||||
if opts.snacks == true then
|
||||
snacks_opts = {}
|
||||
else
|
||||
--- Otherwise we get the table from the config
|
||||
---@type table
|
||||
snacks_opts = opts.snacks
|
||||
end
|
||||
local snacks_opts = get_snacks_opts(opts)
|
||||
|
||||
local merged_opts = vim.tbl_deep_extend("force", snacks_opts, {
|
||||
source = "jj",
|
||||
@@ -48,7 +112,7 @@ function M.status(opts, files)
|
||||
},
|
||||
},
|
||||
preview = function(ctx)
|
||||
if ctx.item.file then
|
||||
if ctx.item and ctx.item.diff_cmd then
|
||||
snacks.picker.preview.cmd(ctx.item.diff_cmd, ctx, {})
|
||||
end
|
||||
end,
|
||||
@@ -57,7 +121,7 @@ function M.status(opts, files)
|
||||
snacks.picker.pick(merged_opts)
|
||||
end
|
||||
|
||||
local function format_jj_log(item, picker)
|
||||
local function format_jj_log(item)
|
||||
local a = Snacks.picker.util.align
|
||||
local ret = {} ---@type snacks.picker.Highlight[]
|
||||
|
||||
@@ -79,7 +143,7 @@ local function format_jj_log(item, picker)
|
||||
end
|
||||
|
||||
if item.author then
|
||||
ret[#ret + 1] = { a(item.author, 8, { truncate = true }), "SnacksPcikerGitMsg" }
|
||||
ret[#ret + 1] = { a(item.author, 8, { truncate = true }), "Identifier" }
|
||||
if #item.author >= 8 then
|
||||
ret[#ret + 1] = { " " }
|
||||
end
|
||||
@@ -101,33 +165,7 @@ local function format_jj_log(item, picker)
|
||||
end
|
||||
end
|
||||
|
||||
-- This comes from snacks git description formattedr
|
||||
local desc = item.description or ""
|
||||
local type, scope, breaking, body = desc:match("^(%S+)%s*(%(.-%))(!?):%s*(.*)$")
|
||||
|
||||
if not type then
|
||||
type, breaking, body = desc:match("^(%S+)(!?):%s*(.*)$")
|
||||
end
|
||||
local msg_hl = "SnacksPickerGitMsg"
|
||||
if type and body then
|
||||
local dimmed = vim.tbl_contains({ "chore", "bot", "build", "ci", "style", "test" }, type)
|
||||
msg_hl = dimmed and "SnacksPickerDimmed" or "SnacksPickerGitMsg"
|
||||
ret[#ret + 1] = {
|
||||
type,
|
||||
breaking ~= "" and "SnacksPickerGitBreaking" or dimmed and "SnacksPickerBold" or "SnacksPickerGitType",
|
||||
}
|
||||
if scope and scope ~= "" then
|
||||
ret[#ret + 1] = { scope, "SnacksPickerGitScope" }
|
||||
end
|
||||
if breaking ~= "" then
|
||||
ret[#ret + 1] = { "!", "SnacksPickerGitBreaking" }
|
||||
end
|
||||
ret[#ret + 1] = { ":", "SnacksPickerDelim" }
|
||||
ret[#ret + 1] = { " " }
|
||||
desc = body
|
||||
end
|
||||
|
||||
ret[#ret + 1] = { desc, msg_hl }
|
||||
append_description_hl(ret, item.description)
|
||||
return ret
|
||||
end
|
||||
|
||||
@@ -139,16 +177,7 @@ function M.file_log_history(opts, log_lines)
|
||||
end
|
||||
|
||||
local snacks = require("snacks")
|
||||
|
||||
local snacks_opts
|
||||
-- If its true we default to an empty table
|
||||
if opts.snacks == true then
|
||||
snacks_opts = {}
|
||||
else
|
||||
--- Otherwise we get the table from the config
|
||||
---@type table
|
||||
snacks_opts = opts.snacks
|
||||
end
|
||||
local snacks_opts = get_snacks_opts(opts)
|
||||
|
||||
local merged_opts = vim.tbl_deep_extend("force", snacks_opts, {
|
||||
source = "jj",
|
||||
@@ -173,8 +202,74 @@ function M.file_log_history(opts, log_lines)
|
||||
end
|
||||
end,
|
||||
preview = function(ctx)
|
||||
if ctx.item.rev and ctx.item.diff_cmd then
|
||||
snacks.picker.preview.cmd(ctx.item.diff_cmd, ctx, {})
|
||||
if ctx.item and ctx.item.preview_cmd then
|
||||
snacks.picker.preview.cmd(ctx.item.preview_cmd, ctx, { ft = "git" })
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
snacks.picker.pick(merged_opts)
|
||||
end
|
||||
|
||||
--- Picker to chose conlicted revisions and resolve them
|
||||
---@param opts jj.picker.config
|
||||
---@param conflicts jj.picker.conflict[]
|
||||
function M.conflict(opts, conflicts)
|
||||
if not opts.snacks then
|
||||
return utils.notify("Snacks picker is `disabled`", vim.log.levels.INFO)
|
||||
end
|
||||
|
||||
local snacks = require("snacks")
|
||||
local snacks_opts = get_snacks_opts(opts)
|
||||
|
||||
local picker = require("jj.picker")
|
||||
|
||||
local function exit_func(rev)
|
||||
return function(exit_code)
|
||||
if exit_code == 0 then
|
||||
utils.notify(string.format("Successfully resolved `%s`", rev), vim.log.levels.INFO)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local merged_opts = vim.tbl_deep_extend("force", snacks_opts, {
|
||||
source = "jj",
|
||||
items = conflicts,
|
||||
title = "JJ Conflicts",
|
||||
format = format_conflict_item,
|
||||
actions = {
|
||||
edit_revision = function(snacks_picker, item)
|
||||
snacks_picker:close()
|
||||
|
||||
if not item or not item.rev then
|
||||
return
|
||||
end
|
||||
|
||||
local _, ok = runner.execute_command(
|
||||
string.format("jj edit %s", item.rev),
|
||||
string.format("could not edit revision '%s'", item.rev)
|
||||
)
|
||||
|
||||
if ok then
|
||||
utils.reload_changed_file_buffers()
|
||||
utils.notify(string.format("Editing conflicted revision `%s`", item.rev), vim.log.levels.INFO)
|
||||
end
|
||||
end,
|
||||
},
|
||||
win = {
|
||||
input = {
|
||||
keys = {
|
||||
["<C-e>"] = { "edit_revision", mode = { "i", "n" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
confirm = function(snacks_picker, item)
|
||||
snacks_picker:close()
|
||||
picker.resolve_conflict(item, exit_func(item and item.rev or ""))
|
||||
end,
|
||||
preview = function(ctx)
|
||||
if ctx.item and ctx.item.preview_cmd then
|
||||
snacks.picker.preview.cmd(ctx.item.preview_cmd, ctx, { ft = "git" })
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -919,4 +919,67 @@ function M.list_github_prs(opts)
|
||||
return open_prs
|
||||
end
|
||||
|
||||
-- Open first conflicted file, runs edit on the conflicted revision and opens the first conflicted file in a buffer.
|
||||
--- @param revset string The revset to check for conflicts
|
||||
function M.open_first_conflicted_file(revset)
|
||||
if not revset or revset == "" then
|
||||
M.notify("No revset provided to open conflicted file", vim.log.levels.ERROR)
|
||||
return
|
||||
elseif not M.is_change_conflicted(revset) then
|
||||
return
|
||||
end
|
||||
|
||||
local repo_root = M.get_jj_root()
|
||||
local quoted_revset = vim.fn.shellescape(revset)
|
||||
|
||||
-- Resolve the first conflicted path before editing so we can still open it
|
||||
-- reliably when the current working directory is outside the repo root.
|
||||
local list_output, list_ok = runner.execute_command(
|
||||
string.format("jj resolve -r %s --list", quoted_revset),
|
||||
string.format("could not list conflicted files for '%s'", revset),
|
||||
nil,
|
||||
true
|
||||
)
|
||||
|
||||
local first_conflicted_path
|
||||
if list_ok and type(list_output) == "string" and list_output ~= "" then
|
||||
for _, line in ipairs(vim.split(list_output, "\n", { trimempty = true })) do
|
||||
local rel_path = vim.trim(line:gsub("^[-*]%s+", ""))
|
||||
if rel_path ~= "" then
|
||||
first_conflicted_path = rel_path
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local _, ok = runner.execute_command(
|
||||
string.format("jj edit %s --ignore-immutable", quoted_revset),
|
||||
string.format("could not edit revision '%s'", revset)
|
||||
)
|
||||
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
|
||||
M.reload_changed_file_buffers()
|
||||
M.notify(string.format("Editing conflicted revision `%s`", revset), vim.log.levels.INFO)
|
||||
|
||||
if first_conflicted_path then
|
||||
local first_conflicted_file = repo_root and vim.fs.joinpath(repo_root, first_conflicted_path)
|
||||
or first_conflicted_path
|
||||
local escaped = vim.fn.fnameescape(first_conflicted_file)
|
||||
local existing_buf = vim.fn.bufnr(first_conflicted_file)
|
||||
|
||||
if existing_buf ~= -1 and vim.api.nvim_buf_is_loaded(existing_buf) then
|
||||
local modified = vim.bo[existing_buf].modified
|
||||
if not modified then
|
||||
vim.cmd("bdelete! " .. existing_buf)
|
||||
end
|
||||
end
|
||||
|
||||
vim.cmd("edit! " .. escaped)
|
||||
vim.cmd("checktime " .. escaped)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user