[magit] 如何 stage 一部分修改?

手册 Staging and Unstaging (Magit User Manual) 是有明确说明支持部分 stage 的:

With Magit, on the other hand, one can un-/stage individual hunks by just moving point into the respective section inside a diff displayed in the status buffer or a separate diff buffer and typing s or u. To operate on just parts of a hunk, mark the changes that should be un-/staged using the region and then press the same key that would be used to un-/stage.

Emacsair! A walk through the Magit interface 也有图文并茂的步骤说明。

然而我始终无法 stage 一部分内容,只能 stage 整个文件

由于我还不了解 magit,可能在描述问题的时候遣词用语不太准确,所以决定改用脚本来重现整个过程,以避免文字上的歧义或漏掉什么没有交代清楚:

  • 克隆 magit 到本地:git clone https://github.com/magit/magit ~/repos/magit
  • 运行测试脚本 (gist): emacs -nw -Q -l test-magti-state-minimal.el --eval '(load-magit "~/repos/magit/lisp")'

结果如图:

可以看出我的 Status 界面跟 magit-walk-through 上的截图 https://magit.vc/screenshots/status-solarized-dark.png 存在明显区别:

  • 根本没有 Unstaged changes
  • diff 内容直接平铺在屏幕最下方,不能按文件折叠。

而且我从终端 stage 了一部分内容,回到 Emacs 刷新,也没有出现如图 https://magit.vc/screenshots/stage-region-after.png 所示的 Staged changes,对比刷新之前没有任何变化


  • Macos 10.12.6
  • Emacs 26.3 / 28.0.50 (2020-01-29)
  • Magit c8cd22e2 (2020-02-28)
  • async-20200113.1745
  • dash-20200119.2310
  • git-commit-20200207.1819
  • transient-20200226.1612
  • with-editor-20200217.1015

因为那个文件还未加入至 git, 第一次只能全部提交。

如果想分批的话,可以先创建一个空的文件,然后 git add 它,对应的 magit 操作就是 s 这个文件。

我的界面

不是这个问题。

都 commit 超过 60 次了,怎么可能项目的主文件还没有 track。

按文件折叠, 快捷键: 2. 展开: 4

stage的最小单位是hunk, 类似下面框里:

如果文件只有一个hunk, 就只能提交整个文件.

我用的老版本magit, 新版本遇到几个坑, 就没再用.

我试过了,hunk 和 region 都失败:

而且是可以 stage 一个 region 的,手册里有写,在教程 Emacsair! A walk through the Magit interface 的 Staging parts of a hunk 小节更是有图有真相。

那看来你安装的是带坑版 :grinning:

无能为力了.

1 个赞

用过不少回了,选好区域(正常的 Emacs 选中区域的操作)之后按 s 就行了。

比如 +3 -2 个修改:

选中前两行修改:

按 s 得到

3 个赞

对此我是不怀疑的,文档写了,而且还有截图。如果不支持这个特性,magit 的价值在我看来要打 7 折。

难道真的有所谓坑版?我要往回滚到哪个版本?

选中某个区域按小写s,用过上百次了

是按小写 s,我一楼打错字了。

你没有修改吧?工作区是干净的

这个很奇怪,正常情况下 Unmerged into 底下不会显示 diff

可以试试看 magit-emacs-Q-command

命令看起来正常:

/usr/local/bin/emacs-nightly \
    -Q --eval \(setq\ debug-on-error\ t\) \
    -L /Users/gqj/.emacs.d/28.0.50/elpa/dash-20200119.2310/ \
    -L /Users/gqj/.emacs.d/28.0.50/elpa/transient-20200219.2113/ \
    -L /Users/gqj/.emacs.d/28.0.50/elpa/with-editor-20200217.1015/ \
    -L /Users/gqj/.emacs.d/28.0.50/elpa/magit-20200223.1224/ \
    -L /Users/gqj/.emacs.d/28.0.50/elpa/git-commit-20200207.1819/ \
    -l /Users/gqj/.emacs.d/28.0.50/elpa/magit-20200223.1224/magit

但是依旧无法 stage 部分修改。

刚开始还怀疑过是不是 mgit 使用了系统自带的旧版 git,结果也不是。

1 个赞

你是不是选择区域操作不对。在 magit 里用 n 或者 p 上下移动到有修改的部分(高亮),然后 s 就可以 stage 这一部分了。相当简单。

在unstaged区域C-n/C-p移动行,找到想stage的行数后C-SPC进行highlight,C-n/C-p选择相邻行(同时stage多行),最后按小写s进行stage。用了一年了,从没出过问题。

1 个赞

是否用了ws-butler?最新版的magit和ws-bulter冲突。症状跟你描述的很像(diff平铺不能折叠)。

如果存在冲突,可能出在 Magit 依赖的那几个包。因为我现在的测试环境:

  • 最小配置,只安装 magit 及其依赖的包。
  • Magit 2019-07-30

问题依旧。

用 edebug 过了一遍 magit-status-setup-buffer 函数,发现其中的两条关键语句都返回空:

  • (magit-get-section '((staged) (status)))
  • (magit-get-section '((unstaged) (status)))

所以我的 Status buffer 没有 Staged changesUnstaged changes 两个段。


又看了一下 magit-get-section 函数,似乎在它被调用之前,status buffer 就应该存在 stageed / unstaged 两个 section 的占位符:

(defun magit-get-section (ident &optional root)
  "Return the section identified by IDENT.
IDENT has to be a list as returned by `magit-section-ident'.
If optional ROOT is non-nil, then search in that section tree
instead of in the one whose root `magit-root-section' is."
  (setq ident (reverse ident))
  (let ((section (or root magit-root-section)))
    ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ;; root section 里面不包含 staged/unstaged,
    ;; 所以后面的代码也就没有意义了。因为找不到等待
    ;; 填充的空 section。问题应该在更早之前就产生了。
    ;;
    (when (eq (car (pop ident))                
              (oref section type))             
      (while (and ident
                  (pcase-let* ((`(,type . ,value) (car ident))
                               (value (magit-section-ident-value value)))
                    (setq section
                          (cl-find-if (lambda (section)
                                        (and (eq (oref section type) type)
                                             (equal (magit-section-ident-value
                                                     (oref section value))
                                                    value)))
                                      (oref section children)))))
        (pop ident))
      section)))

我在magit-20200224.2301Emacs 27.0.60。或许你可以先更新magit或者退回Emacs 27试试。

Unmerged into里面的是已经commit了的,那里只能revert/pick… region/hunk之类。有track了的文件被修改,才有Unstaged changes这一栏。

也许是描述不够清晰,让我感觉楼主根本没有 stage 的文件,当然 magit 就不会显示。我用最新版的 magit 在 vanilla Emacs 中也是可以操作的,没有任何问题。magit 质量很高,也很稳定。楼主不妨全部在 magit 中操作一次,把详细步骤贴出来让大家看看到底问题在哪里。