Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 ?

like image 471
tigrou Avatar asked Oct 24 '14 08:10

tigrou


3 Answers

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.

like image 74
Christophe Muller Avatar answered Oct 17 '22 07:10

Christophe Muller


Here is what worked for me (windows solution), idea was grabbed from Christophe Muller answer :

copy /b "patch01.diff" + "patch02.diff" + ... + "patchXX.diff" "all.diff"

hg import "all.diff" --no-commit

It simply concatenate all the patches together (as one big file) then apply it.

like image 6
tigrou Avatar answered Oct 17 '22 09:10

tigrou


One possible solution is to use Mercurial Queues (note - it seems that this extension is "often considered for deprecation", but it's not deprecated just yet, and comes pre-installed with Mercurial).

If you've not used MQ before, it's very handy if a little complex. But it effectively allows you to create queues of patches, that can be pushed, popped, re-ordered, etc.

You could use it to help solve your issue like so:

:: if you don't already have MQ initialised for the repo, do so
hg init --mq

:: import the patches you want as queue entries
hg qimport -P patch1.diff
hg qimport -P patch2.diff
hg qimport -P patch3.diff

The -P option pushes the changes as you import them, which means you are effectively checking that they apply correctly. When you have all patches imported into MQ, pop them all (so none are applied and you're back to where you started), and fold them all into a new, combined patch:

:: go back to no patches applied, and create a new patch at the front of the queue
hg qpop --all
hg qnew -m "Your planned commit message" combined_patches
:: fold the named patches into the current one
hg qfold patch1 patch2 patch3

Then if you're happy with the resulting patch, just convert it into a "real" changeset:

:: convert combined_patches into a "real" changeset
hg qfinish combined_patches
like image 4
icabod Avatar answered Oct 17 '22 08:10

icabod