Can I squash commits in Mercurial?

我有一对提交,实际上应该只有一个。如果我使用 git,我会使用:

git rebase -i <some-commit-before>

然后压扁他们。

Can I do that in mercurial? If so, how?

67130 次浏览

是的,您可以使用 mercurial 完成此操作,而不需要 连接变更集的任何扩展。

或者,如果你想使用一个扩展,你可以使用:

我最喜欢的是 hg strip <commit_hash> --keep命令,然后在一次提交中提交所有更改。

对我来说,这是最快也是最舒服的方式,因为我喜欢在日常工作中做很多小的承诺;)


注1: strip需要一个内置扩展 mq才能启用。
注2: 我最喜欢的 Git/Mercurial 客户端(SmartGit/Hg)在 strip期间默认附加 --keep参数。更方便的是: 它提供了名为 join commits: ]的选项

重定基扩展运作得非常出色:

$ hg rebase --dest .~2 --base . --collapse

Dot is a shortcut for current revision.

如果您在一个分支上提交了一些内容,并希望将它们全部压缩为一个内容,那么这种方法甚至更简单:

$ hg rebase --dest {destination branch (e.g. master)} --base . --collapse

工作原理:

enter image description here

(来自 http://mercurial-scm.org/wiki/RebaseExtension#Collapsing)

如果你正在阅读这个答案,你可以忘记其他所有选项 在此答案中提到,并使用来自 < a href = “ https://www.mercurial-scm.org/doc/Evolution/”rel = “ noReferrer”> Evolution 的 fold命令 扩展部分 .

evolve is an extension of mercurial which helps us in having safe mutable history, it's still experimental though. You can use it by cloning it from its 回购 and adding it in your .hgrc like this.

[extensions]
evolve = ~/evolve/hgext/evolve.py

假设您在主目录中克隆了进化 repo。现在你可以走了。你也可以通过 hg help fold寻求帮助。

折叠指令

你告诉 fold压缩/折叠一个线性的提交链,它不会被破坏。折叠所做的是,它创建一个新的变更集,其中包含来自所有变更集的更改,并将所有这些提交标记为过时。您可以在 docs上对此有更深入的了解。

现在假设您有以下历史。

a -> b -> c -> d -> e -> f -> g

你想要压扁 efg。你可以这样做

hg up g
hg fold -r e

结果就是

a -> b -> c -> d -> h

其中 h是变更集,其中包含来自所有三个提交的 efg的更改。

您还可以从历史记录的中间折叠变更集,也就是说,您不一定要选择包含提示的链。假设您想折叠 bcd。你可以的

hg up d
hg fold -r b
hg evolve --all

这将导致

a -> i -> j

其中 ibcdj的折叠变更集,与 h的折叠变更集相同。 进化用户指南 是必读的。

我认为 chistedit(从 Mercurial 2.3开始内置)是最接近纯 Mercurial 的 rebase -i(chistedithistedit的交互版本)。在 histedit 中,fold命令映射到 rebase 的 squashroll命令映射到 rebase 的 fixup。有关更多信息,请参见 他说文档。

这里有一个简单的例子。假设您具有以下内容,并且希望将所有1e21c4b1的更改移动到前一个修订版中,并且只保留前一个修订版的消息。

@  1e21c4b1 drees tip
|  A commit you want to squash
o  b4a738a4 drees
|  A commit
o  788aa028 drees
|  Older stuff

可以运行 hg chistedit -r b4a738a4将历史记录编辑回 b4a738a4。然后在 chistedit 中将光标下移到1e21c4b1并按下 r以表示要滚动该修订。请注意,histedit 中的顺序(从最老到最新)与 hg log中的顺序(从最新到最老)是相反的。

#0  pick   160:b4a738a49916   A commit
#1  ^roll  161:1e21c4b1500c

选择修改之后,再选择 c提交修改,结果如下:

@ bfa4a3 be drees tip 承诺 788aa028件衣服 旧东西

If you are relatively new to them, then histedit can be a better choice than chistedit because it provides the command descriptions in histedit file for reference. It just takes a bit more editing to set the commands using normal text editing (just like normal rebase).

Note, to use either histedit or chistedit you need to add histedit to your extensions in your ~/.hgrc:

[extensions]
histedit =

我建议使用 chistedit,因为它最接近 rebase -i,并且在历史上任何地方都可以使用。如果您真的只是想将当前的修订包含/调整为上一个修订,那么@G。Demecki 的 strip建议可能是好的,因为正在发生的事情是清楚的。它是从 Mercuria 2.8开始建造的。要获得与上述相同的结果,可以执行以下操作:

hg strip .
hg add
hg commit --amend

注意,与 histedit 一样,strip需要在 ~/. hgrc 中启用:

[extensions]
strip =

我用:

hg phase --draft --force -r 267
...
hg rebase --dest 282 --source 267 --collapse

让我们假设您想压缩(联合)2最近的提交。

  1. 找一个修订号码

    hg log -G -l 3
    

    possible output:

    @  changeset:   156:a922d923cf6f
    |  branch:      default
    |  tag:         tip
    |  user:        naXa!
    |  date:        Thu Dec 13 15:45:58 2018 +0300
    |  summary:     commit message 3
    |
    o  changeset:   155:5feb73422486
    |  branch:      default
    |  user:        naXa!
    |  date:        Thu Dec 13 15:22:15 2018 +0300
    |  summary:     commit message 2
    |
    o  changeset:   154:2e490482bd75
    |  branch:      default
    ~  user:        naXa!
    date:        Thu Dec 13 03:28:27 2018 +0300
    summary:     commit message 1
    
  2. Soft reset branch

    hg strip --keep -r 155
    
  3. Commit changes again

    hg commit -m "new commit message"
    

Notes

strip requires a built-in extension to be enabled. Create/edit ~/.hgrc config file with the following content:

[extensions]
strip =

使用 Mercurial 4.8(2018年11月,9年后) ,您可以考虑使用新的命令 hg absorb(它是一个 以前的实验特征)。

看“ 吸收 Mercurial 4.8中的提交更改

吸收扩展会接受你工作目录中的每一个变化,找出你系列中的哪一个提交修改了那一行,然后自动修改那个提交。
如果存在任何不明确的地方(比如多次提交修改了同一行) ,那么沿用过去的方法就是忽略这些修改,然后把它们留在你的工作目录中手动解决。

在技术层面上,hg absorb发现所有未提交的更改,并尝试将每个已更改的行映射到明确的先前提交。
对于每个可以清晰映射的更改,未提交的更改将被吸收到适当的先前提交中。受操作影响的提交将自动重新定位。
如果一个更改不能映射到一个明确的先前提交,那么它就是未提交的,用户可以回到现有的工作流(例如使用 hg histedit)。

hg absorb的自动重写逻辑是通过遵循行的历史来实现的: 这与 hg histeditgit rebase采取的方法有根本的不同,后者倾向于依赖基于 三人合并的合并策略来获得给定多个输入版本的文件的新版本。

This approach combined with the fact that hg absorb skips over changes with an ambiguous application commit means that hg absorb will never encounter merge conflicts!

Now, you may be thinking if you ignore lines with ambiguous application targets, the patch would always apply cleanly using a classical 3-way merge. This statement logically sounds correct. But it isn't: hg absorb can avoid merge conflicts when the merging performed by hg histedit or git rebase -i would fail.