UP | HOME

▼ 本文更新于 [2026-01-12 周一 13:52]

emacs-在Windows上使用majutsu管理git

想必使用Emacs的你,应该听说过Git。虽然功能强大且成为了现代社区开发的基石之一,但Git的使用难度着实不低。幸好,我们Emacs上有一个伟大的pcakage Magit ,可以极大程度提升Git使用幸福度。

然而在Windows上,Magit的性能相当差,根据有关讨论,主要是因为magit会运行大量 git 进程来收集所需信息,而Windows上创建进程的代价又相对较高。

时间来到了2025年,在Rewrite It In Rust的风潮中,许多经典的Linux工具被Rust重写,而Git也有一个以Rust编写的相似软件:Jujutsu。简单来说,jj能够管理Git的repo,采用了简化一点的设计思路,当然也有缺少的功能。不过最重要的是,在Windows上用Majutsu+Jujutsu比Magit+Git响应速度更快。

1. 配置Git、Jujutsu

Jujutsu需要和Git一起安装,在管理repo之后也会调用Git进行push/fetch等操作。

根据这篇帖子,Windows上的Git不能用scoop安装,需要下载Installer,并在安装的过程中勾选「使用外部 OpenSSH」。如果你不幸通过scoop安装了,请卸载并改用官方的安装包(否则你会发现jj无法push)。


使用外部SSH时,若要设置Git的代理,应手动指定 connect.exe 的路径,如:

Proxycommand C:\Program Files\Git\mingw64\bin\connect.exe -S 127.0.0.1:7890 %h %p

Jujutsu可以通过scoop安装,直接 scoop install jj 就行了。

2. 配置majutsu

如果你用的emacs 30+ 的版本,可以用 use-package:vc 关键字直接从Github Repo安装Majutsu。

(use-package majutsu
  :vc (:url "https://github.com/0WD0/majutsu"
            :rev :newest)
  :bind
  (("C-x j" . majutsu-log)
   ("C-x C-j" . majutsu-log)))

不然的话,也可以用其他方式,或者手动下载相关文件。

3. 使用majutsu

C-x j 进入majutsu界面。jujutsu的思路和git不一样,取消了暂存区等功能,当前文件的状态是一个未命名的新HEAD,commit之后就会固定。

一个简单的工作流程是:

在当前repo里进行一些变更,等觉得应该保存当前状态了,就 C-x j 然后 c ,输入commit内容,生成一个commit。

如果想上传了,就 C-x j 然后将光标移动至想更新到的commit上,再 b s ,将 main 移动过来,最后 G p p 推送。

更详细的介绍,请看下面两个教程:

忙碌开发者的jujutsu教程

每个人的jujutsu教程

下面我用几个实际案例说明一下操作。

3.1. 初始化

这一步需要在cmd/pwsh中进行。以下均假设默认branch为 main

如果需要从头clone一个repo:

jj git clone --colocate REPO-URL

如果本地已经有一个git的repo,则需要在初始化之后明确追踪 main@origin (根据实际情况的主要分支而定):

jj git init --colocate
jj bookmark track main@origin

3.2. 保存变更为commit

在当前repo里进行一些变更,等觉得应该保存当前状态了,就 C-x j 然后 c ,输入commit内容, C-c C-c 保存并生成一个commit。

3.3. 将本地commit上传

C-x j 进入Majutsu界面,然后将光标移动至想更新到的commit上,再 b s ,将 main 移动过来,最后 G p p 推送。

3.4. 拉取并merge远端

由于没有新branch,默认是采取的rebase方法。
C-x j 进入Majutsu界面,然后 G f f fetch远端,再 r 进入rebase界面,移动光标到本地commit,按 s 将其选为起点,再移动光标到fetch的远端commit,按 d 将其选为终点,最后按 r 确认rebase。

3.5. 放弃本地变更

有的时候需要放弃本地变更。只能放弃可变的变更(非菱形图标),将光标移动到该commit上,按 a 再按 y 就能放弃。

4. 后记

由于我主要用jj管理.emacs.d,很少用到其他功能,因此使用懒猫的one-key来简化操作,放弃了 majutsu

(use-package with-editor
  :ensure t :demand t
  :config
  (defun my/setup-editor-buffer ()
    (when (string-prefix-p "editor-" (buffer-name))
      (with-editor-mode 1)))
  :hook
  (server-visit-hook . my/setup-editor-buffer))
(defun my/personal-jj (action)
  (let* ((emacs-exe (if (eq system-type 'windows-nt)
                        "emacsclientw"
                      "emacsclient"))
         (args " --no-pager")
         (str
          (cond ((eq action 'push)
                 "jj b s main && jj git push")
                ((eq action 'fetch)
                 "jj git fetch && jj rebase -d main")
                ((eq action 'commit)
                 "jj commit")
                ((eq action 'squash)
                 "jj squash")
                ((eq action 'describe)
                 "jj describe")
                ((eq action 'log)
                 "jj log")
                ((eq action 'status)
                 "jj status")
                ((eq action 'abandon)
                 (concat "jj abandon " (read-string "Abandon: ")))
                ((eq action 'edit)
                 (concat "jj edit " (read-string "Edit: ")))))
         (process-environment (cons (concat "JJ_EDITOR=" emacs-exe) process-environment)))
    (async-shell-command (concat str args) "*JJ*")))

(use-package one-key :demand t
  :vc (:url "git@github.com:manateelazycat/one-key"
           :rev :newest))
(one-key-create-menu
 "GIT"
 '(
   (("p" . "Push") . (lambda () (interactive) (my/personal-jj 'push)))
   (("f" . "Fetch") . (lambda () (interactive) (my/personal-jj 'fetch)))
   (("g" . "Status") . (lambda () (interactive) (my/personal-jj 'status)))
   (("d" . "Describe") . (lambda () (interactive) (my/personal-jj 'describe)))
   (("l" . "Log") . (lambda () (interactive) (my/personal-jj 'log)))
   (("q" . "sQuash") . (lambda () (interactive) (my/personal-jj 'squash)))
   (("a" . "Abandon") . (lambda () (interactive) (my/personal-jj 'abandon)))
   (("e" . "Edit") . (lambda () (interactive) (my/personal-jj 'edit)))
   (("c" . "Commit") . (lambda () (interactive) (my/personal-jj 'commit))))
 t)
(global-set-key "\C-cg" #'one-key-menu-git)

© Published by Emacs 31.0.50 (Org mode 9.8-pre) | RSS English-Index