12

Scenario: I've "inherited" a program, kept under Mercurial, that only works on my system with specific tweaks to certain files that are checked in. I do not want to check these tweaks in.

My most recent solution to this is to create a mercurial patch file (hg diff > patchfile) containing these tweaks; when I need to check in my changes, I'll just reverse-apply the patch, commit, and re-apply the patch. (If I had full control of the source, I'd just move all these little tweaks to a single configuration file that isn't under version control, putting a "sample" config file under version control)

Unfortunately, it seems that while the GNU patch command supports the --reverse flag, it does not support hg's multi-file diff format as a single patch file (or maybe it does, and I just don't know the switches for it?). OTOH, hg has its own patch command that can apply the diff, but that doesn't support any kind of reverse flag.

So my question is twofold:

  1. How should this be done in mercurial? Surely hanging on to a "tweak patch" isn't the only way to handle this situation. Maybe mercurial has a plugin or something built in for such temporary, uncommittable changes.
  2. Aside from how things should be done, is there any way to reverse-apply such a mercurial diff-patch to a mercurial repo like this? There are other situations where such functionality would be useful.
eternicode
  • 6,805
  • 4
  • 33
  • 39

2 Answers2

23

Mercurial's patch command (really import) doesn't support reverse, but hg diff does. Use --reverse on that and you'll have a reversed patch that Mercurial can apply.

However, what you're describing is a very common vendor-branch style workflow, which mercurial can better support using features other than diff and patch.

Specfically, Mercurial Queues does exactly what you want.

Dave Weston
  • 6,527
  • 1
  • 29
  • 44
Ry4an Brase
  • 78,112
  • 7
  • 148
  • 169
  • This looks close to what I want (I'll have to admit I never took the effort to learn MQ until now). However, now that I've added my patch to the mq and made changes I want to commit, `hg ci` complains that I "can't commit over an applied mq patch". I can't `qpop` the patch before committing, either, as there are local changes in the repo. This seems to be an impass that decisively prevents MQ from solving the problem, unless I'm missing something. – eternicode Nov 26 '10 at 22:58
  • @eternicode: You can't `hg commit` a patch, but you can `hg qfinish` it. – robert Nov 27 '10 at 00:38
  • @robert Read my question again. I *don't* want to commit the patch, ever. I *do* want to commit changes I've made after the patch is in the queue, but without the changes introduced by that patch. It seems MQ isn't the right tool for this, for just that reason. – eternicode Nov 27 '10 at 01:15
  • @eternicode: So make a new patch on top of the initial patch and then qfinish it. – robert Nov 27 '10 at 01:31
  • 1
    @robert the problem I see with that is that I wouldn't be able to finish the second patch without first finishing the uncommittable patch. I found out, though, that you can swap these around in the `.hg/patches/series` file, and that seems to do the job well (though the whole thing still feels very manual). Naturally, you'd want to be careful about doing this if patch 2's changes depended on patch 1's changes, but in my case they don't. – eternicode Nov 27 '10 at 02:20
  • 2
    You want to put your tweaks in, start a new patch, then when you are finished, lift both patches, reorder your permanent changes ahead of the tweaks and reapply only those, then qfinish. TortoiseHg on Windows makes this a bit easier, but it's the same flow. – Binary Phile Nov 27 '10 at 05:25
1

I found --reverse approach did not work when you have sub repos. i.e.

 hg diff --reverse -S

. In case it helps anyone, this barely tested script seems to do the job:

#!/bin/bash

DIRS="$*"

if [[ $DIRS = "" ]]; then
    DIRS=$(echo *)
fi

for arg in $DIRS; do
    arg=$(echo $arg | sed 's/^\.\/*//g')
    repo=$(echo $arg | cut -d'/' -f-1)

    grep -q "^$repo = " .hgsub 2>/dev/null
    if [ $? -eq 0 ]; then
        if [ -d $repo ]; then
            cd $repo
            hg diff --reverse | sed -e "s;--- a;--- a/$repo;g" -e "s;+++ b;--- b/$repo;g"
            cd ..
        else
            echo Error, unknown repo $repo
        fi
    else
        hg diff $arg --reverse
    fi
done
Goblinhack
  • 2,859
  • 1
  • 26
  • 26