Mac 高效开发指南(二)

第 3.8 章 优秀 workflow 推荐

Workflow 中文的意思是工作流,它表示把多个事件联合在一起,形成一个连贯的开发体验。如果想使用 Workflow,必须购买 Alfred 的 PowerPack,价格在 19 - 35镑不等。

如果没有额外说明地址,下面的 Workflow 都可以在我的 GitHub 上面下载

Evernote

当下大多数资历中使用的还是 v8 版本的 Workflow,根据我的测试已经无法使用,推荐下载使用 v9 版本

我本人只把 Evernote 当做收藏集(也就是只收录文章,不自己编辑),所以只有搜索的需求。可以输入 ens 进行全文搜索,或者用 ent 来搜索标题。

在 ens 命令后面还可以加上 @ 来指定笔记本,或者加上 # 来指定标签。

命令 enn 用于新建一条笔记,同样支持 # 和 @ 语法。

AppLauncher

这是我独立开发的一个 Workflow,用来全局快速打开 App:

所有 App 快捷键都以 Command + Ctrl 开头,这样选择是因为不容易和系统快捷键产生冲突。如果你交换过 Ctrl 和 Caps Lock 键,就会发现触发起来非常方便、

Dash

如果电脑上装了 Dash,这个 Workflow 允许我们快速搜索文档:

IP

这个 Workflow 比较简单, 可以显示自己的内网 IP 和公网 IP:

有道词典

有道词典已经把 API 迁移到了有道智云,所以需要自行注册 API Token,关于 Workflow 的下载和使用,请参考这个 GItHub 仓库

使用效果如下:

GitHub

作为程序员,难免要和 GitHub 打交道,这个 Workflow 能帮助我们快速搜索、操作 GitHub:

Hash/BASE64 工具

提供了一些 md5、hash、sha1 计算工具和 base64 加密解密的功能:

layout

这是一个调整窗口布局的工具,可以设置窗口全屏或者摆放位置,甚至还可以切换屏幕。详情参考:alfred2-layout

color

这是一个颜色格式转换的工具,可以把英文单词转换成各种格式的颜色写法,也可以直接读取 16 进制的颜色:

快递查询

如果在淘宝买了个 MacBook/iPhone 之类的大件,等快递的过程真是心急如焚,恨不得过一会儿就查一次快递到哪了,此时用上快递查询这个 Workflow 了:

第 3.9 章 Chrome 插件介绍

插件管理技巧

有些插件是属于不需要交互,默默在后台运行的那种,比如屏蔽广告、显示 GitHub 仓库大小等等,这类插件可以设置为在 Chrome 中隐藏,这样 Chrome 就不会显得特别乱了。

对于那些需要手动点击,使用频率还很高的插件,我们还可以设置快捷键,打开 chrome://extensions/ 点击左上角的设置,选择 键盘快捷键:

比如我经常用印象笔记收藏文章,所以就可以给它配上快捷键:
应用这两种管理方式后,插件栏就会很简洁,高频交互类和非交互类插件都可以隐藏,只会留下少数低频(不值得设置快捷键)交互类插件。

Vimium++

一个致力于无鼠标操作 Chrome 的插件,因为按键规则类似于 Vim 风格而得名,主要功能有:

f/F: 在当前/新页面打开链接
x/X:关闭/恢复页面
j/k:向下/上滑动网页
J/K:左右切换标签页
H/L:浏览记录前进后退
u/d:向上/下滑动半页
b/B:在当前/新页面打开收藏夹内的页面,支持搜索
o/O:在当前/新页面打开新的网址,支持历史记录搜索
gs:查看网页源码
gi:鼠标定位到输入框
gu/U:前往网页的上一层或根路径
yy:复制当前网址
yf:复制某个链接的地址
p/P:在当前/新页面打开剪贴板中的网址

使用体验如下:

GitHub Repo Size

顾名思义,可以查看 GitHub 中某个 Repo 的大小,是一个小巧实用的工具:

FE 助手

这是一个工具集合,包含了字符串编解码、二维码生成器、网页转图片、网页取色、时间戳转换、BASE64 加解密、页面性能检测等工具。

这些功能都属于二级入口,如果有特别高频的使用场景,可以考虑使用单独的插件。下图展示了开启 JSON 格式化前后的区别(左侧开启 VS 右侧关闭):

Adblock plus

自动屏蔽广告

印象笔记·剪裁

一键收藏喜欢的文章

Vue

开发模式下可以用来调试 Vue 界面

Toby

可以跨设备同步 Chrome 的标签

第 4 章 编辑器之神:Vim

Vim 是纯键盘操作,因此在本书开头就描述过的误区同样适用于 Vim:

一些极端的人不够了解 Vim,感受不到 Vim 的价值,因此拒绝学习 Vim。

另一些极端的人过于沉迷于 Vim,想把 Vim 从编辑器打造成 IDE。因此在本章开头我就想强调的是:

