How to apply several patches using hg command line when there is already uncommitted changes?

tigrou picture tigrou · Oct 24, 2014 · Viewed 27.1k times · Source

I use the following command to apply a patch in Mercurial, without committing it :

hg import patch.diff --no-commit

It works great, however if I try to apply several patches at once like this :

hg import patch1.diff --no-commit
hg import patch2.diff --no-commit
...

I get this error message after second commit :

abort: uncommitted changes

If I do exactly the same in SourceTree (to apply the patch1 then patch2 and choose "Modify working copy files") it works : the two patches are applied on the working copy, changes from patch1 and patch2 combined/folded together.

How to do the same using hg command line ?

Answer

Christophe Muller picture Christophe Muller · Oct 24, 2014

This is the behaviour that was designed by the Mercurial authors: imagine that you have "manually made" uncommitted changes in a working copy, we would not want hg import to automatically apply the patch, and commit both your changes and the patch changes with a wrong log message and both changes entangled together.

This is why the hg help import says:

Because import first applies changes to the working directory, import will
abort if there are outstanding changes.

The import command is more for importing changesets (with metedata when the data comes from hg export) than just applying patches. If you have your own changes in the working copy you can for example still use hg import --bypass and no error will happen there because the commit is directly applied to the repository and not to the working copy. (Note: but then if you just commit your changes, you will get two heads, that you will need to merge.. :-).

A solution with the command line for Unix-systems is to use the patch command directly instead of hg import because then no check of local modifications will be performed. E.g.,

for i in 1 2 etc.
do 
   patch -p1 < patch$i.diff
done
hg commit ...

For non-Unix-systems you can also have a pure Mercurial solution by installing the shelve extension, enabling it in your global configuration file (mercurial.ini), and then use shelve to handle the merges one patch after the other:

hg import --no-commit patch1.diff
hg shelve
hg import --no-commit patch2.diff
hg unshelve
etc.

If ever a conflict would occur, shelve will detect it and you would have to solve it then tell shelve that it is solved with the --continue option.

Hope it'll help.