Source

hgexportfiles / exportfiles.py

Full commit
#!/usr/bin/env python
'''
exportfiles

Export all changed files from a revision or revision range.

Copyright 2010 Albert Brand <albert.brand@gmail.com>
Mercurial extension released under the GNU General Public Licence (GPLv2) 
'''

from mercurial import util, cmdutil
from mercurial.i18n import _
from os import path, makedirs

def exportfiles(ui, repo, outputDir, **opts):
    """Export all files changed in a revision or revision range to a directory.

Files in the specified output directory will be overwritten.

Deleted files are not included in the file export.

Cross-branch export (specifying revisions in two branches) is not allowed (and does
not make much sense).
   
Usage:

hg exportfiles -r 10 /tmp/export
    exports all changed files from rev 10 into /tmp/export

hg exportfiles -r 10:20 /tmp/export
    exports all changed files from rev 10 to 20 (inclusive) into /tmp/export
    
hg exportfiles -r 10:tip /tmp/export
    exports all changed files from rev 10 to tip into /tmp/export
    
"""

    # get revision range
    rng = cmdutil.revrange(repo, opts['rev'])
    if not rng:
        raise util.Abort(_('no revisions specified'))

    first = rng[0]
    last = rng[-1]
    revs = inbetween(repo, first, last)

    if not revs:
        raise util.Abort(_('revision %s is not an ancestor of revision %s\n') % (first, last))

    orderedRevs = list(revs)
    orderedRevs.sort()

    ui.write(_('Checking revisions %s\n') % orderedRevs)

    # set of modified/deleted files
    modifiedFiles = set()
    deletedFiles = set()

    # loop over revisions
    for r in orderedRevs:
        # get changeset context for revision
        ctx = repo[r]

        # loop over files
        for file in ctx.files():
            if(file in ctx):
                modifiedFiles.add(file)
            else:
                deletedFiles.add(file)
                modifiedFiles.discard(file)

    # loop over found modifiedFiles
    ui.write(_("Creating files:\n\n"))
    for file in modifiedFiles:
        ui.write(file, "\n")
        
        # create direc        
        dir = outputDir + "/" + path.dirname(file)
        if not path.exists(dir):
            makedirs(dir)

        # get file context in last seen revision context
        fctx = ctx[file]

        # write data from context to file 
        f = open(outputDir + "/" + file, 'w+b')
        f.write(fctx.data())
        f.close()

    ui.write(_("\nDeleted files:\n\n"))
    for file in deletedFiles:
        ui.write(file, "\n")


def inbetween(repo, first, last):
    'Return all revisions between first and last, inclusive'

    if first == last:
        return set([first])
    elif last < first:
        return set()

    parents = [p.rev() for p in repo[last].parents()]

    if not parents:
        return set()

    result = inbetween(repo, first, parents[0])
    if len(parents) == 2:
        result = result | inbetween(repo, first, parents[1])

    if result:
        result.add(last)

    return result

cmdtable = {
    "exportfiles": (exportfiles,
             [('r', 'rev', [], _('Revision or revision range to export'))],
             "OUTPUT_DIR [options]")
}