views:

474

answers:

6

I have a personal Mercurial repository tracking some changes I am working on. I'd like to share these changes with a collaborator, however they don't have/can't get Mercurial, so I need to send the entire file set and the collaborator will merge on their end. I am looking for a way to extract the "tip" version of the subset of files that were modified between two revision numbers. Is there a way to easily do this in Mercurial?

Adding a bounty - This is still a pain for us. We often work with internal "customers" who take our source code releases as a .zip, and testing a small fix is easier to distribute as a .zip overlay than as a patch (since we often don't know the state of their files).

+1  A: 

To my knowledge, there's not a handy tool for this (though a mercurial plugin might be doable). You can export a patch for the fileset, using hg export from:to (where from and to identify revisions.) If you really need the entire files as seen on tip, you could probably hack something together based on the output of hg diff --stat -r from:to , which outputs a list of files with annotations about how many lines were changed, like:

 ...
 src/test/scala/RegressionTest.scala                        |  25 +++++++++++++----------
 src/test/scala/SLDTest.scala                               |   2 +-
 15 files changed, 111 insertions(+), 143 deletions(-)

If none of your files have spaces or special characters in their names, you could use something like:

hg diff -r156:159 --stat | head - --lines=-1 | sed 's!|.*$!!' | xargs zip ../diffed.zip

I'll leave dealing with special characters as an exercise for the reader ;)

David Winslow
+9  A: 

Well. hg export $base:tip > patch.diff will produce a standard patch file, readable by most tools around.

In particular, the GNU patch command can apply the whole patch against the previous files. Isn't it enough? I dont see why you would need the set of files: to me, applying a patch seems easier than extracting files from a zip and copying them to the right place. Plus, if your collaborator has local changes, you will overwrite them. You're not using a Version Control tool to bluntly force the other person to merge manually the changes, right? Let patch deal with that, honestly :)

NicDumZ
I agree that sending a diff is a much better idea. It is just too easy to lose changes when one person gives another person an updated (sub)set of files. I cringed every time I saw whole .java files emailed around in my previous job.
Chris Johnsen
A: 

Here is a small and ugly bash script that will do the job, at least if you work in an Linux environment. This has absolutely no checks what so ever and will most likely break when you have moved a file but it is a start.

Command:

zipChanges.sh REVISION REPOSITORY DESTINATION
zipChanges.sh 3 /home/hg/repo /home/hg/files.tgz

Code:

#!/bin/sh
REV=$1
SRC_REPO=$2
DST_ZIP=$3

cd $SRC_REPO
FILES=$(hg status --rev $1 $SRC_REPO | cut -c3-)

IFS=$'\n'
FILENAMES=""
for line in ${FILES}
do
    FILENAMES=$FILENAMES" \""$SRC_REPO"/"$line"\""
done

CMD="tar czf \"$DST_ZIP\" $FILENAMES"

eval $CMD
OliverS
+3  A: 

The best case scenario is to put the proper pressure on these folks to get Mercurial, but barring that, a patch is probably better than a zipped set of files, since the patch will track deletes and renames. If you still want a zip file, I've written a short script that makes a zip file:

import os, subprocess, sys
from zipfile import ZipFile, ZIP_DEFLATED

def main(revfrom, revto, destination, *args):
    root, err = getoutput("hg root")
    if "no Merurial repository" in err:
        print "This script must be run from within an Hg repository"
        return
    root = root.strip()

    filelist, _ = getoutput("hg status --rev %s:%s" % (revfrom, revto))
    paths = []

    for line in filelist.split('\n'):
        try:
            (status, path) = line.split(' ', 1)
        except ValueError:
            continue
        if status != 'D':
            paths.append(path)

    if len(paths) < 1:
        print "No changed files could be found."
        return

    z = ZipFile(destination, "w", ZIP_DEFLATED)
    os.chdir(root)
    for path in paths:
        z.write(path)
    z.close()

    print "Done."


def getoutput(cmd):
    p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return p.communicate()


if __name__ == '__main__':
    main(*sys.argv[1:])

The usage would be nameofscript.py fromrevision torevision destination. E.g., nameofscript.py 45 51 c:\updates.zip

Sorry about the poor command line interface, but hey the script only took 25 minutes to write.

Note: this should be run from a working directory within a repository.

A: 

Hi,

I know you already have a few answers to this one but a friend of mine had a similar issue and I created a simple program in VB.Net to do this for him perhaps it could help for you too, the prog and a copy of the source is at the bottom of the article linked below.

http://www.simianenterprises.co.uk/blog/mercurial-export-changed-files-80.html

Although this does not let you pick an end revision at the moment, it would be very easy to add that in using the source, however you would have to update to the target revision manually before extracting the files. If needed you could even mod it to create the zip instead of a folder of files (which is also nice and easy to manually zip)

hope this helps either you or anyone else who wants this functionality.

C Brown
A: 

i just contributed an extension here https://sites.google.com/site/alessandronegrin/pack-mercurial-extension

alessandro