GitHub - 0WD0/majutsu: Majutsu! Magit for jujutsu

我从
jujutsu

名字来源:ma(取自 Emacs,象征“magic/majutsu”的前缀)+ jujutsu → majutsu。
与 Magit 的关系:Majutsu 并非“Magit + Jujutsu”的直拼;与 Magit 的联系来自相同的拼接形式与相近语义(Magic ↔ 魔術),因此形成呼应与致敬。

语义说明:

  • Magic 在中文可译为“魔法/魔术”。
  • Jujutsu 在本项目语境指版本控制系统 Jujutsu(jj);在日语里“呪術”(jujutsu)意为“咒术”,与“魔法”同属玄术范畴;另有同音“柔術”(武术),此处不取其义。
  • Majutsu(魔術,majutsu)在日语中意为“魔术、法术”,与 Magic 相呼应。

项目定位:Majutsu 是 jj-mode.el 的一个 fork,目标是把 Emacs 式、Magit 风格的“魔法般”交互带到 Jujutsu(jj)上。

在 emacs-china 上被人说不应该取消 fork 链接了,让我难受了好一会儿

Components

majutsu-template

我设计了一种 jujutsu 模板语言的 DSL

历时两天实现了 MVP

能否利用 —config flag?

—config <NAME=VALUE>
Additional configuration options (can be repeated)

The name should be specified as TOML dotted keys. The value should be specified as a TOML expression. If string value isn’t enclosed
by any TOML constructs (such as array notation), quotes can be omitted.

STRT majutsu-jjdescription

我需要给 jjdescription 手动做一下美化!

TODO majutu-split

我知道怎么实现 split 的功能了!!!
magit manual link

我们可以在 diff section 上设计两个命令
一个是选中部分 split 出来作为上/下一个 rev(也就是 split)
一个是命令能让小 patch 在前/后 rev 直接传递(也就是 squash)

目前我觉得只能通过 --tool--config 来实现

—config <NAME=VALUE>
Additional configuration options (can be repeated)

The name should be specified as TOML dotted keys. The value should be specified as a TOML expression. If
string value isn’t enclosed by any TOML constructs (such as array notation), quotes can be omitted.

我觉得我可以在临时生成的 —config 参数里里写死 patch

然后可以用这个临时配置

[merge-tools.applypatch]
program = "patch"
edit-args = ["-p1", "-d", "$right", "-i", "/tmp/foo.patch", "-s"]

codex resume 019abdb6-c030-7361-a9ea-9cd0fedab0aa

TODO majutsu-workspace

我们需要做 workspace 支持,类似于 magit-worktree

doom module 设计

打算直接抄 workspace 的 keybinding

TODO majutsu-diff

我觉得这是非常重要的功能!!!

我需要那些类型的 diff

除了 ediff 集成,我还需要有 magit-diff 这种的diff
它还是有一些优势的

我现在发现 jj 自动进行的冲突解决比我想象的智能,
我在重构代码的时候(整行的移动),貌似不会出现冲突?
但是如果我用 resolve,用其他工具打开(比如说 meld),
我就需要手动合并冲突文件(left)中实际上不冲突的部分到 output,
我觉得这很没有意义,需要一个解决方案能让我直接得到已经自动解决了可以自动解决的冲突的 left

TODO 我需要摆脱 magit-diff 的依赖,需要适配 jj 原本的 diff 风格

可能会有启发 difftastic

TODO majutsu-evolog

我需要一种新的 log buffer !

在这里查看一个 working copy 的变更过程应该是非常有用的功能!

TODO majutsu-op-log

重构后已经有了基本的实现

log section 的组成

参考 commit 类型有的属性

change-id
commit-id
refs
bookmarks
tags
working-copies 这个可能会有一点不直观,其实就是展示 workspace 的头
flags
git-head
signature
empty
author
timestamp
description 第一行的提交信息
long-desc 完整的提交信息