Vim 就是个编辑器,用来输入、标记文本的,编程相关的东西交给 IDE 就好了

所以最理想的方案应该是:选择一款 IDE,给它装上 Vim 插件

但曾经的我,在 JetBrains 系列 IDE 和 Vim 之间犹豫不决,甚至还花了相当一段时间研究怎么用 Vim 开发 iOS,至于 Atom 和 Sublime 就根本没考虑过,一切的原因都在于:虽然很多 IDE 都有 Vim 模式,但都不支持 Vim 的插件

不要小瞧 Vim 插件,很多插件真的是** Life-Changing** 系列的,本章第五篇文章就包含了我对一些 Vim 插件的介绍。

最后我放弃了 JetBrains 家的 IDE 套装,主要理由有:

JetBrains 系列的 IDE 太庞大,繁重了。如果你要写不同语言,就要装不同的软件,而且每一个都很占用资源
VSCode 确实是地球上最好的 IDE,微软的技术真的很强。而且 VSCode 有着极活跃的社区,只需要安装这一个 App,配合上各种插件,就可以开发绝大多数语语言。

最终我的编辑器选型为:

前端/Node/其它脚本开发:VSCode + Vim 插件
iOS 开发:Xcode,没有选择 XVim 是因为经常遇到诡异问题,而且不支持 Vim 插件根本没法用
Android 开发:Android Studio + Vim 插件

客户端开发其实没什么选择的,因为暂时没有能完全替代官方 IDE 的东西。而 VSCode 功能就比较强大了,在我的日常工作中它基本上已经取代了原生的 Vim,最后一章会介绍一些 VSCode 中的用法和插件。

我使用了 neovim 来替代 vim,主要区别在于 neovim 更加先进,并且支持插件的异步加载。在使用上和 Vim 几乎是一致的

安装 NeoVim 需要以下几步:

brew install nvim
pip3 install --trusted-host pypi.python.org neovim jedi ipython
pip3 install --user --upgrade --trusted-host pypi.python.org PyYAML

# 下载配置
git clone https://github.com/bestswifter/vim-config.git ~/.config/nvim
ln -s ~/.config/nvim ~/.vim

用 nvim 命令打开文件后可以看到如下效果:

第 4.1 章 Vim 基本操作

Vim 分为三种模式:

输入模式:这就很普通的编辑器一样,光标在某个位置上闪烁,表示我们可以输入内容了。
普通模式:在输入模式下按下 Esc键可以退出输入模式,进入到普通模式。
命令模式:执行搜索、替换等命令或调用插件。

普通模式是 Vim 的一大特色,能够进行很多操作,比如:

快速移动光标
快速替换、删除某些内容
进入输入模式或命令模式

使用 nvim 打开文件后,默认进入普通模式,如果我们要编辑文本,可以按下 i进入编辑模式,编辑完后再按下 Esc键可以退出输入模式,进入到普通模式。

如果想退出 nvim,则需要借助命令模式。按下 : 并输入以下内容之一:

q:退出
q!:如果对文件作了修改,无法用 q 退出,此时 q! 表示丢弃修改并退出
x:保存并退出

因为每次按下冒号还挺麻烦,所以我对我的 Vim 做了配置,无需进入命令模式,直接在普通模式就可以退出:

q:退出
Ctrl-q:丢弃修改并退出
Ctrl-x:保存并退出

这种效果可以使用 Vim 的 map 配置来实现:

nnoremap <silent><buffer> q  :pclose!<CR>:quit<CR>
nnoremap <C-x> :x<CR>

// nnoremap 表示普通模式下的 map,等价于先输入冒号再输入 quit
// inoremap 表示输入模式下的 map,因此输入模式下也可以用 C-q 快捷键
nnoremap <silent> <C-q> :<C-u>:quit!<CR>
inoremap <silent> <C-q> <Esc>:<C-u>:quit!<CR>

第 4.2 章 Vim 光标移动

上一节中我们介绍了 Vim 的基本操作,靠着 i 、:x等指令,配合上方向键,基本上能在 Vim 的世界里幸存下来了。勉强编辑一下服务器上的文件不是问题。

如果你的打字手势标准的话,你会发现方向键其实挺难触摸到,至少会破坏右手的姿势。因此在 Vim 中不鼓励使用方向键,可以用 hjkl 来代替:

h:向左移动一个字符
l:向右移动一个字符
j:向下移动一行
k:向上移动一行

重复移动

如果要向上移动 5 行,或者向右移动 10 个字符,我们是不是要重复按下很多次呢?显然只有方向键才要这么做,Vim 中可以用数字来表示重复:

10j: 向下移动 10 行
5l:向右移动五个字符

这个操作虽然简单,但是引出了两个很有意思的话题。首先,Vim 中的行号应该配置为相对行号 + 绝对行号:

图中左侧高亮显示的 22 是当前的绝对行号,上下都是相对偏移量。这样的好处在于既方便知道当前的位置,又方便通过相对行号进行跳转。

