提交
创建提交
对于熟悉 git 的用户
hj commit <message>近似于:
git add . # 先将未追踪的文件添加到工作区
git add -p
git commit -m message对于熟悉 jj 的用户
hj commit <message>的实质是:
jj commit --interactive --message message这本质上是拆分一个提交,使用 jj split 也可以实现。
在初始化仓库后,你就可以开始工作了!你在此仓库里做的 所有变更操作 都会被自动跟踪(被忽略的文件除外)。
每当你实现了一部分工作,例如新增了一个功能、修复了一个 bug 等,就可以对这部分修改创建一个提交(commit):
hj commit "feat: add new feature"在 commit 子命令后加一个可选参数,填写此次提交的描述信息。
命令会弹出一个终端交互式界面,可以在界面中选择要提交的文件。按下 f 键可以展开单个文件,选择具体的变更行。选择完成后,按下 c 键提交。
如果你在 hj commit 命令中没有给出提交描述信息,命令会认为你想先选择文件、后提供描述。选择完成后,会进入 终端编辑器 中编辑提交描述信息。这种方式更适合多行的提交信息,
TIP
为了极致的效率,大多数 hj 命令都有短缩写。 例如 commit 可以简写为 cm,status 可以简写为 st。这是我们最常用的两个缩写。
重命名提交
对于熟悉 git 的用户
要在 Git 中重命名一个历史提交,可以通过交互式变基 reword 命令实现。
对于熟悉 jj 的用户
hj describe 和 jj describe 完全等价。
hj describe 命令可以用来重命名一个提交:
hj describe REVSET -m "new message"REVSET 处填写一个具体的提交,如 @- 表示最近的一次提交;-m 参数填写新的提交描述信息。
为提交增补变更
对于熟悉 git 的用户
hj amend近似于:
git add -p
git commit --amend而 hj amend <revset> 需要复杂的交互式变基才能实现。
git 没有不可变提交的机制,所以 hj amend --force 在 git 中没有对应项;但是 git 在推送时如果会覆写远程仓库提交,需要 git push --force,这在 jj 中是不需要的。
对于熟悉 jj 的用户
hj amend的实质是:
jj squash --interactive --from @ --into @-hj amend REVSET的实质是:
jj squash --interactive --from @ --into REVSET--force 这个标志对应 jj squash的 --ignore-immutable 标志。
事实上 squash 命令已经足够简单,但 amend 语义更加明确、功能更加单一,所以我们提供了 amend 命令。
如果你在提交后,发现漏掉了一些内容、或者有些已提交的代码有误,可以继续编辑文件之后后进行 提交的增补(amend)。增补的部分会合入 最近一个提交 中:
hj amend同样会弹出一个交互式界面,你可以选择要提交的文件和变更行。
你也可以指定将工作副本中的一部分变更增补到 任意提交 中:
hj amend REVSET已推送(push)的提交很可能是不可变的,增补它可能是 不安全 的。此时可以加上 --force 参数来强制增补。
或者如果你想暂时地离开当前工作的内容,临时地为某个提交增补内容,可以:
hj new xxx # 创建基于将被修改提交的新提交
# 使用编辑器做出变更
hj squash # 方法一:将整个提交压入父提交中
hj amend # 方法二:交互式地选择变更压入父提交中从提交删减变更
对于熟悉 git 的用户
hj reset近似于:
git reset -p HEAD^ # 针对改动块
git reset HEAD^ -- file1 file2 file3 # 针对文件而 hj reset REVSET 需要复杂的交互式变基才能实现。
对于熟悉 jj 的用户
hj reset的实质是:
jj squash --interactive --from @- --into @hj reset REVSET的实质是:
jj squash --interactive --from REVSET --into @撤回一个提交和增补一个提交一样,本质上都是将两个提交合并成一个。
事实上 squash 命令已经足够简单,但 reset 语义更加明确、功能更加单一,所以我们提供了 reset 命令。
reset 操作是 amend 操作的逆操作,可以将一个提交的部分或全部内容撤回到工作副本中(如果没有指定,这个提交默认是最近一次提交):
hj reset REVSET特别地,如果你撤回了一个提交的全部内容,这个提交将会被删除(abandoned)。
和增补一样,可以加上 --force 参数来强制删减。
在提交间移动变更
对于熟悉 git 的用户
对于跨分支的操作,可以使用 git diff + git apply:
git diff C~ C -- path/to/file > patch.diff
git checkout B
git apply patch.diff
git commit -am "手动移动变更"这只是其中一种实现方式。git 中不存在能完成这个操作的单行命令。
对于熟悉 jj 的用户
hj squash 和 jj squash 完全等价。
我们可以总结一下前面提到的两个操作:
hj amend:将 工作副本 中的变更增补到某次提交(默认为最近一次提交)中。hj reset:将某次提交(默认为最近一次提交)中的部分或全部内容撤回到 工作副本 中。
如果我们不局限于在工作副本和提交中间移动变更,而是 在两个任意提交中移动变更,则可以:
hj squash -i --from A --into B这会将提交 A 中的部分变更移动到提交 B 中。其中 -i 参数表示交互式选择变更,如果不提供则表示将提交 A 整体合并入提交 B 中。
更多用法参见 jj squash。
从提交中移除变更
上文中我们提到了 hj reset 可以把一个提交中的变更(默认为最新提交)撤回到工作副本。还有一个命令:
hj throw @-可以交互式地将一个提交中的变更删除。它不会被移动到某个提交中,而是被丢弃掉。如果不指定提交,默认是工作副本提交。
拆分提交
对于熟悉 git 的用户
你可以用交互式 rebase:
git rebase -i HEAD~3- 找到要拆分的提交(例如 pick abc123)
- 将它改为 edit
- 保存并退出编辑器
git reset HEAD^→git add -p→ 多次提交
这只是其中一种实现方式。git 中不存在能完成这个操作的单行命令。
对于熟悉 jj 的用户
hj split 和 jj split 完全等价。
很多时候你会发现自己的提交粒度太大,应该拆分成多个。你可以使用:
hj split -r A -d B这将将提交 A 中的部分变更拆分出来,作为一个新提交放到已有提交 B 之上。
更多用法参见 jj split。
复制提交
对于熟悉 git 的用户
hj duplicate 近似于 git cherry-pick,但是可以将新提交放到任意位置。
对于熟悉 jj 的用户
hj duplicate 和 jj duplicate 完全等价。
duplicate 命令可以将一个提交复制一份,放到另一个提交之上:
hj duplicate REVSET -d B这将把提交 REVSET 复制一份,放到提交 B 之上。
更多用法参见 jj duplicate。
移动提交的位置
对于熟悉 git 的用户
如果你想改变提交的顺序,在同一个分支中可以使用交互式变基 git rebase -i;跨分支的操作需要 git cherry-pick 等操作。
git 中不存在能完成这个操作的单行命令。
对于熟悉 jj 的用户
hj rebase 和 jj rebase 完全等价。
一个非常好用的工具是 rebase,它可以将一个提交移动到另一个提交之上,不论这两个提交是否位于同一分支:
hj rebase -r A -d B这将把提交 A 移动到提交 B 之上。
更多用法参见 jj rebase。
删除提交
对于熟悉 git 的用户
hj abandon B 近似于:
git rebase --onto B^ B特别地,如果 B 是合并提交,B^( B 的父提交)可能存在多个。这种情况下你需要显式地指定,否则命令会失败。
对于熟悉 jj 的用户
hj abandon 和 jj abandon 完全等价。
abandon 命令可以用来删除一个提交:
hj abandon REVSET这将移除这个提交,并将其子提交变基到其父提交上。
特别地,如果不指定 REVSET,将默认为工作副本(@)。此时会清空工作副本的所有变更,并自动地创建一个新工作副本提交。