然后还有一个 flags 表示一个commit的一些 bool 属性

Insight

TODO op log

TODO 研究一下 evolog 如何实现

和正常的 log 不同,evolog 中 change-id 不重要,只需要显示 commit-id 即可
然后其它都差不多

使用的是 CommitEvolutionEntry 类型
包含一个 commit 和一个 operation

这么说起来其实先做一下 op-log 也行?

TODO 重构 diffstat

TODO diffstat 中路径过长 ATTACH

TODO majutsu-jump-to-diffstat-or-diff 对 rename 条目不能正常工作

TODO 直接用 file section,支持跳转到文件

TODO toggle revision section 显示完整的 description ATTACH

现在可能会有这样显示不全的问题,虽然可以用缩小来解决,但是这个前面奇怪的缩进还可以优化一下

TODO 想要在各种地方实现 magit-diff-visit-file

jj file show 来得到临时文件

TODO majtusu-goto-prev / majutsu-edit-prev

majutsu-goto-prev / majutsu-goto-next 的功能上

注意这东西和 jj-prev 和 jj-next 的目标不一样

我们可以用 jj-log 来解析 revsets

DONE flatten log template

∅ U+2205 Empty Set:数学上表示“空”,含义最贴合“无描述”。等宽终端普遍支持,宽度=1。
⌀ U+2300 Diameter Sign:视觉像被划掉的圆,容易读作“空”,常见于工程图,等宽字体支持度也不错。
Ø U+00D8 Latin Capital O with Stroke:与“空 / 无”关联明显,但有时被当作字母 O,可能在排序或搜索时产生歧义。
□ U+25A1 White Square:表示“留空 / 待填”,字体兼容好,但语义不如“∅”直观指向“无”。
🚫 U+1F6AB Prohibited:语义清晰,但宽度在等宽终端可能占 2;一些轻量终端或日志工具对 emoji 支持一般。

这东西其实可以做的很美观

Issues

TODO --allow-new is deprecated

TODO simplify-parents support

jj-simplify-parents

这种小功能,不知道怎么处理啊,感觉不是很能用上,我需要绑到单独的键上吗?

TODO 可视化的 revset builder

codex resume 019aa1d9-0262-7c31-aa61-be1ae1635008

TODO 支持 jj-tag

TODO 多 workspace 下 goto current 会出现问题!

现在是直接跳到 @ ,显然是不对的。。。
但是其实不着急改

TODO --ignore-immutable support

我觉得这可能也是比较重要的功能?

可以加一个统一的 flag 来控制这个

DONE 表示工作区的 default@ 也会被认为是 bookmark!

问题在于,我在显示 log 行元素的哪里没有把 bookmarks 和 其他refs 分开,全部放到 refs 里了

DONE 除了 log-display-function 和 message-display-function 我还想要给其他组件预设显示函数

codex resume 019af299-08ff-7870-97e2-dcdde47b5089

DONE majutsu-jump-to-diffstat-or-diff

修复只能单项跳转的问题:
codex resume 019ac604-37d9-7f50-842f-c7f50d47496c

DONE 各种 refresh 没有确保当前的 buffer 正确

changeid: zprvnsxy
codex resume 019aca6b-5cb2-76e0-9392-d7ec4c3ef42a

DONE diff section refine

解决了当前section的背景样式丢失的问题

changeid: usxpzopo
codex resume 019ac5cc-e08b-7b50-9cfc-b58ebf804753

DONE diffstat 解析

叫它换用 rx 解析,然后就一下就写对了
changeid: owwuopzt

codex resume 019ac4fe-148c-7e52-b57d-9b8c724349f9

DONE with-editor 集成

关于 with-editor 的理解还是放在 with-editor 比较好

with-editor-server-window-alist
文件名和想让他运行的函数的对应

我现在运行的函数是 majutsu—with-editor-display-buffer