另一个话题则是贯穿 Vim 始终的核心思想:就像英语、中文有语法一样,Vim 也有语法

Vim 的操作可以由若干个部分组合而成,每个部分又有多个可选值(比如光标移动就分上下左右)。Vim 的入门难度高,是因为每个部分都要学习,一下子需要掌握很多新知识点。但 Vim 后期的学习成本很低,因为无非就是各种组合而已。

还是以光标移动为例,5j 由重复次数和动作组成,后面我们会介绍更多的动作,而且它们的前面都可以加上数字表示重复。

单词移动

前面我们讲过利用 Option 键进行单词间移动,得益于普通模式和语法,Vim 支持更强大的单词间移动:

w移动光标到下一个单词的词首\(word\)
b 移动光标到上一个单词的词首\(begin\)
e移动光标到下一个单词的结尾\(end\)
ge移动光标到上一个单词的结尾\(不知道怎么解释了。。。\)

这里的单词是有严格约定的,而且不是那么显然,所以个人感觉这几个键并不是特别好用(往往你以为的跳转地址和真实跳转地址并不一致)。

不过如果你把空格当做单词的分隔符,倒是有比较简单的跳转方式,前三个命令都有大写格式:

W 移动光标到下一个单词的词首\(word\)
B 移动光标到上一个单词的词首\(begin\)
E 移动光标到下一个单词的结尾\(end\)

这里的单词特指以空格分割。

同样的,这些移动也支持重复任意多次

在后面的插件一节中,我会介绍如何用 EasyMotion 来让光标移动变得简单。

行和屏幕移动

在行内,^ 表示移动到行首,它会自动忽视掉行首的空格,大部分情况下这是符合预期的,尤其是在代码缩进的情况下。如果想回到行首的空格上,可以用 0 。$ 表示移动到行尾。

Vim 的翻页快捷键是:

Ctrl + f:向后翻一页(forward)
Ctrl + b:向前翻一页(backward)
Ctrl + d:向后翻半页(down)
Ctrl + u:向前翻半页(up)

很巧的是,前两个命令和系统输入框内的,向左向右移动一个字符的快捷键是一样的。后两个命令(u 和 d)则被 Vimium 借鉴,用于浏览器的翻页。

还有几个快捷键可以用于调整页面布局:

zz:让当前行居中显示,方便浏览上下文
zt:让当前行显示在最上方,方便浏览后面的内容(t 表示 top)
zb:让当前行显示在最下方,方便浏览前面的内容(b 表示 bottom)

高级移动

