feat(log): add immutable-ignoring rebase keymaps

Changes add support for rebasing while ignoring immutability constraints with new keymaps (<S-CR>, <S-o>, <S-a>, <S-b>) and update documentation. Refactors rebase handler to accept optional ignore_immut parameter and includes minor comment improvements in buffer and command modules.
This commit is contained in:
NicolasGB
2026-01-06 13:20:19 +01:00
committed by Nicolas GB
parent ca75a50858
commit ba48ed08b5
4 changed files with 59 additions and 12 deletions
+9 -3
View File
@@ -134,8 +134,11 @@ Once in rebase mode, the interface highlights your selection and the current reb
From rebase mode, choose how to rebase:
- `<CR>` or `o` - Rebase onto (`-o`) the revision under cursor
- `a` or `A` - Rebase after (`-A`) the revision under cursor
- `b` or `B` - Rebase before (`-B`) the revision under cursor
- `a` - Rebase after (`-A`) the revision under cursor
- `b` - Rebase before (`-B`) the revision under cursor
- `<S-CR>` or `<S-o>` - Rebase onto (`-o`) ignoring immutability
- `<S-a>` - Rebase after (`-A`) ignoring immutability
- `<S-b>` - Rebase before (`-B`) ignoring immutability
- `<Esc>` or `<C-c>` - Exit rebase mode without making changes
**Visual mode selection:** Select multiple revisions in visual mode before pressing `r` to rebase them all at once. The plugin extracts each selected revision and rebases them together.
@@ -300,9 +303,12 @@ The plugin also provides `:Jdiff`, `:Jvdiff`, and `:Jhdiff` commands for diffing
open_pr_list = "<S-o>", -- Open PR/MR by selecting from all bookmarks
rebase = "r", -- Enter rebase mode targeting revision under cursor or selected revisions
rebase_mode = {
onto = { "<CR>", "o" }, -- Select revision under cursor as rebase destination
onto = { "<CR>", "o" }, -- Select revision under cursor as rebase onto destination
after = { "a", "A" }, -- Rebase after revision under cursor
before = { "b", "B" }, -- Rebase before revision under cursor
onto_immutable = { "<S-CR>", "<S-o>" }, -- Select revision as a rebase onto destination (ignore immutability)
after_immutable = "<S-a>", -- Rebase after revision under cursor (ignore immutability)
before_immutable = "<S-b>", -- Rebase before revision under cursor (ignore immutability)
exit_mode = { "<Esc>", "<C-c>" }, -- Exit rebase mode
},
},
+8 -2
View File
@@ -52,6 +52,9 @@ local status_module = require("jj.cmd.status")
--- @field onto? string|string[]
--- @field after? string|string[]
--- @field before? string|string[]
--- @field onto_immutable? string|string[]
--- @field after_immutable? string|string[]
--- @field before_immutable? string|string[]
--- @field exit_mode? string|string[]
--- @class jj.cmd.bookmark
@@ -128,8 +131,11 @@ M.config = {
rebase = "r",
rebase_mode = {
onto = { "<CR>", "o" },
after = { "a", "A" },
before = { "b", "B" },
after = "a",
before = "b",
onto_immutable = { "<S-CR>", "<S-o>" },
after_immutable = "<S-a>",
before_immutable = "<S-b>",
exit_mode = { "<Esc>", "<C-c>" },
},
},
+39 -4
View File
@@ -127,7 +127,7 @@ local function apply_target_highlight(buf, revset_line, hl_group)
log_rebase_target_ns_id,
revset_line,
0,
{ end_line = revset_line + HIGHLIGHT_RANGE, hl_group = hl_group }
{ end_line = revset_line + HIGHLIGHT_RANGE, end_col = 0, hl_group = hl_group }
)
last_rebase_target_line = revset_line
end
@@ -176,6 +176,9 @@ function M.log(opts)
-- If a log was already being displayed before this command we will want to maintain the cursor position
if terminal.state.buf_cmd == "log" then
terminal.store_cursor_position()
-- Make sure to clear highlights before rerunning since the previous log buffer might have some
vim.api.nvim_buf_clear_namespace(terminal.state.buf, log_selected_ns_id, 0, -1)
vim.api.nvim_buf_clear_namespace(terminal.state.buf, log_rebase_target_ns_id, 0, -1)
end
local jj_cmd = "jj log"
@@ -636,7 +639,7 @@ function M.log_keymaps()
modes = { "n" },
},
new_after_immutable = {
desc = "Create new change after revision under cursor (ignore immutable)",
desc = "Create new change after revision under cursor (ignores immutability)",
handler = M.handle_log_new,
args = { "after", true },
modes = { "n" },
@@ -726,6 +729,24 @@ function M.rebase_keymaps()
args = { "before" },
modes = { "n" },
},
onto_immutable = {
desc = "Rebase onto (-O) the revision under cursor (ignores immutability)",
handler = M.handle_rebase_execute,
args = { "onto", true },
modes = { "n" },
},
after_immutable = {
desc = "Rebase revset(s) after (-A) the revision under cursor (ignores immutability)",
handler = M.handle_rebase_execute,
args = { "after", true },
modes = { "n" },
},
before_immutable = {
desc = "Rebase revset(s) before (-B) the revision under cursor (ignores immutability)",
handler = M.handle_rebase_execute,
args = { "before", true },
modes = { "n" },
},
exit_mode = {
desc = "Exit rebase to normal mode",
handler = M.handle_rebase_mode_exit,
@@ -790,13 +811,15 @@ function M.handle_rebase_mode_exit()
-- Clear highlights
local buf = terminal.state.buf or 0
vim.api.nvim_buf_clear_namespace(buf, log_selected_ns_id, 0, -1)
vim.api.nvim_buf_clear_namespace(buf, log_rebase_target_ns_id, 0, -1)
utils.notify("Rebase operation `canceled`", vim.log.levels.INFO, 500)
utils.notify("Rebase `canceled`", vim.log.levels.INFO, 500)
end
--- Handle rebase execution with mode
--- @param mode "onto" | "after" | "before" Rebase mode
function M.handle_rebase_execute(mode)
--- @param ignore_immut boolean? Wether or not to ignore immutability
function M.handle_rebase_execute(mode, ignore_immut)
-- Get all revsets in the format "xx xy xz"
local revsets = vim.b.jj_rebase_revsets
local destination_revset = get_revset()
@@ -813,6 +836,13 @@ function M.handle_rebase_execute(mode)
utils.notify(string.format("Rebasing...", revsets, mode, destination_revset), vim.log.levels.INFO, 500)
local cmd = string.format("jj rebase -r '%s' %s %s", revsets, mode_flat, destination_revset)
-- If ignore_immut is true, add the flag
-- This is not currently exposed in keymaps but could be in the future
if ignore_immut then
cmd = cmd .. " --ignore-immutable"
end
runner.execute_command_async(cmd, function()
utils.notify(
string.format("Rebased `%s` %s `%s` successfully", revsets, mode, destination_revset),
@@ -820,6 +850,11 @@ function M.handle_rebase_execute(mode)
)
vim.b.jj_rebase_revsets = nil
-- Clear all highlighting before transitioning
local buf = terminal.state.buf or 0
vim.api.nvim_buf_clear_namespace(buf, log_selected_ns_id, 0, -1)
vim.api.nvim_buf_clear_namespace(buf, log_rebase_target_ns_id, 0, -1)
M.transition_mode("normal")
-- Refresh log
M.log({})
+3 -3
View File
@@ -447,9 +447,9 @@ function M.set_cursor(buf, pos, opts)
return
end
-- For terminal buffers, delay to allow rendering to complete
-- Must validate position INSIDE the deferred function for terminal buffers
-- because the buffer content may not be stable yet
-- For terminal buffers, we delay cursor positioning to allow async rendering to complete.
-- The position is validated inside the deferred function because buffer content
-- may not be stable at function call time
if vim.bo[buf].buftype == "terminal" or delay > 0 then
vim.defer_fn(function()
if not vim.api.nvim_buf_is_valid(buf) or not vim.api.nvim_win_is_valid(winid) then