local log = require("log") local repo = require("git.repo") local M = {} ---@param opts { amend: boolean? }? function M.commit(opts) local amend = opts and opts.amend or false local gitdir, worktree = repo.resolve_cwd() if not gitdir or not worktree then log.warning("not in a git repository") return end local msg_path = vim.fs.joinpath(gitdir, "COMMIT_EDITMSG") local initial = "" if amend then local result = vim.system( { "git", "log", "-1", "--pretty=%B" }, { cwd = worktree, text = true } ):wait() if result.code == 0 then initial = (result.stdout or ""):gsub("\n+$", "") else log.warning("git log -1 failed: %s", vim.trim(result.stderr or "")) end end local f, err = io.open(msg_path, "w") if not f then log.error("failed to open %s: %s", msg_path, err or "") return end f:write(initial) f:close() local ok, err = pcall(vim.cmd.edit, vim.fn.fnameescape(msg_path)) if not ok then log.error("failed to open %s: %s", msg_path, err or "") return end local buf = vim.api.nvim_get_current_buf() if vim.api.nvim_buf_get_name(buf) ~= msg_path then -- `:edit` returned without surfacing an error but didn't actually -- switch (defensive against an unusual ftplugin/autocmd path). Bail -- before attaching a BufWriteCmd that would overwrite the wrong -- file on the next `:w`. log.error("failed to switch to %s", msg_path) return end vim.bo[buf].filetype = "gitcommit" vim.api.nvim_create_autocmd("BufWriteCmd", { buffer = buf, callback = function() local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) local fw, werr = io.open(msg_path, "w") if not fw then log.error("failed to write %s: %s", msg_path, werr or "") return end fw:write(table.concat(lines, "\n")) fw:close() vim.bo[buf].modified = false local cmd = { "git", "commit", "-F", msg_path } if amend then table.insert(cmd, "--amend") end local result = vim.system(cmd, { cwd = worktree, text = true }) :wait() if result.code ~= 0 then log.error( "git commit failed: %s", vim.trim(result.stderr or "") ) return end local out = vim.trim(result.stdout or "") if out ~= "" then log.info("%s", out) end vim.api.nvim_buf_delete(buf, { force = true }) end, }) end return M