代码中经常遇到括号(包括 (、[ 和 {,也就是大中小三中括号),如果当前光标停留在括号上,按下 % 即可匹配到对应的括号上。

除此以外,*和#分别表示匹配当前光标下的单词,并移动到下一个单词和上一个单词

第 4.3 章 Vim 文本编辑

阅读完前两章,相信读者以及能够在 Vim 中自如的移动光标了,不过我们的编辑方式还比较简陋,只能进入编辑模式,像往常一样逐个字符的输入和删除,本节会介绍一些更高级的操作。

基础文本编辑

在普通模式下,按下 o会快速进入输入模式,并且在下一行进行编辑。如果按下大写的O会在上一行编辑:

如果想在当前光标位置开始编辑,可以按下 a 或 i,前者表示在光标所在字符的右侧开始编辑,后者表示在左侧开始编辑。

我们还可以用 I 前往行头开始编辑,或者用 A 跳转到行尾编辑。

按下 dd 可以快速删除一行,此时被删除的那一行会保存在 Vim 的剪贴板中。这一点类似于系统的 Ctrl + X 剪切,我们可以通过配置 Vim 来选择是否要与系统共享剪贴板。

普通模式下,按下 p 可以把 Vim 剪贴板中的内容再拷贝出来,注意这里总是会拷贝到下一行或者光标的右侧。因此如果想交换当前行和下一行,可以输入:ddp,这是因为当前被删除的行会复制到下一行的下面。

如果只想复制,不想删除,可以把命令 p 换成 y(表示:yank)。如果想删除后进入编辑模式,可以把 p 换成 c,比如 cc 表示删除当前行并且从行首开始编辑。

注意这里的 p、c、y、d 都是动作,和前面说的 j、k、l、h、w、b、e 一样,都支持用数字来重复,比如 2dd 表示删除两行。

撤销与重做

如果想撤销上一次修改,可以用 u,类似于系统的 Command + z 快捷键。

如果想重做,可以用 Ctrl + R,类似于系统的 Command + Shift + z 快捷键。

文本对象

Vim 的文本对象能极大幅度的提高编辑效率,也是 Vim 进阶之路上必须要掌握的概念。

对于上文介绍的 c、d、y 这些动作来说,作为单独的动作用处并不大,无非就是连续按两下对当前行做一些操作,而文本对象则扮演了语法中名词的概念,提供了更细粒度的操作。比如以下几种操作:

diw: 删除一个单词(word)
ca":删除双引号内的文本并编辑(包含双引号自身)
yi":复制双引号内的文本(不包含双引号自身)
dtx:向右删除,直到遇到遇到字符 x(不包含 x)

以上三个命令都属于同一大类操作,它们普遍遵循以下模式:

命令 = 动作 + 数量 + 描述词 + 文本对象

这类命令通常由三个到字母组成:

第一个字母是动词,包括上文介绍的 c、y、d等,表示你这个命令的主要目的。
第二个字母是可选的,表示重复几次
第三个字母是对范围的描述,比如 a 表示包含边界,i 不包含,t 表示向右查找,T 向左。
第四个字母是文本对象,可以是内容的字面量描述,比如 "、'、<、`、{、( 等等,也可以是固定单词

这里详细介绍下文本对象,除了字面量外,以下单词有特定的含义:

p:表示段落
s:表示句子
w:表示单词
t:特指标签,一定要具备 <xx>abc</xx> 的结构,此时的 t 代表 abc

对于字面量的文本对象,除了 "、' 和 这三个引号外,另外几个括号都可以用 b 来表示(block),这样就不用区分大中小和尖括号了。

搜索与替换

使用 / 进行搜索,我的搜索默认是忽略大小写的,使用 /pattern\C 可以强制匹配大小写。

使用 :范围s/oldpattern/newpattern/替换模式 可以进行替换。

如果我们已经进行过搜索,那么 oldpattern 可以省略不写,默认就是上一次的搜索内容。

最常用的范围是 begin,end,其中 begin 是开始行号,end 是结束行号,表示在第 begin 行到第 end 行之间进行替换,比如::1,10s/hello/hallo/g 表示把第 1 - 10 行中所有的 hello 换成 hallo。

范围还有一些简单的表示法:

.,10 表示当前行到第 10 行,点表示当前行
.,$ 表示当前行到文件末尾,美刀符号表示文件结尾
% 表示 1,$,也就是整个文件,比如全文替换通常写成 :%s/old/new/g
.,+2 表示当前行和接下来的两行

上文反复提到的字母 g 其实是一种替换模式,表示替换这个范围内所有出现的 oldpattern,如果不写则只替换第一次出现。常用的模式还有 c,表示替换需要确认(Confirm),i 表示大小写不敏感,I 表示大小写敏感。

如果需要替换光标停留的单词,并不需要搜索,或者手动输入它。直接使用 <Leader + s> 即可。接下来只需要输入替换后的内容和替换模式。这种替换是全局替换。

大小写切换

以下是几个常用的,切换大小写的命令

guu 当前行全部小写,gUU 当前行全部大写
~ 当前光标下的字符切换大小写, 3~ 当前光标下后面三个字符切换大小写
guiw 当前单词全部小写,gUiw 当前单词全部大写
g~iw 当前单词每个字符大小写交换

列操作

如果有多行文本要做相同的操作,可以借助 Vim 中列操作的概念来快速完成,举一个实际实际的例子,假设有以下这段代码:

int a;
int b;
int c;
// ...
int z;

有一天我们发现 int 不是很准确,要改成:

uint8_t a;
uint8_t b;
uint8_t c;
// ...
uint8_t z;

难道还要手动复制粘贴不成?请看列操作大法:

首先按下 Ctrl + V 进入列操作模式
然后用 j、k 进行跳转,表示你要操作多少行
这时你对每一行的操作都会应用到所有行上,比如输入 ec,删除单词进入编辑模式
输入 uint_8,按下 Esc 退出编辑,Vim 会自动应用到所有行上。

列操作的本质是多行同时操作,如果每一行的操作类似,但都有细微差别,就需要引入 录制、回放 的概念了。此时可以用 Vim 中的宏。

一个典型的场景是,我们在写 Markdown:

1. 第一条
2. 第二条
3. 第三题
...
10. 第十条

写着写着突然发现,第一条和第二条中间还要加一条,左边的序号就不好处理了,因为我们要把原来的序号 2 到 10 分别加一。

在 Vim 中,我们“只”需要输入 qajC-aq6@a,相信你一定很想打我,这特么一点也不简单啊。别急,拆开来看:

qa(q 表示开始录制宏,a 表示录制内容存储在寄存器 a 中)
j(向下一行)
Ctrl + a(数字加一)
q(再按一次 q 表示结束录制)
6@a(把存储在寄存器 a 中的宏执行 6 次)

是不是感觉 So Easy(录像中我用的是大写 Q,因为小写 q 被映射了):

第 4.4 章 Vim 神级插件

在本章的开头,我就提到目前的主力 IDE 是 VSCode,其实和 Atom/Sublime 这类 IDE 一样,它也是模拟了 Vim 的按键,并不支持 .vimrc配置文件。不过 VSCode 的强大之处在于,它支持了部分 Vim 的插件。

我想,不需要我做过多解释,读者也能明白这几个被支持的插件的重要性了,必然都是 Life-Changing 系列的。下面就逐个介绍一下:

Easymotion

从名字就能看出,这个插件的目的是快速光标跳转。它支持按行跳转,按字符/字符串跳转和按单词跳转等。跳转时还可以选择方向为向前或者向后, 不过这就增加了一倍的复杂度,作为一个懒人,我一般选择全屏跳转。

我配置的快捷键有:

;s:按字符串跳转(跳转到开头)
;w:按单词的开头跳转
;e:按单词的结尾跳转
;c:按字符串跳转
;l:按行首跳转(忽略空格)

完整的快捷键列表可以查看文档。

Surround

上一节中介绍过文本对象(Text Object)的概念,它主要用来描述括号和引号中间的文本,但考虑一下变换:

"hello" -> 'hello'

如果想把双引号换成单引号,就远远比改变 hello 这个单词难了。而 Surround 这个插件的目的,就是为了方便对引号和括号做修改。

它支持以下几种用法:

y s motion + <char>:给指定范围添加 surround 字符,比如:ysaw" 表示用双引号包裹单词。
d s <char>:删除周围的 surround,比如 ds" 表示删除两侧的双引号
c s <old-char> <new-char>:更换周围的 surround,比如 cs"'表示把周围的双引号换成单引号。

Comment

大多数 IDE 都支持 Command + / 给当前行添加注释,而且会根据语言自动选择注释方式。

在 Vim 中,给当前行添加/取消注释的方式是用 gcc,虽然多了一个按键,但按起来更容易了,而且支持多行注释。这里前两个字符 gc 表示按行注释,最后一个 c 表示当前行。如果想注释当前行和下面一行,可以输入 gcj。

美中不足的是,有些语言(如 HTML)中 VSCode 会用 /* ... */ 的写法进行多行注释,这样如果想取消注释,就只能去注释开始或结束的那一行把注释全部取消,而不能只单独取消中间某一行的注释。

输入 gC 可以注释某一块代码,后面接一个文本对象,比如 gCi( 表示注释括号内的东西(不包含括号)。不过个人认为注释小括号的需求几乎不存在,但下面这个场景还是有的,考虑这段代码:

if (1 > 0) {
    printf("Hello");
    printf("wrold");
}

有时候我们只想注释掉那两行 printf,写点别的逻辑,有时候会注释掉整个 if。问题是,怎么用文本对象来表示呢?下一个插件可以让我们用文本对象的方式来表示某一块缩进

Indent

其实很简单,一般就两个文本对象:

ii:表示某一块缩进,不包含上一层,比如上面例子中的两个 printf
aI:表示某一块缩进,包含上一层,比如上面例子中整个的 if 语句

其实还有一个不太常用的,ai 表示某一块缩进,和它的上面一行,也就是上面例子中的前三行,不包括第四行的大括号。这在 Python 中非常常用,因为它没有大括号,完全靠缩进来区分代码块。

因此,注释掉两行 printf 的语法是 gCii,注释掉整个 if 语句的方法是 gCaI。

Sneak

sneak 是和 EasyMotion 差不多的插件,主打快速跳转的功能。用法是 s<char><char>,即按下 s 键后输入两个字符,可以跳转到下一个,这俩字符连续出现的位置。

然而相比原生的插件,VSCode 似乎有一个很严重的问题:不支持搜索高亮,这就导致我根本无法肉眼看出在当前位置和我的目标位置之间,隔了多少个 <char><char>,导致无法用重复来跳转,也不支持标签跳转。更何况这个功能以及由 EasyMotion 实现了(快捷键 ;s),所以我就不再研究了。

第 4.5 章 VSCode 最佳实践

基本设置

在 VSCode 中,按下 Command + ,可以编辑配置,配置分为全局配置和项目配置,项目配置的优先级高于全局配置。
按下 Command + k + Command + s键可以管理快捷键。
按下 Command + p 可以在项目中根据名称搜索并打开文件。
按下 Command + Shift + p 可以打开命令面板,那些不记得快捷键的命令,都可以在这里找到。

掌握这些就可以初步使用 VSCode 了,下面会列举一些我的配置和插件,更多关于 VSCode 的配置介绍,请参考:vscode-tips-and-tricks

通用插件

Beautify:代码格式化插件,可以将 JS、CSS 和 HTML 代码格式化
Code Outline :支持多种语言的 outline 提取
Code Runner:可以运行多种语言,我配置的快捷键为 Command + R。注意新的文本文件因为没有后缀名,要用快捷键 Command + K + M 手动切换语言类型。
Dash:使用快捷键 Ctrl + h 快速打开 Dash 查找当前单词
MarkdownLint:Markdown 语法检查,帮助你写出标准的 Markdown
Project Manager:帮助你管理项目,在多个项目间切换,快捷键 Option + P(首先要添加进项目列表)
TODO Highlight:高亮显示 TODO
VSCode Icons:根据文件和文件夹名称展示恰当的图标
Bracket Pair Colorizer:给成对的括号配上颜色,方便区分
File Size:在底部 bar 中显示文件大小
Path Intellisense:自动补全文件路径

前端插件

Auto Close Tag:自动补全另一侧的 Tag
Auto Rename Tag:修改一个 Tag 时,另一侧的 Tag 自动修改
Color Highlight:遇到颜色字符串,自动在旁边显示颜色,方便预览
CSS Peek:
ESLint:代码格式化,支持实时监测和保存时自动修改
HTML Snippets:HTML 语法片段,方便快速输入
IntelliSense for CSS class:CSS 自动补全
JavaScript code snippets:JS 的语法片段,方便快速输入
Modern JavaScript Snippets:补充了一些更新的语法片段
HTML CSS Support:方便在 HTML 中插入 CSS 代码
SASS:sass 格式文件的代码补全和高亮
Vetur:Vue 开发必备的插件,提供代码补全、高亮等功能
Vue 2 Snippets:Vue 2 的代码片段
Vue peek:快速跳转定义

语法插件

一般用什么语言就装什么插件就行了。比如 Node、Bash、Python、Go、Ruby 等。

集成终端

VSCode 集成了一个终端,可以直接读取 .zshrc 中的配置(也就是说使用起来和 iTerm 几乎是一模一样的)。相关快捷键有:

cmd + \: 水平切割,生成一个新的终端界面
cmd + 数字:当光标停留在终端时,通过数字来选择要聚焦的窗口
cmd + w:当光标停留在终端时,关闭当前终端
cmd + t:当光标停留在终端时,新建一个终端
cmd + l: 在 VSCode 内嵌的终端和代码编辑器中互相切换,再也不用跳出到 iTerm 了
``ctrl + ```:隐藏/展示 终端

第 5 章 Git 从入门到进阶

对于很多人来说,Git 只是一种代码同步工具,他们要做的无非就是在 Source Tree 或 Tower 这样的应用上,先把代码同步下来,然后选中本地的修改,推送到远程仓库而已。

有了这么傻瓜式的 GUI 工具,为什么还要学习 Git 呢?在我看来,主要有以下原因:

GUI 工具底层还是通过 git 命令行来实现,学习多个 GUI 工具的使用,不见得比学习 git 来得快。
git 命令博大精深,GUI 工具只支持一些最基本的操作,很多强大的命令无法通过 GUI 工具来实现。
git 命令能让你从本质上去了解 git 的设计思想,这样的思想将来可能会在别的场景下复用。
GUI 工具的操作无法自动化,而 git 命令可以借助 Shell 脚本,部署在任何机器上,与任何上下游服务串联。

本章将由浅入深,首先介绍基本的 Git 配置,然后介绍 log、branch、tag、push、commit、diff、stash、reset、checkout、grep等操作。最后则会介绍一些进阶配置和 Git 的核心原理。

由于本书的目的是提高效率,因此不会过多介绍每个命令的基础用法,而是侧重于分享很多人可能不清楚的进阶用法,展示 git 命令行工具强大、高效的一面。

第 5.1 章 Git 配置

入门配置

配置文件原理

在 Git 中,我们一般只需要关注两个配置文件,gitconfig 和 .gitignore,前者用来对 git 的行为进行配置,后者则用来指定文件的忽略规则。如果命中了规则,就不会添加到 Git 的版本管理中。

对于某个 Git 仓库来说,一般同时有两个 gitconfig 文件生效,一个是 ~/.gitconfig,它是全局的配置,另一个则位于每个 git 仓库下的 .git/config。如果有重复的配置项,项目配置的优先级高于全局配置,否则两者是相辅相成的关系。

.gitignore 文件也是类似,分为全局和项目两个配置,区别在于在 git 仓库中的任意一个目录都可以有 .gitignore 文件,当然这个文件只对此目录内的文件生效

配置 GPG 秘钥

GPG 秘钥可以确保提交的安全性,和 HTTPS 有些类似的是,使用 GPG 秘钥的提交在 GitHub 上会显示成绿色的 Verified 字样。

为了实现这个效果,需要进行以下几步:

如果使用我的安装脚本可以跳过这一步,否则执行 brew install gpg 来安装依赖。
输入 gpg --full-generate-key 并按照配置安装,注意第三步选择时长时,如果是个人使用可以填 0,表示永不过期,避免以后的麻烦。最后填写密码时可以不填。
安装完成后输入 gpg --list-keys 查看刚刚生成的秘钥,在 pub 下面有一长串数字和字母,这个是 GPG-ID。
输入 gpg --armor --export pub GPG-ID 把公钥复制出来,拷贝到 [GitHub GPG Keys](https://github.com/settings/keys) 上。
输入 git config --global user.signingkey GPG-ID 来配置使用哪个 key。
单次提交时使用 git commit -s 参数来开启 GPG key,或者使用git config --global commit.gpgsign true 设置为全局默认使用。
输入 gvc <commit_hash> 来检查某次提交的 GPG 签名情况,如果没有输出,说明没有签名。它是git verify-commit命令的缩写。

配置个人信息

首先要配置个人的名字和邮箱:

gce ktzhang@bestswifter.com:配置邮箱,这是我的简写,完整的命令是:git config user.email
gcn bestswifter:配置用户名,完整的命令是 git config user.name

配置忽略规则

在 .gitignore 文件中我们可以配置需要忽略哪些文件和文件夹。注意,这个文件仅对还没有被纳入 git 版本管理的文件生效,一旦某个文件被暂存过,再配置 .gitignore 就无效了,此时我们需要先把所有的文件取消暂存,再重新暂存。

我配置了命令 reignore 专门用于解决这类问题,它的完整定义如下:

alias reignore='git rm -r --cached . && git add .'

如果某个文件不小心命中了忽略策略,就无法暂存到 git 中,而且 git 给出的提示并不友好。尤其是如果有多个 .gitignore 文件,很难知道哪一条规则导致了文件被忽略,此时可以使用 whyignore file_name 命令,它的完整定义时:

alias whyignore='git check-ignore -v'

进阶配置

scmpuff

scmpuff 是一个 git 拓展,支持数字快捷键的操作,如图所示:

尤其是当文件路径较长时,我们可以用数字来代替文件,同时也支持 1-3 这种写法,表示多个文件。

icdiff

git 默认的对比是上下对比,但个人认为不够直观。尤其是在大屏显示器上,浪费了大量空间。

icdiff 是一个左右水平显示的 diff 工具,在 git 中也可以使用:

function gdt() {
    params="$@"
    if brew ls --versions scmpuff > /dev/null; then
        params=`scmpuff expand "$@" 2>/dev/null`
    fi

    if [ $# -eq 0 ]; then
        git difftool --no-prompt --extcmd "icdiff --line-numbers --no-bold" | less
    elif [ ${#params} -eq 0 ]; then
        git difftool --no-prompt --extcmd "icdiff --line-numbers --no-bold" "$@" | less
    else
        git difftool --no-prompt --extcmd "icdiff --line-numbers --no-bold" "$params" | less
    fi
}

然后用 gdt 来代替 git diff 即可,效果如图所示:

尤其是当两行只有少数变化时,默认的 git diff 会展示太多无用的干扰信息,而 icdiff 的输出就非常简洁直观了。

非文本文件 Diff

默认情况下,对于图片或者二进制文件,git 不会显示它们的具体变化。但如果我们自己配置了 diff 工具,就可以显示特定类型文件的变动。如果使用我的配置,默认自带了图片和 plist 文件的 diff 的功能。

也可以手动开启,首先编辑 ~/.gitconfig 文件:

[core]
    attributesfile = ~/.gitattributes
[diff "exif"]
    textconv = exiftool
[diff "plist"]
    textconv = plutil -convert xml1 -o -

然后编辑 ~/gitattributes 文件:

*.png diff=exif
*.jpg diff=exif
*.plist diff=plist

最后再安装一下依赖:

brew install exiftool

最终 plist 文件的 diff 效果如图所示:

第 5.2 章 查看提交记录

Git 做为版本管理工具,最重要的特色之一就是强大的记录查询,可以花式查看各种提交历史和变动,同时也支持过滤和筛选。本章主要介绍 log、diff 等工具的使用

Log

通过 git log 命令可以查看过去的提交记录。这个命令非常常用,但也非常复杂,常见的有以下几种:

基础命令

在我的配置中,输入 gg,它会单行展示提交历史,也支持展示分支的关系,如图所示。

它的完整命令是:

alias gg='git lg'

lg 命令可以通过上一节介绍的 gitconfig 来配置:

[alias]
    lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%ci) %C(bold blue) <%an>%Creset' --abbrev-commit

常用拓展

如果只想简单的查看提交历史,使用 gg 命令就足够了。但如果想查看更细的内容,gg 就不太够用了。我们可以输入 ggs 命令,它在 gg 的基础上会展示每次提交具体改动的文件,等价于命令 gg --stat,如图所示:

如果想查看某次提交具体的变动,可以使用 ggp 命令。它在 gg 的基础上会展示文件的具体改动,等价于命令 gg -p,如图所示

进阶使用

输入 gga,可以展示所有的分支,一般建议用它代替 gg,它等价于命令 gg --all
不管是gg,还是衍生出来的 ggs 和 ggp,后面都可以加上 -n,比如 ggs -2,或者 ggp -1,表示只显示前 n 个提交
如果想看某一个区间内的 log,可以输入 gg HEAD~3..HEAD,注意区间是左开右闭,所以这个命令会查看三次提交的记录。注意较早的提交要写在左边。
如果只记得某次提交内容\(content\)里有一个词,比如 networksetup,可以用 ggp -G networksetup 来查找所有提交内容中包含 networksetup 的提交。-G 选项和 -S 选项主要有两个区别, 前者可以接收正则,而后者只会查找那些导致 string 出现次数发现变动的提交,比如把 name:0 换成 name:1,用 -S 就无法搜索到 name, 因为它的出现次数并没有改变,但是用 -G 可以。
如果只记得某次提交记录\(message\)里有一个词,比如 highlight,可以用 ggp --grep highlight 来查找所有提交记录中包含 highlight 的提交
如果想查看某个文件中某一段代码的提交记录,可以用 ggp -L start,end:file_name 命令,详情请参考 Git grep 这一节的用法。
如果想查看某个文件或者文件夹的改动历史,可以输入 gg file_name,当然 ggs 和 ggp 也都是支持的。

Diff

输入 gd 即可查看工作区内的变动,等价于命令 git diff
输入 gds 可以查看暂存区的变动,也就是查看那些被 git add 了的文件的变动,等价于命令 git diff --staged
输入 gdc 可以查看最近一次提交的变动,等价于命令 git diff HEAD^ HEAD
输入 gdcr 可以倒过来查看某次提交的变动,它的第一个参数是提交的 SHA-1 值,如果不写则是 HEAD,所以 gdcr 和 gdc 是恰好相反的 diff。这样的好处是如果 想撤销某次提交,只要用 gdcr sha-1 就可以获得那次提交的逆提交,如果想要精确到只恢复某个文件,第二个参数可以是文件名。比如 gdcr sha-1 file_name | git apply
输入 gdt 即可用外部 diff 工具查看 diff,它是 git difftool --no-prompt --extcmd "icdiff --line-numbers --no-bold" "$@" | less 命令的缩写,这个命令依赖 icdiff 这个工具,可以用 homebrew 安装。
输入 gdts 和 gdtc 可以对应的用外部 diff 查看已暂存和上次提交的变动,这些和 gds 与 gdc 命令基本上是一样的,区别在于使用外部 diff 工具,会更美观一些(当然速度也会更慢),效果如图所示

Grep

这个命令和 grep 的区别在于运行更快,而且可以指定搜索范围(比如是否搜索未跟踪文件,搜索某个特定的 tag等),如果当前目录是 git 目录,可以用 ggrep 来替代 grep

ggrep 是 git grep --break --heading -n 命令的缩写,第一个参数表示不同文件的搜索结果间用空格分割,便于阅读。第二个参数非常有用,它不再在每一行输出前面加上文件名,而是在所有属于同一个文件的匹配之前加上一次文件名, 这样输出结果的可读性更高,-n 表示输出行号。

以查找 gignore 这个命令的历史为例,先输入 ggrep gignore,得到如图所示的结果,这告诉我们它定义在 zsh-config/git.sh 这个文件的第 25 行

然后输入 ggp -L 25,25:./zsh-config/git.sh,参数 —L 表示行内查找,即查找这个文件的第 25-25 行的提交记录,得到的结果如图所示:

提交的 SHA-1 值、日期、提交者等信息就完全显示出来了

提交区间

git 的提交区间指的是用两个点 A..B 或者三个点 A...B 的语法将 AB 两个提交连接起来,从而表示一系列提交。

这是非常容易让人产生疑惑的一个话题,主要是因为它没有固定的含义,需要根据不同的命令来记忆。本文主要结合 diff、log 这两个命令来介绍。

首先,假设我们基于初始提交建立了 a 和 b 两个分支,各有一次提交,如图所示。分支 a 的提交是数字,分支 b 的提交是字母。

diff

此时,双点语法 A..B 表示对比这 AB 两个提交的差异:

git diff branch_a..branch_b

# 直接对比两个 commit
# 等价于直接写空格

git diff branch_a branch_b

这个结果还是非常符合常识的,就不多解释了。

三点语法 A...B 则表示:AB 两次提交的共同祖先和 B 之间的 diff

git diff branch_a...branch_b

两个分支的共同祖先是第一次提交,因此上述命令其实是展示分支 b 和 b 的父提交之间的 diff。如图所示,

需要注意的是,如果两次提交在同一个分支上,那么双点和三点语法的效果是一致的。

log

log 的双点语法 A..B 和 diff 的三点写法具有相同的含义,都表示仅在 B,但不在 A 的那些提交。如图所示:

比如:
ggp branch_b..branch_a

这个命令只会输出分支 a 的日志,如图所示:

三点语法 A...B表示的是那些只属于 A 提交,和只属于 B 提交的那些提交,如图所示:

例如:
ggp branch_b...branch_a

这个命令将单独得到分支 b 和 分支 a 上的各一次提交:

另外需要注意的是,根据我的实践,git show 中的双点、三点表示法和 git log 是一致的,就不另外说明了。
最后用一张图来总结: