好记性比如烂笔头, 这是我个人总结的 Emacs 手册。
更多 Emacs 内容请访问我的博客。
1 文件 #
按键 | 函数 | 功能 |
---|---|---|
C-x i | insert-file | 在当前位置插入文件 |
C-x C-r | find-file-read-only | 只读模式打开文件(C-x r 是矩形编辑和寄存器的快捷键前缀) |
C-x C-f | find-file | 打开文件(C-x f 是设置 fill column) |
C-x 4 C-f | find-file-other-window | 在一个新窗口中打开文件 |
C-x 4 f | find-file-other-window | 同上 |
C-x 5 C-f | find-file-other-frame | 在一个新 frame 中打开文件 |
C-x 5 f | find-file-other-frame | 同上 |
C-x 5 r | find-file-read-only-other-frame | 在另一个 frame 中只读模式打开文件 |
C-x t f | find-file-other-tab | 在另一个 tab 中打开文件 |
C-x t C-f | find-file-other-tab | 在另一个 tab 中打开文件 |
C-x C-v | find-alternate-file | 打开一个文件,取代当前缓冲区(C-x v 是 VC 的前缀) |
C-x C-s | save-buffer | 保存当前缓冲区文件 |
C-x s | save-some-buffers | 保存某个缓冲区文件(依次询问) |
C-x C-w | write-file | 另存为新文件 |
C-x C-c | save-buffers-kill-emacs | 退出 Emacs |
C-x C-f
快速切换目录:
//
: 根目录;~
: 家目录;- TRAMP 模式下,三个连续
///
打开本地文件;
save-some-buffers
可以使用的快捷键:
- n、y、q、!
- C-r:view 模式查看要保存的 buffer;
2 重复 #
按键 | 函数 | 功能 |
---|---|---|
M-N | digit-argument | |
C-u | universal-argument | |
C-x z | repeat | 重复执行上一次名称,每按一次 z 字符就多执行一次 |
C-x M-: | repeat-complex-command | 重复执行需要从 minibuff 读取输入的命令(可以和命令历史记录结合使用) |
C-M-; | consult-complex-command | 重复执行复杂命令。 |
M–CMD | 反向执行 CMD 一次 | |
C-u– CMD | 同上 | |
C-u–N CMD | 反向执行 CMD N 次 |
C-x ESC ESC 或 C-M-;
适合于快速重复执行需要从 minibuff 读取输入的命令。univeral-argument
与digit-argument
的区别是:- 它可以不带数字参数,表示重复 4 次;
- 除了表示重复次数,不带数字的
univeral-argument
还有改变命令行为的特殊含义;
3 光标 #
按键 | 函数 | 功能 |
---|---|---|
C-f | forward-char | 前进一个字符 |
C-b | backward-char | 后退一个字符 |
M-f | forward-word | 前进一个单词 |
M-b | backward-word | 后退一个单词 |
c-a | beginning-of-line | 移到行首 |
C-e | end-of-line | 移到行尾 |
M-a | backward-sentence | 移到句首 |
M-e | forward-sentence | 移到句尾 |
C-p | previous-line | 后退一行 |
C-n | next-line | 前进一行 |
M-m | back-to-indentation | 移到当前行非空首字符 |
M-g M-g | goto-line | 跳转到指定行号 |
M-g g | goto-line | 同上 |
M-g c | goto-char | 跳转到指定字符位置 |
M-g TAB | move-to-column | 移动光标到当前行的第 N 列 |
C-v | scroll-up-command | 向下翻页 |
M-v | scroll-down-command | 向上翻页 |
C-M-v | scroll-other-window | 将临近的窗口中内容前翻 |
C-M-S-v | scroll-other-window-down | 将临近的窗口中内容后翻 |
C-M-f | forward-sexp | 向前匹配光标右侧的记号(如字符串,括号) |
C-M-b | backward-sexp | 向后匹配光标左侧的记号 |
C-M-a | beginning-of-defun | 向后移动光标到函数定义的开始 |
C-M-e | end-of-defun | 向前移动光标到函数定义的结束 |
C-M-n | forward-list | 按照括号向前移动 |
C-M-p | backward-list | 按照括号向后移动 |
C-M-u | backward-up-list | 跳转到当前括号的上一层次 |
C-M-d | down-list | 深入到当前行所在的下一级括号层次 |
C-M-k | kill-sexp | 删除当前 S 表达式 |
C-M-@ | mark-sexp | 标记当前 S 表达式 |
C-M-SPC | mark-sexp | 标记当前 S 表达式 |
C-M-h | mark-defun | 标记函数 |
C-M-q | prog-indent-sexp | 格式化当前 S 表达式 |
M-q | fill-paragraph | 重新填充当前段落 |
M-< | beginning-of-buffer | 缓冲区头部 |
M-> | end-of-buffer | 缓冲区尾部 |
M-r | move-to-window-top-buttom | 移到光标到中间行、屏幕头部、屏幕底部 |
C-u M-r | move-to-window-top-buttom | 移到光标到屏幕头部 |
M– M-r | move-to-window-top-buttom | 移到光标到屏幕底部 |
C-l | recenter-top-bottom | 将光标位置置于屏幕中间、头部、底部 |
C-u C-l | 将光标居中 | |
C-M-l | reposition-window | 将窗口调整到最佳大小 |
M-. | lsp-bridge-find-def | 跳转到定义位置 |
M-, | lsp-bridge-find-def-return | 跳转到上一次位置 |
M-? | lsp-bridge-find-references | 查找符号引用 |
M-g n | next-problem | |
M-g p | previous-problem | |
C-x = | what-cursor-position | 显示光标位置 |
C-\ | toogle-input-method | 切换输入法(M-\ 是删除光标附近的空格) |
对于 M-r
,数字参数指定了光标的位置:
- 数值参数是正值:从 window 头部开始的第 n 行;
- 数字参数是负值:从 windown 尾部开始的第 n 行;
- 特殊的 C-u – 或 M– M-r 则移动到 windown 尾部;
consult
提供跳转到 mark 和 global mark 的功能:
M-g m
: 跳转到 mark;M-g k
: 跳转到全局 mark;
consult
还提供了 consult-imenu 功能:
- M-g i: 查看当前 buffer 的项目;
- M-g I:查看当前 project 已打开的 buffer 的项目;
4 撤销 #
按键 | 函数 | 功能 |
---|---|---|
C-_ | undo | 撤消操作 |
C-/ | undo | 同上 |
C-x u | undo | 同上 |
C-x / | undo | 同上 |
连续按 undo 时一直撤销,但是中间可以用非 undo 命令如 C-f 来终止,这样后续再 undo 时会撤销刚才的 undo,达到 redo 的效果。
5 缓冲区 #
按键 | 函数 | 功能 |
---|---|---|
c-x b | 选择当前窗口的缓冲区 | |
C-x C-b | ibuffer | 打开分组缓冲区列表 |
C-x 4 b | switch-to-buffer-other-window | 选择当前窗口的缓冲区 |
C-x 5 b | switch-to-buffer-other-frame | 选择当前窗口的缓冲区 |
C-x k | kill-buffer | |
kill-some-buffers | 提示删除一些 buffers | |
eval-buffer | 执行当前缓冲区中的 lisp 语句 | |
rename-buffer | 重命名 buffer,当要打开多个 eshell 或 info 时需要 | |
rename-uniquely | 唯一重命名 | |
revert-buffer | 丢弃缓冲区自上次保存以来的所有改变 | |
M-~ | not-modified | 清除当前缓冲区的修改标志(保存时不会提示保存该文件) |
C-x C-q | read-only-mode | 切换缓冲区为只读或者读写模式 |
C-q | quoted-insert | quoted-insert |
M-/ | dabbrev-expand | 自动提示扩充当前单词 |
insert-buffer | 选择一个 Buffer,然后将它的内容插入当前 Buffer | |
C-M-/ | dabbrev-completion | 动态补全 |
ibuffer:两个快捷键前缀:/ 和 *
6 窗口 #
按键 | 函数 | 功能 |
---|---|---|
C-x 0 | 关闭本窗口 | |
C-x 1 | 只留当前窗口 | |
C-x 2 | 垂直均分窗口 | |
C-x 3 | 水平均分窗口 | |
C-x o | 切换到别的窗口 | |
C-x 4 o | 切换到别的窗口 | |
C-x 5 o | other-frame | 切换到别的 frame |
C-x 5 0 | delete-frame | deletes the selected frame |
C-x 5 1 | delete-other-frames | deletes all other frames |
C-x 5 2 | make-frame-command | 为当前 buffer 创建新的 frame |
C-x + | balance-windows | 把所有窗口调整为同样 大小 |
C-x - | shrink-window-if-larger-than-buffer | 如果编辑缓冲区比窗口小就压缩窗口面积 |
C-x { | shrink-window-horizontally | 水平缩小窗口 |
C-x } | enlarge-window-horizontally | 水平扩大窗口 |
C-x ^ | enlarge-window | 垂直扩大窗口 |
C-x < | scroll-left | 向左滚动窗口 |
C-x > | scroll-right | 向右滚动窗口 |
M-x winner-mode
: 启动 frame 窗口布局记忆机制,当一种窗口布局改变后可以用:
- C-c ←
- 命令恢复上一个布局
- C-c →
- 恢复下一个窗口布局
也可以使用寄存器来保存 frame 和 window 的布局:
C-c r f
: 保存 frame 布局;C-c r w
: 保存 window 布局;
consult 提供了两个更方便和通用的 register 操作:
- M-x consult-register: C-M-'
- M-x consult-register-store: C-'
C-] (command abort-recursive-edit)
命令可以在任意 buffer 关闭 minibuffer 的编辑模式。
7 显示 #
C-x n b | org-narrow-to-block | |
---|---|---|
C-x n s | org-narrow-to-subtree | |
C-x n n | narrow-to-region | |
C-x n p | narrow-to-page | |
C-x n d | narrow-to-defun | |
C-x n w | widen | |
C-x C-+、s - + | text-scale-increase | 放大显示的字体大小 |
C-x C–、s - - | text-scale-decrease | 缩小显示的字体大小 |
C-x C-0、s - 0 | text-scale-adjust | 恢复显示的字体大小 |
M-s h r regexp RET face RET | highlight-regexp | |
M-s h u regexp RET | unhighlight-regexp | |
M-s h l regexp RET face RET | highlight-lines-matching-regexp | |
M-s h . | highlight-symbol-at-point | |
M-x whitespace-mode | 显示 buffer 的空白字符(包括换行符) |
按一次 C-x C-+ 后,可以一直按 C-+,这样就持续放大或缩小。
8 标记 #
按键 | 函数 | 功能 |
---|---|---|
C-SPC | set-mark-command | 在当前位置设置标记并激活 |
C-@ | 同上 | |
C-SPC C-SPC | 设置标记,push 到 mark ring,但不激活 | |
C-u C-SPC | 跳转到上一个标记环中的位置 | |
C-u C-@ | 同上 | |
C-x C-SPC | pop-global-mark | 使用 global mark ring 跳转到对应 buffer 中的位置 |
C-x C-x | exchange-point-and-mark | 交换当前光标位置到上一个标记位置 |
M-@ | mark-word | |
C-M-@ | mark-sexp | |
C-M-SPC | mark-sexp | 标记语法区域 |
M-SPC | just-one-space | 只保留一个空格(M-\ 是删除光标附近所有空格和 TAB) |
C-x C-o | delete-blank-lines | 将多个空行合并为一个空行 |
M-h | mark-paragraph | |
C-M-h | mark-defun | |
M-= | count-words-region | 统计 region 内的字符数、行数等信息 |
C-x h | mark-whole-buffer |
在特定 buffer 中添加位置标记时,也会被自动添加到全局的标记环,使用它可以在多个buffer 中跳转。如果要临时跳转到其它位置,后续再返回当前位置,则通过 C-SPC C-SPC
做快速标记,后续再通过 C-u C-SPC
返回是个不错的方案。一般情况下,引起光标位置改变的操作,都会自动记录上一次位置,可以通过 C-u C-SPC
跳转到以前的历史位置。
Shift-Selection
:按住 Shift 键的同时执行一些光标移动命令,如 S-C-f S-C-n
等,也可以用来标记区域。
consult
提供跳转到 mark 和 global mark 的功能:
M-g m
: 跳转到 mark;M-g k
: 跳转到全局 mark;
9 寄存器 #
Emacs 寄存器可以保存多种类型记录:
- 光标位置记录;
- frame 或 window 配置记录;
- bookmark 记录;
- rectangle 内容记录等;
按键 | 函数 | 功能 |
---|---|---|
C-x r SPC r | point-to-register | |
C-x r j r | jump-to-register | Jump to the position |
C-x r s r | copy-to-register | Copy region into register r |
C-x r i r | insert-register | Insert text from register r |
M-x append-to-register <RET> r | Append region to text | |
M-x prepend-to-register <RET> r | Prepend region to text | |
C-x r f r | frameset-to-register | 保存 frame 配置,后续可以恢复当前 frame |
C-x r w r | window-configuration-to-register | 将当前 window 保存到寄器 |
C-x r d | delete-rectangle | |
C-x r k | kill-rectangle | |
C-x r M-w | copy-rectangle-as-kill | |
C-x r y | yank-rectangle | 粘贴上一次删除的矩形区块,插入点左上角 |
C-x r o | open-rectangle | 用空格插入选择的矩形块,选择的内容右移 |
C-x r N | rectangle-number-lines | 在选中的矩形块前插入序号 |
C-x r c | clear-rectangle | 用空格填充选择的矩形块 |
C-x r t | string-rectangle | 用指定字符填充选定的矩形区域 |
C-x r r | 拷贝矩形区域到寄存器中 | |
C-x r i r | 使用寄存器 r 的内容 | |
C-x r m <RET> | Set the bookmark for the visited file | |
C-x r m <bookmark> | bookmark-set | 在当前位置设置书签 |
C-x r b | bookmark-jump | 跳转到书签 |
M-x bookmark-rename | 重命名书签 | |
M-x bookmard-delete | 删除书签 | |
M-x bookmark-load | 从指定文件中加载书签 | |
M-x bookmark-insert | 将书签指向的文件的内容插到光标处 | |
M-x bookmard-write | 把书签全部保存到指定文件 | |
M-x bookmark-save | 将书签内容保存到文件 | |
C-x r l | bookmark-menu-list | 列出所有的书签 |
M-x view-register RET r | 查看寄存器 r 的内容 |
对于保存的 frame 或 window,可以使用 C-x r j
来恢复,从而实现类似于多 frame 切换且保持 window 布局的效果。
可以使用寄存器快速保存部分临时内容, 如:
- C-x r s
- 将 region 内容保存到寄存器;
- C-x r i
- 将寄存器内容插入到当前位置;
安装 consult 包后,在进行寄存器和书签跳转前,可以自动预览和过滤。同时还支持如下快捷特性:
- M-x consult-register-store, 根据上下文保存到寄存器;支持文件、window、frame、point;
- M-x consult-register-load, Do what I mean with a REG;
10 删除 #
按键 | 函数 | 功能 |
---|---|---|
C-d | delete-char | 删除插入点右边一个字符即光标所在的字符 |
M-d | kill-word | 删除一个单词 |
C-k | kill-line | 删除一行,注意:不删除行尾的换行符。 |
M-BACKSPACE | 反向删除一个 word | |
M-k | kill-sentence | 删除一句 |
M–C-k | 从行首删除到光标位置 | |
C-w | kill-region | 删除标记区域 |
M-w | kill-region-save | 复制标记区域 |
C-y | yank | 粘贴删除的内容 |
M-y | yank-pop | 循环粘贴粘贴板内容 |
C-M-k | kill-sexp | 向前剪切某个表达式,跨越的区域和 C-M-f 相同 |
C-o | open-line | 插入一个空行 |
C-x C-o | delete-blank-line | 删除光标附近的空行 |
M-^ | delete-indentation | 将当前行和上一行连接 |
M-\ | delete-horizontal-sapce | 删除光标附近的空格(C-\ 切换输入法,C-/ 撤销) |
M-SPC | just-one-space | 删除光标附近的空格,仅保留一个空格 |
C-S-Backspace | kill-whole-line | 删除当前行 |
M-z Char | zap-to-char | 向前删除到 Char 字符 |
M-x prepend-to-buffer | 将区域内容添加到缓冲区首 | |
M-x append-to-buffer | ||
M-x copy-to-buffer | 将内容拷贝到 buffer,删除 buffer 以前的内容 | |
M-x insert-buffer | 将内容插入到 buffer 当前位置 | |
M-x append-to-file |
- M– 和 M-z 反向命令前缀联合使用,可以实现快速的删除光标前面连续字符的功能。
- embark find-file 时,可以使用
C-M-Backspace
删除上级目录。
11 格式化 #
如果想在行内容达到指定长度(而非默认的 window edge)自动添加回车,则可以使用 Auto-Fill Mode
(modeline 显示 Fill) 。
Fill Paragraph 有两个功能:
- 为各行添加 Fill 前缀字符串;
- 为 Paragraph 的各行按照该 Paragraph 第一行缩进;
Auto-Fill 或 fill-paragraph 都是针对当前 paragraph 的,而 Emacs 使用缩进或至少两个空行来识别
paragraph 的。如果一个 region 是多个缩进的行,则 Emacs 认为它们是多个 paragraph,这时如果想整体
Fill, 则需要使用使用 fill-indiviual-paragraphs
命令。
与 fill 相关的还有 indented-text-mode, 它是一个编辑 text 的 major mode(modeline 显示 Indented Text Fill
), 且必须和 Auto-Fill minor mode 结合使用, 只要把第一行缩进, Emacs 就会把整段文字都自动缩进。如果要退出该 Mode, 则可以调用 M-x text-mode 命令。
如果一行内容超过了屏幕长度,Emacs 会自动将它作多行显示,这时每行行尾有一个箭头示意。每行的原始内容称为 logical line,展示的多行称为 screen line,这种行为也称为 line wrapping or continuation(一般是在 window edge 开始 wrapping 的。)
Emacs 命令,如 C-a、C-e, 是按照 screen line 来移动的。如果想在 window edge 自动 line wrapping,但是 C-n、C-p等按照 logical line 来移动,则可以使用 Visual Line Mode
。
如果安装了 visual-fill-column
,则它提供的 visual-fill-column-mode
则结合了 Visual Line Mode 和
Auto-Fill Mode 的特性,可以在行内容达到指定长度(默认使用 fill-column,但如果设置了
visual-fill-column-width 则以它为准)后自动 line wrapping,同时按照 logical line 来执行命令。
按键 | 函数 | 功能 |
---|---|---|
M-x auto-fill-mode | 自动 fill 模式,只对输入的段落有效。 | |
C-x . | set-fill-prefix | 设置 Fill 前缀字符串 |
C-x f | set-fill-column | 设置列边界 |
M-q | fill-paragraph | fill 当前段落 |
M-x fill-individual-paragraphs | fill 多个段落 | |
M-x refill-mode | 自动 fill 模式,对整个文件有效 | |
M-x display-fill-column-indicator-mode | 展示 fill-column 位置的边界线 | |
C-M-\ | indent-region | 按照代码风格格式化当前段落。 |
C-x <TAB> | indent-rigidly | 对选中区域使用左右箭头进行缩进 |
M-^ | delete-indentation | 删除当前行的缩进,与上一行连接 |
M-m | back-to-indentation | 跳转到当前行第一个缩进字符 |
C-j | 换行并缩进 | |
M-j | default-indent-new-line | |
C-M-j | indent-new-comment-line | 换行,如果位于注释中则继续插入一个注释行 |
C-M-o | split-line | 将行拆分为台阶排列的两行 |
C-x ; | comment-set-column | 设置 comment 列位置 |
C-x C-; | comment-line | |
M-; | comment-dwim | |
M-x white-space-mode | 显示当前 buffer 中的空白字符 | |
M-x tabify | 将当前选中的内容中连续的空格转变为 tab | |
M-x untabify | 将当前选中的内容中的 tab 转换为空格 | |
M-x center-line | 将行内容居中 | |
M-x center-paragraph | 将段落居中 | |
M-x center-region | 将选中的区域内容居中 |
Lisp 注释:如果是两个以上 ; 开头,则格式化的时候不移动注释位置。否则,按照 comment-set-column
的位置来移动注释。
12 调换 #
按键 | 函数 | 功能 |
---|---|---|
C-t | transport-chars | 将光标所在的字符和前一个字符对调 |
M-t | transport-words | 将光标所在 word 与前一个 word 对调 |
C-M-t | transport-sexps | Transpose two balanced expressions |
C-x C-t | transport-lines | 将光标所在的行与上一行对调 |
13 大小写 #
按键 | 函数 |
---|---|
M-u | upcase-word |
M-l | downcase-word |
M-c | capitalize-word |
C-x C-u | upcase-region |
C-x C-l | downcase-region |
14 搜索 #
-
C-s key:isearch-forward 向前增量搜索
-
C-r key:isearch-backward 反向增量搜索
-
非增量搜索模式
-
进入反向非增量搜索模式
搜索默认大小写不敏感,但如果搜索字符串中包含大写字母,则大小写敏感。
搜索过程中,可以使用 M-e(isearch-edit-string)
编辑搜索字符串。
搜索过程中可以使用的快捷键:
-
C-s: 查找下一个,ENTER 停止搜索
-
C-r: 查找上一个,ENTER 停止搜索
-
C-g: isearch-abort 停止搜索
-
M-n、M-p: 查找命令历史记录
-
C-y: isearch-yank-kill 粘贴删除环中文本
-
M-y: isearch-yank-pop 循环粘贴删除环中的文本
-
C-w: isearch-yank-word-or-char 将光标所在位置到下一个标点符号或空格符间的文本添加到搜索字符串中
-
C-M-w: isearch-yank-symbol-or-char 将光标所在的字符或 symbol 添加到搜索字符串中
-
C-M-y: isearch-yank-char appends the character after point to the search string
-
C-M-z: isearch-yank-until-char appends to the search string everything from point until the next occurrence of a specified character
-
C-M-d: isearch-del-char deletes the last character from the search string
-
M-s C-e: isearch-yank-line 将光标所在的位置到行尾的内容添加到搜索字符串中
-
M-c: 大小写敏感模式切换
-
M-r: 正则匹配模式切换
-
M-%: 切换到 query-replace 替换模式
-
C-M-%: 切换到 query-replace-regexp 替换模式
-
M-s o: 切换到 occur 模式;
-
M-s h r: (isearch-highlight-regexp): 切换到 highlight 模式;
单词搜索模式:上面都是字符串完整匹配的搜索模式,如果要搜索多个字符串同时忽略它们之间的标点和换行符,则可以使用单词搜索模式: M-s w
是单词搜索模式命令前缀:
M-s w
:isearch-forward-word 增量式单词搜索(在 C-s 或 C-r 命令中使用时切换到单词搜索模式)。M-s w C-r words
:反向增量式单词搜索M-s w <RET> words <RET>
:非增量式单词搜索M-s w C-r <RET> words <RET>
: 反向非增量式单词搜索
标识符(symbol)搜索模式:按照 symbol 搜索(非常适合搜索代码的标识符和字符串):
M-s .
:isearch-forward-symbol-at-point 搜索光标附近的 SymbolM-s _
:isearch-forward-symbol 按照 symbol 进行增量搜索M-s _ RET symbol RET
: Search forward for symbol, nonincrementally.M-s _ C-r RET symbol RET
: Search backward for symbol, nonincrementally.
正则表达式搜索:
C-M-s
:isearch-forward-regexp 向前增量式正则搜索C-M-r
:isearch-backward-regexp 向后增量式正则搜索C-M-s <RET>
:向前非增量式正则搜索C-M-r <RET>
:向后非增量式正则搜索
Occur:
- M-s o:输入一个 regexp,然后显示当前 buffer 中匹配该正则的 line 列表
- M-x multi-occur:和 occur 类似,但可以选择多个 buffer 来进行搜索
- M-x multi-occur-in-matching-buffers:先执行 buffer name 的 regexp,然后搜索
Occur Buffer 快捷键:
-
e:编辑 Occur Buffer,然后 C-c C-c 保存;C-g 丢弃修改;
-
q: 终止 buffer;
-
c:clone Occur buffer;
-
r: rename buffer;
-
g:刷新 buffer;
-
h:显示帮助
-
C-c C-f: next-error-follow-minor-mode,关闭跟随光标的预览模式;
-
n:next-error-no-select
-
p:previous-error-no-select
-
l:recenter-current-error,将光标位置的原始行居中显示;
-
M-n、M-p:跳转到下一个或上一个匹配记录,不预览显示内容;
-
RET、C-c C-c:在新窗口显示当前记录对应的文件;
-
o:在新窗口打开当前记录对应的文件,并移动光标到对应窗口;
-
C-o:在新窗口打开当前记录对应的文件,光标不移动;
安装 consult 后,可以通过 M-s l/L
来执行 consult-line/consult-multi-line
,利用它的自动补全功能快速选择多个 buffer。
Grep mode:
M-x grep、M-x lgrep (local grep)
:Run grep asynchronously under Emacs, listing matching lines in the buffer named grep.M-x grep-find、M-x find-grep、M-x rgrep (recursive grep)
:Run grep via find, and collect output in the grep buffer.M-x zrgrep
:Run zgrep and collect output in the grep buffer.M-x kill-grep
:Kill the running grep subprocess.
Your command need not simply run grep; you can use any shell command that produces output in the
same format. For instance, you can chain grep commands, like this: grep -nH -e foo *.el | grep bar | grep toto
。The output from grep goes in the *grep* buffer
. You can find the corresponding
lines in the original files using M-g M-n, RET, and so forth, just like compilation errors
.
grep、occur、riggrep 的 buffer 都继承自 compile mode,它们的 buffer 快捷键类似:
-
TAB : compilation-next-error
-
RET : compile-goto-error
-
C-o : compilation-display-error
-
< : beginning-of-buffer
-
> : end-of-buffer
-
? : describe-mode
-
e : consult-compile-error
-
g : recompile
-
h : describe-mode
-
q : quit-window
-
C-c C-p : wgrep-change-to-wgrep-mode,wgrep 编辑模式;
-
C-c C-f : next-error-follow-minor-mode
-
n、p : 上一个或下一个 error,会在其它窗口显示对应内容
-
M-n : compilation-next-error,不显示内容
-
M-p : compilation-previous-error,不显示内容
-
{ 或 M-{ : compilation-previous-file
-
} 或 M-} : compilation-next-file
-
C-c C-c : compile-goto-error
-
C-c C-k : kill-compilation
如果安装了 wgrep-mode,则在 grep buffer 中按 C-c C-p 来进行批量编辑。
14.1 搜索文件内容 consult grep #
- M-s g: consult-grep
- M-s G: consult-git-grep
- M-s r: consult-ripgrep
对于上述搜索结果 Buffer,可以使用 embark-export 将其保存到一个 Grep 类型的 buffer,然后就可以使用 Grep Mode 的快捷键和命令进行处理,如 批量编辑(C-c C-p,切换到 wgrep 模式)。
15 替换 #
Replace 是从光标到 buffer 尾部,但如果有选中 Region,则只会替换该 Region 内容。
- M-x replace-string 从光标到 buff 尾全局字符串替换
- C-u M-x replace-string 同上,但是只在 word 边界处替换
- M-x replace-regexp 从光标到 buff 尾全局符合正则表达式的字符串替换
- M-% query-replace 交互替换
- C-M-% query-regexp 交互式正则表达式替换
Query replace 过程中可以使用的快捷键:
- e 编辑替换字符串
- y 或 SPACE 确定替换当前位置,然后前进到下一个
- n 或 DEL 不替换,前进到下一位置
- . 在当前位置替换后退出查询替换操作
- , 替换并显示替换情况(按空格或 y 继续,适合替换过程中先暂停的情况)
- ! 对后面的内容全部替换,不再询问回车或 q 退出查找替换
- ^ 返回上一次替换位置
- u to undo the last replacement and go back to where that replacement was made.
- U to undo all the replacements and go back to where the first replacement was made.
进入查找-替换模式后,按 C-r 进入递归编辑,或 C-w 删除此次内容并进入递归编辑模式:
- C-M-x 退出递归编辑模式,返回到查找/替换模式 (exist)
- C-M-c 同上(cancel)
- C-] 退出递归编辑和查找替换模式
其它替换命令:
- M-x projectile-replace:在 project 级别搜索或替换文件。
- M-x dired-do-find-regexp-and-replace:在 dired mode 上选中文件,按 Q 进行查询替换;
在 grep buffer 中使用 wgrep 进行批量编辑。
16 magit #
16.1 Core #
C-x g | magit-status | 显示当前 buffer 对应的 git project status |
---|---|---|
C-x M-g | magit-dispatch | 在小的 buffer window 中显示当前可以执行的 magit 快捷键命令。 |
C-c M-g | magit-file-dispatch | 在小的 buffer window 中显示可以对当前 file 执行的 magit 命令。 |
在 magit-status buffer 中,执行的命令与当前光标所在位置有关系,如在某一个 commit 上时,SPACE 会显示该 commit 的内容,d 会显示前后两个 commit 的差别:
- ?/h:根据光标所在的 buffer,显示对应的帮助菜单。
- $:显示 git process 的输出内容窗口,用于 debug, 用 q 关闭 debug 窗口。
- k:discard:
- 当光标在 stage 位置时,丢弃 stage 和 worktree 中的内容。
- 当在 unstage 位置时,丢弃 worktree 的修改。
- 当在 untrack 位置时删除文件。
- v:reverse a changing to worktree, 可以是 staged 或者 commited.
- s: stage 当前的改动, 可以在 file 级别,也可以在 hunk 级别.
- S: 将所有 unstaged 的变动提交到 Stage
- u: unstage 当前的变动, 可以在 file 级别,也可以在 hunk 级别.
- U: unstage 所有的变动.
- g: 刷新 magit buffer.
C-SPACE:标记当前 file 或 hunk 区域,最后用 s(stage)、u(unstage) 操作标记的区域。也可以用来在 Commit 或 log 列表中批量选中 commit,后续可以 apply 或 revert。
移动:
- n/p: 在 section 或者 section 内部的 hunk 之间移动;
- M-n/M-p: 在 slibling section 之间移动;
- ^:移动到 section 的上一级(不是 u,u 的含义是 unstage);
显示和展开:
- TAB 展开当前 section
- C-TAB:循环展示当前 section 和它的 children;而 TAB 是直接展开所有 children 的内容。
- Enter:访问当前 section 的文件
- 1-4:分别在当前 section 的 1-4 级之间之间展开
- M 1-4: 分别在所有 section 的 1-4 级之间展开
执行命令:
- !: 在当前工作目录或 git root 目录运行 git 或 shell 命令
- C-g: 终止当前的 git 命令
C-c M-g: magit-file-dispatch 针对当前文件,支持:
-
stage、unstage、commit 当前文件;
-
Diff 和 diff 当前文件与其它 commit 或 master 的差别;其中 Diff 可以查看的更多样:
- dwim: 功能同 Diff range;
- Diff range: 提示输入比较的 commit ref,然后比较 workspace 当前文件与它的的差别;ref 可以使用 HEAD~N 语法。
- Diff paths: 提示输入两个路径的文件,然后显示差别;
- Diff unstaged: 显示当前文件 unstaged changes;
- Diff staged:显示当前文件 staged changes;
- Diff worktree: 显示当前文件在 HEAD 和 working tree 之间的差别;
- Show commit: 显示某一个 commit 中当前文件的变更;
-
status(g): 整个 workspace 当前的状态(untracked、unstaged、staged 等状态文件)。
-
Log/log: 查看当前文件的历史 commit;其中 Log 功能更丰富:
- current: 展示当前文件在当前分支的历史 commit;
- other:展示当前文件在其它分支或 commit 中的更新情况;
- head: 展示在 HEAD 对应分支中,当前文件的历史 commit 情况;
- Local Branchs: 展示在所有本地分支中,当前文件的 commit 情况;
- all branchs: 除了所有本地分支外,也包括远程分支,当前文件的 commit 情况;
- all reference: 展示所有分支中当前文件的 commit 情况;
-
trace: 查看光标处所在函数或代码的修改历史(也可以选中区域),如下面的 main -L:sign_request 表示 main 分支的sign_request 函数的修改历史:
16.2 Branch #
在 magit-log 页面,用蓝色表示当前所处的分支:
- b: branch/revision
- checkout 本地或 remote 分支,如果是本地分支则切换过去,如果是 remote 分支,则 HEAD 会变成 detached(因为它不会为 remote 分支创建本地分支)。指定的分支必须存在,不创建新分支。
- l: local branch
- checkout 本地或 remote 分支,如果是本地则切换过去。如果是远程,则本地创建创建一个同名的 track 分支(自动将本地分支 track remote 分支)。如果是一个新的分支名,则会提示它的 starting-point,并自动 track 这个分支。
- c: new branch
- Create and checkout BRANCH at branch or revision START-POINT. 创建并 checkout 到新的分支(并不设置 track remote 分支),如果当前分支有未提交的修改则失败。
- s: new spin-off
- Create new branch from the unpushed commits.
spin-off 是基于当前分支 checkout 一个新的分支,然后将旧的分支重置到上次和 upstream 同步的位置。如果旧的分支没有 upstream 或者没有 unpush 的 commit,则老分支不变。这非常适合在旧的 branch 上提交了一些 commit 但没有 push 到远程分支,想把这些改动转移到新的特性分支的情况:老分支未 commit 的改动将体现在新的分支中。例如:当前是 add-test 分支,并有一些 unstage 的修改,则 new spin-off 创建一个新的 next-test-spinoff 分支,并将 unstage 的内容保留到这个分支:
- n: new branch
- Create BRANCH at branch or revision START-POINT. 创建分支但是不 checkout。
- S: new spin-out
- 从 unpushed commits 位置创建新的分支,但是不 checkout,当前分支不变。如果当前分支有 uncommitted changes,则和 spin-off 类似,会 checkout 这个新的分支。
小技巧:
- 如果想基于历史 commit 创建一个 branch,可以先用 l l 展示当前分支 log,然后移动到目标 commit,再执行上述 branch 命令,则会提示以目标 commit 创建 branch。
16.3 Stash #
git stash 保存当前工作进度,把暂存区和工作区的改动保存起来,然后当前是一个干净的工作区。
- git stash save ‘message…’
- 添加注释。
- git stash list
- 显示保存进度的列表。
git stash pop [–index] [stash_id] - git stash pop: 恢复最新的进度到工作区。git 默认会把工作区和暂存区的改动都恢复到工作区。 - git stash pop –index: 恢复最新的进度到工作区和暂存区。(尝试将原来暂存区的改动还恢复到暂存区) - git stash pop stash@{1}: 恢复指定的进度到工作区。stash_id 是通过 git stash list 命令得到的。通过 git stash pop 命令恢复进度后,会删除当前进度。
- git stash apply [–index] [stash_id]
- 除了不删除恢复的进度之外,其余和 git stash pop 命令一样。
- git stash drop [stash_id]
- 删除一个存储的进度。如果不指定 stash_id,则默认删除最新的存储进度。
- git stash clear
- 删除所有存储的进度。
magit 提供了 stash 和 snapshot 两种选择:https://emacs.stackexchange.com/a/22482
对于 snapshot,magit 会创建一个 WIP commit,当前 working tree 内容不变。
Both the “stash” and “snapshot” variants create the same stash objects. The difference is that when
you create a snapshot, then the stashed changes are not removed
from the files in the working tree
and/or the index. (Just like when you take a snapshot of your friends having a good time - that
doesn’t cause them to disappear either ;-)
This is intended as a backup mechanism of sorts. Say you are performing some complicated refactoring and you just tested and the modified code still appears to work but you are not done yet. Now would be a good time to create a snapshot, so that you have something to go back to if you mess it up later.
Of course you could just create a temporary "wip" commit
, right on the branch you are working on, to
accomplish the same. That’s usually what I do.
And you can also automate the process of recording work-in-progress by enabling the Wip modes. I do have these modes enabled as a safety net, but I still create wip commits directly on the current branch or create a snapshot. Those are easier to work with than the wip refs.
Note that Magit comes with its own stash implementation written in Elisp. That was necessary to implement the snapshot variants and the worktree-only and index-only stash variants. Git doesn’t provide any of these variants.
16.4 Commit #
修改当前 HEAD:
- a Amend
- add the staged changes to HEAD and edit its commit message
- e Extend
- add the staged changes to HEAD without editing the commit message
- w Reword
- change the message of HEAD without adding the staged changes to it
修改历史 Commit(如果当前没有 stage 修改,则不做任何操作):
- f Fixup
- 选择一个历史 commit,然后将当前 stage 的修改合并进去,创建一个新的 commit,commit msg 是 fixup! 前缀 + 选中的历史 commit msg;
- s Squash
- 选择一个历史 commit,然后将当前 stage 的修改合并进去,创建一个新的 commit,commit msg 是 squash! 前缀 + 选中的历史 commit msg;
- A Argument
- 和 s Squash 类似,也是创建一个 squash commit,但是可以修改 squash message.
效果如下:
后续通过 r i (interactive) 进行 rebase 前,打开 –autosquash 选项,这样会自动将 Commit 进行 fixup 或 squash:
git 使用 fixup! 或 squash! 后的 msg 来匹配历史 commit,然后 rebase 时加到相应commit 的后面:
上面的 Fixup、Squash 还有 Instance 版本,它们是立即启动 rebase,将当前 stage 的内容自动 rebase 到选择的历史commit 中:
- F:Instance fixup
- S:Instance squash
在提交 msg 编辑界面:
- C-c C-c:提交 commit
- C-c C-k:cancel commit
- M-p M-n:使用上一次或下一次的 commit message
16.5 Diff #
magit 模式是使用 Contex 模式来展示 diff 内容。如果想 side-by-side 则需要使用 ediff 模式。
- d p: 选择两个路径文件,然后比较内容
M-x ediff: 选择两个文件进行比较。
ediff 在一个单独的 frame 显示一个 ediff control panel,使用 C-x 5 o 切换到该 frame。
- ~
- rotate ediff window 的布局, 可以通过 buffer name 来判断各自显示的内容。
- |
- 在水平和垂直窗口布局间切换;
- m
- 最大化 frame,特别适合水平布局的情况;
- C-x 5 o
- 显示隐藏的 ediff panel;
- A/B/C
- 将 buffer a、b、c 设置为只读。
- ?
- 显示 ediff control panel 的帮助菜单,再次按 ? 会隐藏菜单。
- n、p
- 下一个或上一个 diff 位置。
- j
- 跳转到第一个 diff 位置。nj: n 为数字,表示跳转到第 n 个 diff 位置。
- g a/b/c
- 将视图定位到 a/b/c buffer,这样后续该 buffer 中的 diff 总是处于可见区域的中间位置。
- v、V
- 在当前 diff 位置上移或下移滚动,用于查看 diff 上下文信息。
- h
- 切换 highlight 的风格:
- 高亮所有 diff 区域;
- 只高亮当前 diff 区域;
- 使用 ascii 标识 diff 区域;
- |
- 在水平和垂直方向上切换当前显示的方式。
- </>
- 水平向左或向右滚动显示所有 buffer。
- #f
- 提示输出各 buffer 匹配的正则表达式,后续只显示匹配这些正则的 diff 区域。后续再次按 #f 取消选择。
#h ::和 #f 类似,但是隐藏匹配的 diff 预期。后续再次按 #h 取消隐藏。
- w a/b/c
- 将 buffer a、b、c 的内容保存到 新的文件 中。
- wd
- 将 buffer b 和 c 的 diff 内容保存到新的文件中。
- D
- 在单独的 buffer 中显示指定的两个 buffer 的 diff 差别。
- z
- 将当前 diff session 保存到后台,后续可以使用 M-x eregistry 命令查看暂存的 session,非常适合有多个 ediff session 的情况;
- q
- 终止 diff session。如果前面修改了 buffer 内容,会提示 save buffer。
- !
- 刷新 diff region,更新 diff 区域数量。
注:
- 如果 ediff panel frame 没有在单独的 frame 中显示,则可使用 C-x b 切换到该 buffer,然后使用 ? 来恢复。
- 在 macos 系统下,需要将 ns-use-native-fullscreen 和 ns-use-fullscreen-animation 设置为 nil,否则显示 ediff panel 时有问题。
- which-key 可能会导致 ediff 的 gX 命令 hang,这时可以发送 USR2 信号来重新激活 Emacs;
- update 2021.09.18: 下线 which-key,配置 (setq prefix-help-command #’embark-prefix-help-command) 后,可以使用 C-h 来显示匹配前缀的命令。
ediff 的 buffer 两种类型:
- diff view:两个 buffer;
- merge view:三个 buffer,第一个是 HEAD,第二个是 Index(Stage),第三个是 Workspace;
在 magit 的 unstage、staged 区域的某个 diff 上:
-
按 e:三窗口的 merge view。
-
按 E:
- u(show unstaged): 显示 unstaged 区域的文件与 HEAD 的差别。
- i(show staged): 显示 stage 区域的文件与 HEAD 的差别。
- w(show worktree):显示 workspace文件与 HEAD 的差别。
上面三个 show xxx,都是显示两个 buffer,A 为只读的 HEAD,b 为 unstage、staged 或 worktree 中的文件,可以实现用 index 或 commit 的内容恢复 workspace 的修改。
- E(dwim) 或者 s(staged): 和上面直接按 e 类似,显示三窗口的 merge view。
- c(show commit): 显示指定的 commit 的内容,两窗口 diff,指定一个 commit,然后 diff 它和上一次 commit 的差别。
- r(show range): 两窗口 diff,指定一个 commit,显示和当前 workspace 文件的差别,可以用于从历史恢复当前文件的变更。
三窗口 merge view:
- 第一个是 HEAD,只读状态;
- 第二个是 Index(Stage),可读写状态;
- 第三个是 Worktree,可读写状态。
可以修改 index 和 workspace 中的内容,实现将 workspace 内容(可以部分保存)保存到 index 的效果,或者将 index 或 HEAD 的修改保存到 Workspace的效果。
内容拷贝:
- 两窗口的情况:a、b:a 表示把 a buffer diff 内容拷贝到 b,反之亦然。
- 三窗口的情况:ab、ac、bc、cb:将前一个 buffer 当前 diff 区域拷贝到第二个 buffer。
- a buffer 是 HEAD 的内容,不能修改,所以没有 ba、ca。
内容恢复:
- ra、rb、rc:将对应 buffer 当前 diff 区域的内容恢复到该 buffer 最开始的内容。
merge: 出现三个窗口,上面两个是冲突的版本,最下面是合并后的版本,可以将 A 或 B 的内容拷贝到 C,退出时提示保存,从而解决冲突。
magit-find-file:指定一个文件的 revision,可以查看该文件的内容。
16.6 Fetch #
- fa:将 remote 仓库的所有 branch、tag 等拉取到本地;
16.7 Push #
P:push ::
- p:push 到上游仓库
- u:另一个上游仓库
16.8 Log #
可以按作者、Commit Msg、修改的内容、 文件等条件搜索历史:
可以查看当前 branch、指定 branch 或所有 branch 的 commit log:
- SPACE: 显示当前 commit 的内容
- DELETE:反向显示当前 commit 的内容
- TAB:显示当前 commit 的内容
- Enter:显示当前 commit 的内容,并切换到 commit buffer 中,按 q 可以关闭该 buffer。
- +: 显示更多 commit
- -:显示更 少 commit
- C-c C-n:移动到当前 commit 的 parent commit
在查看 commit 内容 buffer (magit-revision) 可以使用
- M-[0-9]
- 来显示和隐藏变化内容:
- C-SPC
- 标记修改内容;然后用 n/p 来选中下一行或上一行;
鼠标放到某个修改位置,然后按 回车,可以查看该位置 commit 后的文件内容;然后按 n/p 可以切换到上一次 commit 或下一次 commit 后的文件内容。
L: 修改 log 显示的信息,如 singlestat、margin 等
小技巧:C-c M-g l 查看当前文件在 当前分支
的提交记录,这时按 l a 则可以看到当前文件在 所有分支
的提交记录,然后就可以按 A 或 a 来 Apply 某个 commit 到当前分支。
16.9 Merge #
- i: Dissolve(merge into): 将当前分支内容 merge 到其它分支,然后删除当前分支,并切换到 merge into 的分支:
- a: Absorb 将另一个 branch merge 进当前 branch,然后删除那个分支。
- s: squash merge 将指定分支的修改合并到当前分支,但是不创建 commit。注意:指定分支的多次 commit 内容会合并到当前 worktree,这样后续 commit 时,只会看到一次提交(而不管指定分支有多少次历史提交)。 squash 的含义就是merge 历史合并。在 rebase 时也会使用。
如果只是想把其它分支的 commit 应用到当前分支,除了 merge 外,还可以使用 Appply(A 或 a) 或 Cherry(Y)。
为了得到线性、干净的历史提交记录,在将当前分支 merge 到主干前,可以先将它 rebase 到主干分支(期间还可以修改历史提交记录),这样后续在 merge 时会得到一个线性的提交记录。
如果 merge 出现冲突,magit 会在 magit-status(C-x g)
buffer 的 unstage 或 stage change section,而且行首有unmerged 的字符串提示。可以在 unmerge 的位置按 k 丢弃 apply,或者按 e 使用 ediff 解决冲突。
16.10 Cherry/Apply #
Cherry 和 Apply 都是将其它 Commit merge 到到当前分支, 在执行相关命令之前需要先切换到要合并到的目标分支。
- Y (Cherries)
- 先输入 HEAD,再输入 UPSTREAM,显示 HEAD 可以 cherry pick 到 UPSTREAM 的 commit
列表,然后使用 Aa、AA 或 a 来选择性的 apply 到当前 branch。
需要先把当前 branch 切换到 UPSTREAM
,这样后续才能使用各种 Apply 命令。 - A 或 a(Apply)
- 是 Cherry 的快捷方式,用于将一个或多个 commit 快速应用到当前分支。
使用流程:
- Checkout the branch which you want to add the commit to, b b then select branch name, eg. master.
- Still in Magit, l a to open the log buffer and show log for all git references (commits, stashes, etc).
- Move the cursor to the commit you wish to cherry pick from.
A A
topick + stage + commit
to the currently checked-out branch.- Or use
A
a to pick and stage if you want toedit the change before committing it
to the currently checked-out branch.
示例: 把 origin/Ark-v19.xR-zArm_fs 的部分 commit merge 到 origin/Ark-sm-kylin 分支中:
- Cherry head: 选择提供 commit 的分支 origin/Ark-v19.xR-zArm_fs;
- Cherry upstream 选择 Ark-sm-kylin;
- 出现 commit cherry pick 列表:
- 以 - 号开始的表示已经 pick 过;
- 以 + 号开始的表示没有 pick 过;
可以在 Magit 的所有 commit 上执行 AA 或 Aa 或 a 命令来 Apply 这个 commit 到当前 branch。可以使用 C-SPC 来选中多个 commits,然后批量 Apply 或其它操作。
A A:Pick(magit-cherry-copy, 为 pick+stage+commit):
- 将光标处的或者选中的多个 commit 拷贝到当前 branch,并提示 commit message,如果选中多个 commit,则直接 pick 它们,不提示编辑 commit msg。
A a 或者 a 命令 (magit-cherry-apply, 为 pick+stage):
- 将光标处或选中的 commit cherry apply 到当前分支,cherry apply 只是在 worktree 中 appy changes,
并不 commit
,后续 commit 时默认使用当前的 commit msg。如果选中了多个 commit,则直接 apply。
Cherry apply 有可能失败,这时 worktree 中会提示冲突,需要解决冲突并 stage 后按 A 继续;
可以在 unmerge 的位置按 k 丢弃 apply,或者按 e 使用 ediff 解决冲突,然后按 A 继续、忽略或终止。
下面这些命令都是将 commit apply 到 some branch,但是这些 commit 也会被从以前的分支移除,以前分支和当前分支都可能出现冲突,需要解决完冲突后才能继续:
- A h (magit-cherry-harvest)
- 将其它分支的 commit 合并到当前分支;
- A d (magit-cherry-donate)
- 将当前分支的 comit 合并到其它分支;
- A n (magit-cherry-spinout)
- 将当前分支的 commit 移动到一个新的分支,结束后当前分支不变;
- A s (magit-cherry-spinoff)
- 将当前分支的 commit 移动到一个新的分支,结束后新的分支会被 checkout;
在 cherrk-pick 进行的过程中,可以执行如下命令:
- A A (magit-sequence-continue)
- Resume the current cherry-pick or revert sequence.
- A s (magit-sequence-skip)
- Skip the stopped at commit during a cherry-pick or revert sequence.
- A a (magit-sequence-abort)
- Abort the current cherry-pick or revert sequence. This discards all changes made since the sequence started.
16.11 Reset #
Reset 类型:
- m mixed:reset HEAD and index;
- s soft:reset HEAD Only;
- h hard:reset HEAD、index 和 files;
- k keep:reset HEAD 和 index,但是保存 uncommitted 的 files;
- i index only
- w worktree:只 reset worktree 内容到指定 commit,HEAD 和 index 不变(即提交历史不变,已经 stage 但为 commit的内容还在,但是 unstage 的内容会被 reset);
- f a file:reset file 到某个 commit;
mixed、soft 命令 reset HEAD 或 index 后,worktree 内容不变,即 reset 到的 commit 之后的变更都还在 worktree 的unstaged 区域中:
但 hard 命令将 HEAD、index 和 worktree 都 reset 到指定 commit 的状态(丢失 commit 以后的变更)。
先切换到要 reset 的分支,然后按 X (reset), 选择 h(reset 所有内容),然后输入要 reset 到的 commit 位置:
- 指定 log 中显示的 7 位 commitid;
- 或者相对 commit,如 HEAD
1、HEAD2;
小技巧:切换到 reset 分支,然后按 l l 显示当前分支 log 历史,然后移动到要 reset 到的 commit 位置,按 X h,这样就不需要手动输入 commitid 了。
全局快捷键 x(magit-reset-quickly)
将当前分支的 HEAD 和 index reset 到指定的 Commit,该 Commit 之后的更新保存到 worktree 的 unstated 区域中:
16.12 Revert #
Revert 是创建一个相反的 Commit 来达到清除某次提交全部或部分变更的效果。
使用场景:
- Revert 某个 Commit:在 log 中选择某个 commit,然后按 V V,提示 revert 某个commit,然后出现 commit 界面,自动填充 commit msg:Revert xxx;
- Revert 某个 Commit 中的个别 change:可以在 commit 的 change list 中选择每个change,然后按 V v(Revert),自动创建一个可以 revert commit change 的修改,并 stage 保存到 worktree 中,提示 Revert 进行中,可以按 A 选择
action 来继续,终止; V v 有
全局快捷键 v
。
在 Revert 的过程中,由于会创建一个 Revert Change,可能与当前 worktree 的内容冲突,这时 Revert 会暂停,需要手动解决冲突后继续(也可以按 A 然后选择 abort 中断Revert 过程):
解决冲突后,如果 stage 不为空,则 A A 会创建一个 Revert Commit。如果 stage 为空,则说明没有需要 commit 的内容,这时可以 A a(abort) 或 A s(skip) 结束 Revert 过程。
16.13 Rebase #
Rebase 原理,以将 feature1 分支 rebase 到 master 为例:
- git 把 feature1 分支里面的每个 commit 取消掉;
- 把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;
- 把 feature1 分支 HEAD 指向最新的 master 分支;
- 把上面保存的 patch 文件应用到 feature1 分支上;(由于以master分支为 base,应用的时候可能会有冲突)。后续,在 master 分支里 merge feature1 分支时,可以fast-forward,得到一个线性的提交历史。
rebase 冲突的时候会暂停,需要解决冲突后 git add,然后用 git rebase –continue 来继续 rebase,如果要终止 rebase则可以用 git rebase –abort 命令,这时分支会回到rebase 前的状态。
rebase 另外两个用途:
- 改写 commit 历史记录,如合并、删除多个 commit,修改 commit 的顺序、message 等。如 git rebase feature~5 feature,可以实现将 feature 分支的最近 5 次提交合并为一个。这可以使用 rebase 的 interactive 模式来轻松实现。
- 变基,如有三个分支 master、feature1、feature2,feature1 从 master checkout 出来,做了几次commit,然后feature2 从 feature1 checkout 出来,也做了几次提交。如果希望将 feature2 的修改合并到 master,但是 feature1不变的话,就需要变基了,即用命令 git rebase –onto master feature1 feature2;
rebase(r):
- i: interactively: 交互式 rebase,在当前分支 commit history 中选择一个 commit,然后交互式的 rebase 从该 commit 开始的后续 commit。用于修改对当前分支提交历史。
- s: a subset: 选择一个 target newbase,然后在当前分支选择一个 START commit,将 START 到 HEAD 的 commit 都 rebase 到 newbase 上。用于变基合并。
如果当前 commit 已经 push 到远程仓库,则后续执行 rebase 操作后,需要 force push到原仓库,否则会 push 失败。
-
rebase on 其它分支-全部
按 r e,然后选择将当前分支 rebase 到的其它分支,这会将当前分支的所有 commit rebase 到其它分支:
-
rebase on 其它分支-部分
使用 l a 命令,定位到要 rabase onto 的分支 commit,然后执行 r s(subset) 命令,选择要 rebase onto 的分支 commit 位置:
选择当前分支的 start commit,例如 deb7,然后按 e,这时从这个 commit 开始到 HEAD的 commit 都会rebase 到第一步的新 base 上:
出现了合并冲突:
解决冲突后,按 A r 继续 rebase:
结束后,可以看到当前分支已经 rebase 到了 master 分支上了:
-
rebase 修改历史
通过 rebase interactive 实现当前分支 commit 合并、删除、修改、msg 修改。
- pick = use commit
- reword = use commit, but edit the commit message
- edit = use commit, but stop for amending
- squash = use commit, but meld into previous commit
- fixup = like “squash”, but discard this commit’s log message
- exec = run command (the rest of the line) using shell
例如将下面红框中的 5 个 commit 合并为 2 两个:
首先将光标移动到 start commit,然后输入 r i(interactive):
修改历史 commit 的 rebase 方式(从旧到新),结束后 按 C-c C-c 开始:
rebase 过程中,对 pick 类型的 commit,都可以修改它的 commit message:
-
rebase: modify a commit
这时将 worktree 恢复到 7983d0b,可以修改文件和内容:
只有 stage 修改后的内容,才能继续 rebase。
如果按 e(edit),则出现当前分支到 HEAD 位置的 rebase 界面,可以调整后续 commit的 rebase 行为。
-
rebase:remove commit
删除一个 commit 时,会将该 commit 后面的 commit 合并到前一个 commit,这时可能会出现冲突(因为删除后面的 commit 还可能含有被删除 commit 涉及的变更):
提示合并冲突:
删除 commit 结束:
-
rebase: reword a commit
用于修改一个 commit 的 message,选择当前分支的某个 commit( rebase 操作的都是当前分支的 commit,其它分支的不行):
修改的 commit 即以后的 commit 都会以 rebase 的方式重新提交。
16.14 Refers #
y:show refers,查看本地或 remote 所有的 branch、tags 等信息。
在分支上,执行 k 命令可以用来删除 branch,使用 b checkout 新的分支,使用空格查看 commit diff。
16.15 案例 #
修改 commit 的作者信息( Name 和 Email):
- 切换到 commit 所在分支;
- 执行 r i 命令(interactive rebase);
- 光标移动到要修改的 commit 上,按回车;
- 按 m,表示 edit 该 commit,然后按 C-c C-c
- rebase 暂停,这时按 c -A, -A 表示重写 commit author,这时提示选择一个 author;
- 按 a (ammend), 然后按 C-c C-c;
- 按 r c,继续完成 Rebase。