Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I squash commits in Mercurial?

Tags:

mercurial

People also ask

Can I squash commits?

You can manually squash your commits at any time using Git's "Interactive Rebase" feature.

How do you squash between commits?

Git squash with a commit id The last command opens the interactive Git rebase tool which lists all of the commits in the branch. You must type the word pick next to the commit you want all others to be squashed into. Then type 'squash', or just the letter 's', next to each commit to squash.

Can you squash commits without rebase?

Solution: It is possible to squash multiple commits into single one without rebasing.

Can you squash commits on GitHub UI?

Squashing a commitIn GitHub Desktop, click Current Branch. In the list of branches, select the branch that has the commits that you want to squash. Click History. Select the commits to squash and drop them on the commit you want to combine them with.


Yes, you can do this using mercurial without any extensions by Concatenating Changesets.

Alternately if you want to use an extension you could use:

  • The Collapse Extension
  • The Rebase Extension or
  • The Histedit Extension

My favourite is hg strip <commit_hash> --keep command. And then I commit all changes in one commit.

It is the fastest and most comfortable way for me, because I like to do many small commits during my daily work ;)


Note 1: strip needs a built-in extension mq to be enabled.
Note 2: My favourite Git/ Mercurial client (SmartGit/Hg) appends by default --keep parameter during strip. And what is even more convenient: it provides option called join commits :]


The Rebase extension worked like a charm. To squash 2 commits:

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

Dot is a shortcut for current revision.

It's even easier when you have a few commits on a branch and want to collapse them all into one:

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

How this works:

enter image description here

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


If you are reading this answer, you can forget every other option mentioned in this answer and use the fold command from the evolve extension.

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 repo and adding it in your .hgrc like this.

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

Assuming that you cloned evolve repo in your home directory. Now you are good to go. You can also look for help by hg help fold.

Fold Command

You tell fold to squash/fold a linear chain of commits which is not broken. What fold does is, it creates a new changeset which contains changes from all the changesets and mark all those commits as obsolete. You can have a more deep view into this at docs.

Now suppose you have the following history.

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

You want to squash e, f and g. You can do

hg up g
hg fold -r e

The result will be

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

where h is the changeset which contains changes from all the three commits e, f and g.

You can also fold changesets from the middle of the history, i.e. not necessarily you have to pick a chain which includes the tip. Suppose you want to fold b, c and d. You can do

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

This will result in

a -> i -> j

where i is the folded changeset of b, c, d and j is the same changeset as h. Evolve user guide is a must read.


With Mercurial 4.8 (Nov. 2018, 9 years later), you could consider the new command hg absorb (it was an experimental feature before).

See "Absorbing Commit Changes in Mercurial 4.8"

The absorb extension will take each change in your working directory, figure out which commits in your series modified that line, and automatically amend the change to that commit.
If there is any ambiguity (i.e multiple commits modified the same line), then absorb will simply ignore that change and leave it in your working directory to be resolved manually.

At a technical level, hg absorb finds all uncommitted changes and attempts to map each changed line to an unambiguous prior commit.
For every change that can be mapped cleanly, the uncommitted changes are absorbed into the appropriate prior commit. Commits impacted by the operation are rebased automatically.
If a change cannot be mapped to an unambiguous prior commit, it is left uncommitted and users can fall back to an existing workflow (e.g. using hg histedit).

The automatic rewriting logic of hg absorb is implemented by following the history of lines: This is fundamentally different from the approach taken by hg histedit or git rebase, which tend to rely on merge strategies based on the 3-way merge to derive a new version of a file given multiple input versions.

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.