windows 下 with-editor 中的 start-file-process 不起作用

的确就是那个括号的问题,我让它在 windows 下强制删括号了,就能正常工作了
可能有更优雅的解决方案,但是我不想管了

with-editor 本身存在问题

最后的解决方案是不用 switch-to-buffer 改用 pop-to-buffer

在 log buffer 打开 message/diff buffer 再关掉,可以回到 log buffer 但是这时候再 quit-window 就不会自动删除 window 了

直接由 pop-to-buffer 得到的 window

((quit-restore window window #<window 3 on majutsu.el> #<buffer *majutsu-log:majutsu*>))

调用 diff 后的 window parameter

((quit-restore-prev other
                    (#<buffer *majutsu-log:majutsu*> 1 #<marker at 11 in
                              *majutsu-log:majutsu*> 84)
                    #<window 232 on *majutsu-diff*> #<buffer *majutsu-diff*>)
 (quit-restore window window #<window 3 on majutsu.el> #<buffer *majutsu-log:majutsu*>))

调用 message buffer 之后的 window parameter

(quit-restore other
              (#<buffer *majutsu-log:majutsu*> 1 #<marker at 11 in *majutsu-log:majutsu*> 84)
              #<window 264 on editor-67yjNs.jjdescription> #<buffer editor-67yjNs.jjdescription>)

关掉 diff 之后的 log buffer

((quit-restore-prev)
 (quit-restore other
               (#<buffer majutsu.el> 30168 #<marker at 30965 in majutsu.el> 84)
               #<window 240 on *majutsu-log:majutsu*> #<buffer *majutsu-log:majutsu*>))

关掉 message buffer 之后的 log buffer

((quit-restore-prev)  (quit-restore))

这个可以用来记录日志

(defvar my/quit-restore-log nil)
 
(defun my/track-quit-restore (orig window parameter value &rest args)
  (when (memq parameter '(quit-restore quit-restore-prev))
    (let ((trace (with-temp-buffer
                   (backtrace)
                   (buffer-string))))
      (push (list :time (current-time-string)
                  :buffer (window-buffer window)
                  :window window
                  :parameter parameter
                  :value value
                  :stack trace)
            my/quit-restore-log)
      (message "set-window-parameter %s -> %S\n%s"
               parameter value trace)
      ;; (debug) ; 若希望直接进入调试器
      ))
  (apply orig window parameter value args))
 
(advice-add 'set-window-parameter :around #'my/track-quit-restore)

DONE windows 下的 commit 字符编码问题

由于 jj 在 windows 上的性能优于 git
所以可能是有不少人是因为这一点来使用 jj 的
应该做好 windows 兼容

出现问题的地方是:子进程调用使用的 codepage 是固定的!并且默认不是 utf-8

问题:如果当前目录(default-directory)含有系统代码页无法表示的非 ASCII 字符,Emacs 在 Windows 上启动外部命令会失败,出现 “Spawning child process: Exec format error”。
原因:Emacs 在 Windows 调用程序时必须把程序路径和参数用系统代码页编码。你的系统代码页是 1252(西欧),而像 “zażółć gęślą jaźń” 这类波兰字符不能用 1252 表示,所以失败。很多 Windows 程序只支持当前代码页,不真正支持 UTF‑8/Unicode。
误区:在 Emacs 里改编码(如设为 cp1250)只影响缓冲区、文件和键盘/终端 I/O,不会改变操作系统的代码页,因此对启动子进程没用。
解决:把 Windows 的“非 Unicode 程序的语言”(系统代码页)改成能覆盖你语言字符的代码页(如 cp1250),或避免在路径中使用不在当前代码页内的字符,或使用真正支持 Unicode 的程序/环境。文末作者已将系统代码页全局改了,之后就能正常运行。

如何查询 codepage?

可按需查询两种不同的 code page(很重要的区别):

  • 系统 ANSI 代码页(ACP):很多非 Unicode 程序和 Emacs 在 Windows 下给子进程编码时受它限制
  • 控制台代码页(chcp):仅影响当前终端的输入/输出

查询方法:

  • PowerShell
    • 系统 ANSI(ACP):[System.Text.Encoding]::Default.CodePage 和 [System.Text.Encoding]::Default.WebName
    • 控制台:[Console]::OutputEncoding.CodePage
  • CMD
    • 控制台:chcp
    • 注册表(系统 ANSI 与控制台 OEM):reg query “HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage” /v ACP 和 /v OEMCP
  • Emacs 中(简便调用)
    • 控制台:M-: (shell-command-to-string “cmd /c chcp”) RET
    • 系统 ANSI:M-: (string-trim (shell-command-to-string “powershell -NoProfile -Command [System.Text.Encoding]::Default.CodePage”)) RET
  • 图形界面
    • 控制面板 → 区域 → 管理 → “非 Unicode 程序的语言”(这里显示/更改系统 ANSI 代码页;勾选“使用 Unicode UTF-8(测试版)”则为 65001)

更好的解决方案

借鉴 magit 的解决方案,用 with-editor

DONE 实现在 majutsu buffer 外的 majutsu-diff

DONE 复杂 graph 测试 ATTACH

无意间出现了问题!

这是我在测试 duplicate 功能时的截图

这个是相同template下的正确的 graph 输出

行首空格被吞掉了!

DONE bookmark 向前移动

需要使用 —allow-backwards -B flag
我觉得可以放到 M 上

DONE 不及时刷新可能会出现 change-id?? 的情况 ATTACH

我应该让他优先解析 change-id 的

我觉得我需要对这种情况设置一个比较的函数

Change Log

我需要准备一下周期发布
现在应该是v0.3.0

可以查看一下这个 keepachangelog

User Manaul

一些规范

fill-column 设置为 70

70 列主要来自 GNU/Emacs 的传统文本排版习惯:在 80 列终端里留出前导引号(邮件引用的 “> ” 多层缩进)、行首缩
进和对齐空间,还能降低长行 diff 带来的噪音。Emacs 自带的 many manuals / NEWS 也常用 70(或 72)列作为 fill-
column;Magit 文档沿用了这一约定,以保证在终端、邮件补丁、git diff 和 Texinfo 导出时都保持良好的可读性。

但是这对中文来说的确是短了一点,不太能对中文使用,因为中文一般来说不会真的占两个字符长度!然后中文行的长度就会显得短很多

sentence-end-double-space

那是为了写/导出手册时的“句间双空格”排版习惯:

  • Emacs 的文本约定里,句子末尾默认用两个空格分隔,便于 fill-paragraph、句尾检测和排版工具(如 Texinfo)准确断句。
  • Magit 手册是从 Org 导出 Texinfo 的长篇文档,保持双空格可以在 refilling 时减少把缩写/小数误判为句末的概率,
    也方便后续 diff(少因自动 reflow 产生大面积改动)。
  • 所以 magit.org 里句点后保留两个空格是有意遵循传统的 Emacs/Texinfo 文档格式,而不是手误。

要不要使用 fill-paragraph

不一定需要使用

Texinfo (and Info) ignore line breaks inside a paragraph, so authors
often “semantically line break” instead of using fill-paragraph.
Reasons the Magit docs do this:

  • Keep identifiers (like winner-undo) off the wrap boundary so they
    don’t get split with hyphens in the source.
  • Make diffs readable in version control: one clause/sentence per line
    means edits produce small, localized hunks.
  • Avoid reflow churn when only a word changes.
  • Preserve deliberate grouping (e.g., command + keybinding) that
    fill-paragraph might separate.

So the odd-looking breaks are a maintenance choice; the rendered output is unchanged. You can keep
using semantic line breaks if that workflow (clean diffs, no code-word splits) matters to you.