Commits

Anonymous committed 29c1e17

converted all text files to unix line endings

Comments (0)

Files changed (22)

-syntax: glob 
-attic.pyc 
+syntax: glob 
+attic.pyc 
-#!/usr/bin/env python
-
-# attic.py - noninteractive shelves for mercurial
-#
-# Copyright 2009 Bill Barry <after.fallout@gmail.com>
-#
-# Portions liberally adapted from mq.py
-# Copyright 2005, 2006 Chris Mason <mason@suse.com>
-#
-# This software may be used and distributed according to the terms
-# of the GNU General Public License, incorporated herein by reference.
-
-"""helps manage uncommited changes with a set of named patches
-
-This extension lets you create patches from uncommited changes using its
-'shelve' command. Shelved changes are unapplied from the working copy and
-stored as patches in the .hg/attic directory.
-
-They can be later restored using the 'unshelve' command, which merges the
-changes back into the working copy.
-
-This allows task switching between many patches in a single repository.
-
-As applied patches are just changes in the working copy they are not part of
-the project history but can, of course, be commited if desired.
-
-Having all known patches in the .hg/attic directory allows you to easily
-share patch sets between repositories and even control version them.
-
-Common tasks (use 'hg help command' for more details):
-
-attic-shelve (shelve):
-    store the current working copy changes in a patch in the attic and
-    prepare to work on something else unapplying those changes
-
-attic-display (attic, ls):
-    list the patches in the attic
-
-attic-unshelve (unshelve):
-    activate a patch to work on and merge its changes into the working copy
-"""
-
-from mercurial.i18n import _
-from mercurial.node import bin, hex, short
-from mercurial.repo import RepoError
-from mercurial import commands, cmdutil, hg, patch, revlog, util
-from mercurial import extensions, repair, fancyopts
-import copy, cStringIO, os, sys, re, errno, tempfile
-
-normname = util.normpath
-
-class attic:
-    """encapsulates all attic functionality that is dependant on state"""
-
-    def __init__(self, ui, path, patchdir=None):
-        """initializes everything, this was copied from mq"""
-        self.basepath = path
-        self.path = patchdir or os.path.join(path, 'attic')
-        self.opener = util.opener(self.path)
-        self.ui = ui
-        self.applied = ''
-        self.appliedfile = '.applied'
-        self.currentpatch = 'default'
-        self.currentfile = '.current'
-        if not os.path.isdir(self.path):
-            try:
-                os.mkdir(self.path)
-            except OSError, inst:
-                if inst.errno != errno.EEXIST or not create:
-                    raise
-        if os.path.exists(self.join(self.appliedfile)):
-            self.applied = self.opener(self.appliedfile).read().strip()
-        if os.path.exists(self.join(self.currentfile)):
-            self.currentpatch = self.opener(self.currentfile).read().strip()
-
-    def diffopts(self, opts={}):
-        """proxies a call to patch.diffopts, providing the ui argument"""
-        # could this be curried like opener is?
-        return patch.diffopts(self.ui, opts)
-
-    def join(self, *p):
-        """proxies a call to join, returning a path relative to the attic dir"""
-        return os.path.join(self.path, *p)
-
-    def remove(self, patch):
-        """removes a patch from the attic dir"""
-        os.unlink(self.join(patch))
-
-    def exists(self, patch):
-        """checks if a patch exists or not"""
-        return os.path.exists(self.join(patch))
-
-    def available(self):
-        '''reads all available patches from the attic dir
-
-        This method skips all paths that start with a '.' so that you can have
-        a repo in the attic dir (just ignore .applied and .currrent).'''
-        available_list = []
-        for root, dirs, files in os.walk(self.path):
-            d = root[len(self.path) + 1:]
-            for f in files:
-                fl = os.path.join(d, f)
-                if (not fl.startswith('.')):
-                    available_list.append(fl)
-        available_list.sort()
-        return available_list
-
-    def persiststate(self):
-        '''persists the state of the attic so that you can avoid using
-        the patch name to call commands'''
-        fp1 = self.opener(self.appliedfile, 'w')
-        fp1.write('%s' % self.applied)
-        fp1.close()
-        fp2 = self.opener(self.currentfile, 'w')
-        fp2.write('%s' % self.currentpatch)
-        fp2.close()
-
-    def patch(self, repo, patchfile):
-        """actually applies a patch; copied from mq"""
-        files = {}
-        try:
-            fuzz = patch.patch(self.join(patchfile), self.ui, strip = 1,
-                               cwd = repo.root, files = files)
-        except Exception, inst:
-            self.ui.note(str(inst) + '\n')
-            if not self.ui.verbose:
-                self.ui.warn('patch failed, unable to continue (try -v)\n')
-            return (False, files, False)
-
-        return (True, files, fuzz)
-
-    def check_localchanges(self, repo, force=False):
-        """guards against local changes; copied from mq"""
-        m, a, r, d = repo.status()[:4]
-        if m or a or r or d:
-            if not force:
-                raise util.Abort(_('local changes found'))
-        return m, a, r, d
-
-    def apply(self, repo, patch, sim, force=False, **opts):
-        """applies a patch and manages repo and attic state"""
-        self.check_localchanges(repo, force)
-        (success, files, fuzz) = self.patch(repo, patch)
-        if success:
-            if files:
-                cmdutil.addremove(repo,
-                                  ['%s/%s' % (repo.root, f) for f in files],
-                                  opts, similarity = sim/100.)
-            self.applied = patch
-            self.currentpatch = patch
-            self.persiststate()
-
-    def readheaders(self, patch):
-        """reads the headers of a patch; copied from mq"""
-        def eatdiff(lines):
-            while lines:
-                l = lines[-1]
-                if (l.startswith('diff -') or
-                    l.startswith('Index:') or
-                    l.startswith('===========')):
-                    del lines[-1]
-                else:
-                    break
-        def eatempty(lines):
-            while lines:
-                l = lines[-1]
-                if re.match('\s*$', l):
-                    del lines[-1]
-                else:
-                    break
-
-        pf = self.join(patch)
-        message = []
-        comments = []
-        user = date = format = subject = None
-        diffstart = 0
-        gitdiff = False
-
-        for line in file(pf):
-            line = line.rstrip()
-            if line.startswith('diff --git'):
-                diffstart = 2
-                gitdiff=True
-                break
-            if diffstart:
-                if line.startswith('+++ '):
-                    diffstart = 2
-                break
-            if line.startswith('--- '):
-                diffstart = 1
-                continue
-            elif format == 'hgpatch':
-                # parse values when importing the result of an hg export
-                if line.startswith('# User '):
-                    user = line[7:]
-                elif line.startswith('# Date '):
-                    date = line[7:]
-                elif not line.startswith('# ') and line:
-                    message.append(line)
-                    format = None
-            elif line == '# HG changeset patch':
-                format = 'hgpatch'
-            elif (format != 'tagdone' and (line.startswith('Subject: ') or
-                                           line.startswith('subject: '))):
-                subject = line[9:]
-                format = 'tag'
-            elif (format != 'tagdone' and (line.startswith('From: ') or
-                                           line.startswith('from: '))):
-                user = line[6:]
-                format = 'tag'
-            elif format == 'tag' and line == '':
-                # when looking for tags (subject: from: etc) they
-                # end once you find a blank line in the source
-                format = 'tagdone'
-            elif message or line:
-                message.append(line)
-            comments.append(line)
-
-        eatdiff(message)
-        eatdiff(comments)
-        eatempty(message)
-        eatempty(comments)
-
-        # make sure message isn't empty
-        if format and format.startswith('tag') and subject:
-            message.insert(0, '')
-            message.insert(0, subject)
-        return (message, comments, user, date, diffstart > 1, gitdiff)
-
-    def createpatch(self, repo, name, msg, user, date, pats=[], opts={}):
-        """creates a patch from the current state of the working copy"""
-        p = self.opener(name, 'w')
-        if date:
-            p.write('# HG changeset patch\n')
-            if user:
-                p.write('# User ' + user + '\n')
-            p.write('# Date %d %d\n' % date)
-            p.write('\n')
-        elif user:
-            p.write('From: ' + user + '\n')
-            p.write('\n')
-        if msg:
-            if not isinstance(msg, str):
-                msg = '\n'.join(msg)
-            msg = msg + '\n'
-            p.write(msg)
-        m = cmdutil.match(repo, pats, opts)
-        chunks = patch.diff(repo, match = m, opts = self.diffopts(opts))
-        for chunk in chunks:
-            p.write(chunk)
-        p.close()
-        self.currentpatch=name
-        self.persiststate()
-
-    def cleanup(self, repo):
-        '''removes all changes from the working copy and makes it so
-        there isn't a patch applied'''
-        node = repo.dirstate.parents()[0]
-        hg.clean(repo, node, False)
-        self.applied = ''
-        self.persiststate()
-
-    def resetdefault(self):
-        '''resets the default patch
-        (the next command will require a patch name)'''
-        self.applied = ''
-        self.currentpatch = ''
-        self.persiststate()
-
-def setupheaderopts(ui, opts):
-    """sets the user and date; copied from mq"""
-    def do(opt, val):
-        if not opts.get(opt) and opts.get('current' + opt):
-            opts[opt] = val
-    do('user', ui.username())
-    do('date', '%d %d' % util.makedate())
-
-def makepatch(ui, repo, name=None, pats=None, opts=None):
-    """sets up the call for attic.createpatch and makes the call"""
-    if pats == None:
-        pats = []
-    if opts == None:
-        opts = {}
-
-    s = repo.attic
-    usr = msg = ''
-    d = date = user = None
-    force = opts.get('force')
-
-    if name and s.exists(name) and name != s.applied and not force:
-        raise util.Abort(_('attempting to overwrite existing patch'))
-    if name and s.applied and name != s.applied and not force:
-        raise util.Abort(_('a different patch is active'))
-    if not name:
-        name = s.applied
-    if not name:
-        raise util.Abort(_('you need to supply a patch name'))
-    message = cmdutil.logmessage(opts)
-    if opts.get('edit'):
-        message = ui.edit(message, ui.username())
-    setupheaderopts(ui, opts)
-    if opts.get('user'):
-        usr=opts['user']
-    if opts.get('date'):
-        d=opts['date']
-    if s.applied:
-        (msg, comments, user, date, hasdiff, gitdiff) = s.readheaders(s.applied)
-        if gitdiff:
-            opts['git'] = 1
-    if message:
-        msg = message
-    if usr:
-        user = usr
-    if d:
-        date = d
-    if date:
-        date = util.parsedate(date)
-    s.createpatch(repo, name, msg, user, date, pats, opts)
-
-def headerinfo(ui, repo, name):
-    s = repo.attic
-    (message, comments, user, date, hasdiff, g) = s.readheaders(name)
-    if not isinstance(message, str):
-        message = '\n'.join(message)
-    if not message:
-        message = None
-    else:
-        message = '\n' + message
-    repo.ui.write(_('user: %s\ndate: %s\nmessage: %s\n') %
-                  (user, date, message))
-
-def currentinfo(ui, repo):
-    """lists the current active patch"""
-    s = repo.attic
-    active = s.applied
-    default = s.currentpatch
-    if not active and not default:
-        repo.ui.write(_('no patch active or default set\n'))
-    elif not active:
-        repo.ui.write(_('no patch active; default: %s\n') % (default))
-    if active:
-        repo.ui.write('active patch: %s\n' % (active))
-        headerinfo(ui, repo, active)
-
-def refilterpatch(allchunk, selected):
-    """return chunks not in selected"""
-    try:
-        record = extensions.find('record')
-    except KeyError:
-        raise util.Abort(_("'record' extension not loaded"))
-    l = []
-    fil = []
-    for c in allchunk:
-        if isinstance(c, record.header):
-            if len(l) > 1 and l[0] in selected:
-                fil += l
-            l = [c]
-        elif c not in selected:
-            l.append(c)
-    if len(l) > 1 and l[0] in selected:
-        fil += l
-    return fil
-
-def makebackup(ui, repo, dir, files):
-    """make a backup for the files pointed to in the files parameter"""
-    try:
-        os.mkdir(dir)
-    except OSError, err:
-        if err.errno != errno.EEXIST:
-            raise
-
-    backups = {}
-    for f in files:
-        fd, tmpname = tempfile.mkstemp(prefix = f.replace('/', '_')+'.',
-                                       dir = dir)
-        os.close(fd)
-        ui.debug('backup %r as %r\n' % (f, tmpname))
-        util.copyfile(repo.wjoin(f), tmpname)
-        backups[f] = tmpname
-
-    return backups
-
-def interactiveshelve(ui, repo, name, pats, opts):
-    """interactively select changes to set aside"""
-    if not ui.interactive:
-        raise util.Abort(_('shelve --interactive can only be run interactively'))
-    try:
-        record = extensions.find('record')
-    except KeyError:
-        raise util.Abort(_("'record' extension not loaded"))
-
-    def shelvefunc(ui, repo, message, match, opts):
-        files = []
-        if match.files():
-            changes = None
-        else:
-            changes = repo.status(match = match)[:3]
-            modified, added, removed = changes
-            files = modified + added + removed
-            match = cmdutil.matchfiles(repo, files)
-        diffopts = repo.attic.diffopts( {'git':True, 'nodates':True})
-        chunks = patch.diff(repo, repo.dirstate.parents()[0], match = match,
-                            changes = changes, opts = diffopts)
-        fp = cStringIO.StringIO()
-        fp.write(''.join(chunks))
-        fp.seek(0)
-
-        # 1. filter patch, so we have intending-to apply subset of it
-        ac = record.parsepatch(fp)
-        chunks = record.filterpatch(ui, ac)
-        # and a not-intending-to apply subset of it
-        rc = refilterpatch(ac, chunks)
-        del fp
-
-        contenders = {}
-        for h in chunks:
-            try: contenders.update(dict.fromkeys(h.files()))
-            except AttributeError: pass
-
-        newfiles = [f for f in files if f in contenders]
-
-        if not newfiles:
-            ui.status(_('no changes to shelve\n'))
-            return 0
-
-        modified = dict.fromkeys(changes[0])
-        backups = {}
-        backupdir = repo.join('shelve-backups')
-
-        try:
-            bkfiles = [f for f in newfiles if f in modified]
-            backups = makebackup(ui, repo, backupdir, bkfiles)
-
-            # patch to shelve
-            sp = cStringIO.StringIO()
-            for c in chunks:
-                if c.filename() in backups:
-                    c.write(sp)
-            doshelve = sp.tell()
-            sp.seek(0)
-
-            # patch to apply to shelved files
-            fp = cStringIO.StringIO()
-            for c in rc:
-                if c.filename() in backups:
-                    c.write(fp)
-            dopatch = fp.tell()
-            fp.seek(0)
-
-            try:
-                # 3a. apply filtered patch to clean repo (clean)
-                if backups:
-                    hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
-
-                # 3b. apply filtered patch to clean repo (apply)
-                if dopatch:
-                    ui.debug('applying patch\n')
-                    ui.debug(fp.getvalue())
-                    patch.internalpatch(fp, ui, 1, repo.root)
-                del fp
-
-                # 3c. apply filtered patch to clean repo (shelve)
-                if doshelve:
-                    ui.debug("saving patch to %s\n" % (name))
-                    s = repo.attic
-                    f = s.opener(name, 'w')
-                    f.write(sp.getvalue())
-                    del f
-                    s.currentpatch = name
-                    s.persiststate()
-                del sp
-            except:
-                try:
-                    for realname, tmpname in backups.iteritems():
-                        ui.debug('restoring %r to %r\n' % (tmpname, realname))
-                        util.copyfile(tmpname, repo.wjoin(realname))
-                except OSError:
-                    pass
-
-            return 0
-        finally:
-            try:
-                for realname, tmpname in backups.iteritems():
-                    ui.debug('removing backup for %r : %r\n' % (realname, tmpname))
-                    os.unlink(tmpname)
-                os.rmdir(backupdir)
-            except OSError:
-                pass
-    fancyopts.fancyopts([], commands.commitopts, opts)
-    return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
-
-def shelve(ui, repo, name=None, *pats, **opts):
-    """saves a patch to the attic from the current changes
-    and removes them from the working copy.
-
-    Note that only those changes done in tracked files will be considered
-    so you may wanto to hg add untracked files with desired changes.
-    """
-    s = repo.attic
-    if not name and s.currentpatch == 'default':
-        name = 'default'
-    if opts['interactive']:
-        interactiveshelve(ui, repo, name, pats, opts)
-        repo.ui.status(_('Patch %s shelved\n' % (s.currentpatch)))
-    else:
-        makepatch(ui, repo, name, pats, opts)
-        if opts['refresh']:
-            if name:
-                s.applied = name
-                s.persiststate()
-            repo.ui.status(_('Patch %s refreshed\n') % (s.applied))
-        else:
-            s.cleanup(repo)
-            repo.ui.status(_('Patch %s shelved\n' % (s.currentpatch)))
-
-def unshelve(ui, repo, name = None, **opts):
-    """applies a patch from the attic to the working copy"""
-    s = repo.attic
-    force = opts['force']
-    if s.applied and not force:
-        raise util.Abort(_('cannot apply a patch over an already active patch'))
-    if not name:
-        name = s.currentpatch
-    if not name:
-        raise util.Abort(_('patch name must be supplied'))
-    try:
-        sim = float(opts.get('similarity') or 0)
-    except ValueError:
-        raise util.Abort(_('similarity must be a number'))
-    if sim < 0 or sim > 100:
-        raise util.Abort(_('similarity must be between 0 and 100'))
-    s.apply(repo, name, sim, **opts)
-    s.persiststate()
-    repo.ui.status(_('Patch %s unshelved\n') % (s.applied))
-    if opts['delete']:
-        s.remove(name)
-        repo.ui.status(_('patch removed\n'))
-        s.resetdefault()
-
-def listattic(ui, repo, **opts):
-    """lists the available patches in the attic"""
-    if opts['current']:
-        currentinfo(ui, repo)
-    elif opts['header']:
-        headerinfo(ui, repo, opts['header'])
-    else:
-        available = repo.attic.available()
-        repo.ui.write('\n'.join(available))
-        if len(available) > 0:
-            repo.ui.write('\n')
-
-def commitwrapper(orig, ui, repo, *args, **opts):
-    s = repo.attic
-    name = s.applied
-    if not name:
-        orig(ui, repo, *args, **opts)
-    else:
-        makepatch(ui, repo, name, [], opts)
-        (m, comments, u, d, hasdiff, gitdiff) = s.readheaders(name)
-        orig(ui, repo, message = '\n'.join(m),
-             logfile = None, user = u, date = d)
-
-        if not opts['keep']:
-            s.remove(name)
-        s.resetdefault()
-
-def reposetup(ui, repo):
-    if repo.local():
-        repo.attic = attic(ui, repo.join(''))
-
-def uisetup(ui):
-    'Replace commit with a decorator to take care of shelves'
-    entry = extensions.wrapcommand(commands.table, 'commit', commitwrapper)
-    entry[1].append(('k', 'keep', None, _('keep patch file if it is a shelf')))
-
-headeropts = [
-    ('U', 'currentuser', None, _('add \'From: <current user>\' to patch')),
-    ('u', 'user', '', _('add \'From: <given user>\' to patch')),
-    ('D', 'currentdate', None, _('add \'Date: <current date>\' to patch')),
-    ('d', 'date', '', _('add \'Date: <given date>\' to patch'))]
-
-cmdtable = {
-    'attic-shelve|shelve': (
-        shelve, [
-            ('e', 'edit', None, _('edit commit message')),
-            ('f', 'force', None,
-                _('force save to file given, overridding pre-existing file')),
-            ('g', 'git', None, _('use git extended diff format')),
-            ('r', 'refresh', None,
-                _('refresh the current patch without stowing it away')),
-            ('i', 'interactive', None,
-                _('use the \'record\' extension ' +
-                  'to create a patch interactively')),
-            ] + commands.walkopts + commands.commitopts + headeropts,
-        _('hg attic-shelve [options] [name]')),
-
-    'attic-display|attic|ls': (
-        listattic, [
-            ('c', 'current', None,
-                _('show information about the current patch being worked on')),
-            ('d', 'header', '',
-                _('show information about <given patch name>'))
-        ],
-        _('hg attic-display [-c | -d name]')),
-
-    'attic-unshelve|unshelve': (
-        unshelve, [
-            ('f', 'force', None, _('force patch over existing changes')),
-            ('n', 'dry-run', None,
-                _('do not mark files as added or removed, and print changes')),
-            ('', 'delete', None,
-                _("don't keep the patch in the attic after applying it")),
-            ('s', 'similarity', '',
-                _('guess renamed files by similarity (0<=s<=100)'))],
-        _('hg attic-unshelve [-f] [-n] [-s #] [name]'))
-}
+#!/usr/bin/env python
+
+# attic.py - noninteractive shelves for mercurial
+#
+# Copyright 2009 Bill Barry <after.fallout@gmail.com>
+#
+# Portions liberally adapted from mq.py
+# Copyright 2005, 2006 Chris Mason <mason@suse.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+"""helps manage uncommited changes with a set of named patches
+
+This extension lets you create patches from uncommited changes using its
+'shelve' command. Shelved changes are unapplied from the working copy and
+stored as patches in the .hg/attic directory.
+
+They can be later restored using the 'unshelve' command, which merges the
+changes back into the working copy.
+
+This allows task switching between many patches in a single repository.
+
+As applied patches are just changes in the working copy they are not part of
+the project history but can, of course, be commited if desired.
+
+Having all known patches in the .hg/attic directory allows you to easily
+share patch sets between repositories and even control version them.
+
+Common tasks (use 'hg help command' for more details):
+
+attic-shelve (shelve):
+    store the current working copy changes in a patch in the attic and
+    prepare to work on something else unapplying those changes
+
+attic-display (attic, ls):
+    list the patches in the attic
+
+attic-unshelve (unshelve):
+    activate a patch to work on and merge its changes into the working copy
+"""
+
+from mercurial.i18n import _
+from mercurial.node import bin, hex, short
+from mercurial.repo import RepoError
+from mercurial import commands, cmdutil, hg, patch, revlog, util
+from mercurial import extensions, repair, fancyopts
+import copy, cStringIO, os, sys, re, errno, tempfile
+
+normname = util.normpath
+
+class attic:
+    """encapsulates all attic functionality that is dependant on state"""
+
+    def __init__(self, ui, path, patchdir=None):
+        """initializes everything, this was copied from mq"""
+        self.basepath = path
+        self.path = patchdir or os.path.join(path, 'attic')
+        self.opener = util.opener(self.path)
+        self.ui = ui
+        self.applied = ''
+        self.appliedfile = '.applied'
+        self.currentpatch = 'default'
+        self.currentfile = '.current'
+        if not os.path.isdir(self.path):
+            try:
+                os.mkdir(self.path)
+            except OSError, inst:
+                if inst.errno != errno.EEXIST or not create:
+                    raise
+        if os.path.exists(self.join(self.appliedfile)):
+            self.applied = self.opener(self.appliedfile).read().strip()
+        if os.path.exists(self.join(self.currentfile)):
+            self.currentpatch = self.opener(self.currentfile).read().strip()
+
+    def diffopts(self, opts={}):
+        """proxies a call to patch.diffopts, providing the ui argument"""
+        # could this be curried like opener is?
+        return patch.diffopts(self.ui, opts)
+
+    def join(self, *p):
+        """proxies a call to join, returning a path relative to the attic dir"""
+        return os.path.join(self.path, *p)
+
+    def remove(self, patch):
+        """removes a patch from the attic dir"""
+        os.unlink(self.join(patch))
+
+    def exists(self, patch):
+        """checks if a patch exists or not"""
+        return os.path.exists(self.join(patch))
+
+    def available(self):
+        '''reads all available patches from the attic dir
+
+        This method skips all paths that start with a '.' so that you can have
+        a repo in the attic dir (just ignore .applied and .currrent).'''
+        available_list = []
+        for root, dirs, files in os.walk(self.path):
+            d = root[len(self.path) + 1:]
+            for f in files:
+                fl = os.path.join(d, f)
+                if (not fl.startswith('.')):
+                    available_list.append(fl)
+        available_list.sort()
+        return available_list
+
+    def persiststate(self):
+        '''persists the state of the attic so that you can avoid using
+        the patch name to call commands'''
+        fp1 = self.opener(self.appliedfile, 'w')
+        fp1.write('%s' % self.applied)
+        fp1.close()
+        fp2 = self.opener(self.currentfile, 'w')
+        fp2.write('%s' % self.currentpatch)
+        fp2.close()
+
+    def patch(self, repo, patchfile):
+        """actually applies a patch; copied from mq"""
+        files = {}
+        try:
+            fuzz = patch.patch(self.join(patchfile), self.ui, strip = 1,
+                               cwd = repo.root, files = files)
+        except Exception, inst:
+            self.ui.note(str(inst) + '\n')
+            if not self.ui.verbose:
+                self.ui.warn('patch failed, unable to continue (try -v)\n')
+            return (False, files, False)
+
+        return (True, files, fuzz)
+
+    def check_localchanges(self, repo, force=False):
+        """guards against local changes; copied from mq"""
+        m, a, r, d = repo.status()[:4]
+        if m or a or r or d:
+            if not force:
+                raise util.Abort(_('local changes found'))
+        return m, a, r, d
+
+    def apply(self, repo, patch, sim, force=False, **opts):
+        """applies a patch and manages repo and attic state"""
+        self.check_localchanges(repo, force)
+        (success, files, fuzz) = self.patch(repo, patch)
+        if success:
+            if files:
+                cmdutil.addremove(repo,
+                                  ['%s/%s' % (repo.root, f) for f in files],
+                                  opts, similarity = sim/100.)
+            self.applied = patch
+            self.currentpatch = patch
+            self.persiststate()
+
+    def readheaders(self, patch):
+        """reads the headers of a patch; copied from mq"""
+        def eatdiff(lines):
+            while lines:
+                l = lines[-1]
+                if (l.startswith('diff -') or
+                    l.startswith('Index:') or
+                    l.startswith('===========')):
+                    del lines[-1]
+                else:
+                    break
+        def eatempty(lines):
+            while lines:
+                l = lines[-1]
+                if re.match('\s*$', l):
+                    del lines[-1]
+                else:
+                    break
+
+        pf = self.join(patch)
+        message = []
+        comments = []
+        user = date = format = subject = None
+        diffstart = 0
+        gitdiff = False
+
+        for line in file(pf):
+            line = line.rstrip()
+            if line.startswith('diff --git'):
+                diffstart = 2
+                gitdiff=True
+                break
+            if diffstart:
+                if line.startswith('+++ '):
+                    diffstart = 2
+                break
+            if line.startswith('--- '):
+                diffstart = 1
+                continue
+            elif format == 'hgpatch':
+                # parse values when importing the result of an hg export
+                if line.startswith('# User '):
+                    user = line[7:]
+                elif line.startswith('# Date '):
+                    date = line[7:]
+                elif not line.startswith('# ') and line:
+                    message.append(line)
+                    format = None
+            elif line == '# HG changeset patch':
+                format = 'hgpatch'
+            elif (format != 'tagdone' and (line.startswith('Subject: ') or
+                                           line.startswith('subject: '))):
+                subject = line[9:]
+                format = 'tag'
+            elif (format != 'tagdone' and (line.startswith('From: ') or
+                                           line.startswith('from: '))):
+                user = line[6:]
+                format = 'tag'
+            elif format == 'tag' and line == '':
+                # when looking for tags (subject: from: etc) they
+                # end once you find a blank line in the source
+                format = 'tagdone'
+            elif message or line:
+                message.append(line)
+            comments.append(line)
+
+        eatdiff(message)
+        eatdiff(comments)
+        eatempty(message)
+        eatempty(comments)
+
+        # make sure message isn't empty
+        if format and format.startswith('tag') and subject:
+            message.insert(0, '')
+            message.insert(0, subject)
+        return (message, comments, user, date, diffstart > 1, gitdiff)
+
+    def createpatch(self, repo, name, msg, user, date, pats=[], opts={}):
+        """creates a patch from the current state of the working copy"""
+        p = self.opener(name, 'w')
+        if date:
+            p.write('# HG changeset patch\n')
+            if user:
+                p.write('# User ' + user + '\n')
+            p.write('# Date %d %d\n' % date)
+            p.write('\n')
+        elif user:
+            p.write('From: ' + user + '\n')
+            p.write('\n')
+        if msg:
+            if not isinstance(msg, str):
+                msg = '\n'.join(msg)
+            msg = msg + '\n'
+            p.write(msg)
+        m = cmdutil.match(repo, pats, opts)
+        chunks = patch.diff(repo, match = m, opts = self.diffopts(opts))
+        for chunk in chunks:
+            p.write(chunk)
+        p.close()
+        self.currentpatch=name
+        self.persiststate()
+
+    def cleanup(self, repo):
+        '''removes all changes from the working copy and makes it so
+        there isn't a patch applied'''
+        node = repo.dirstate.parents()[0]
+        hg.clean(repo, node, False)
+        self.applied = ''
+        self.persiststate()
+
+    def resetdefault(self):
+        '''resets the default patch
+        (the next command will require a patch name)'''
+        self.applied = ''
+        self.currentpatch = ''
+        self.persiststate()
+
+def setupheaderopts(ui, opts):
+    """sets the user and date; copied from mq"""
+    def do(opt, val):
+        if not opts.get(opt) and opts.get('current' + opt):
+            opts[opt] = val
+    do('user', ui.username())
+    do('date', '%d %d' % util.makedate())
+
+def makepatch(ui, repo, name=None, pats=None, opts=None):
+    """sets up the call for attic.createpatch and makes the call"""
+    if pats == None:
+        pats = []
+    if opts == None:
+        opts = {}
+
+    s = repo.attic
+    usr = msg = ''
+    d = date = user = None
+    force = opts.get('force')
+
+    if name and s.exists(name) and name != s.applied and not force:
+        raise util.Abort(_('attempting to overwrite existing patch'))
+    if name and s.applied and name != s.applied and not force:
+        raise util.Abort(_('a different patch is active'))
+    if not name:
+        name = s.applied
+    if not name:
+        raise util.Abort(_('you need to supply a patch name'))
+    message = cmdutil.logmessage(opts)
+    if opts.get('edit'):
+        message = ui.edit(message, ui.username())
+    setupheaderopts(ui, opts)
+    if opts.get('user'):
+        usr=opts['user']
+    if opts.get('date'):
+        d=opts['date']
+    if s.applied:
+        (msg, comments, user, date, hasdiff, gitdiff) = s.readheaders(s.applied)
+        if gitdiff:
+            opts['git'] = 1
+    if message:
+        msg = message
+    if usr:
+        user = usr
+    if d:
+        date = d
+    if date:
+        date = util.parsedate(date)
+    s.createpatch(repo, name, msg, user, date, pats, opts)
+
+def headerinfo(ui, repo, name):
+    s = repo.attic
+    (message, comments, user, date, hasdiff, g) = s.readheaders(name)
+    if not isinstance(message, str):
+        message = '\n'.join(message)
+    if not message:
+        message = None
+    else:
+        message = '\n' + message
+    repo.ui.write(_('user: %s\ndate: %s\nmessage: %s\n') %
+                  (user, date, message))
+
+def currentinfo(ui, repo):
+    """lists the current active patch"""
+    s = repo.attic
+    active = s.applied
+    default = s.currentpatch
+    if not active and not default:
+        repo.ui.write(_('no patch active or default set\n'))
+    elif not active:
+        repo.ui.write(_('no patch active; default: %s\n') % (default))
+    if active:
+        repo.ui.write('active patch: %s\n' % (active))
+        headerinfo(ui, repo, active)
+
+def refilterpatch(allchunk, selected):
+    """return chunks not in selected"""
+    try:
+        record = extensions.find('record')
+    except KeyError:
+        raise util.Abort(_("'record' extension not loaded"))
+    l = []
+    fil = []
+    for c in allchunk:
+        if isinstance(c, record.header):
+            if len(l) > 1 and l[0] in selected:
+                fil += l
+            l = [c]
+        elif c not in selected:
+            l.append(c)
+    if len(l) > 1 and l[0] in selected:
+        fil += l
+    return fil
+
+def makebackup(ui, repo, dir, files):
+    """make a backup for the files pointed to in the files parameter"""
+    try:
+        os.mkdir(dir)
+    except OSError, err:
+        if err.errno != errno.EEXIST:
+            raise
+
+    backups = {}
+    for f in files:
+        fd, tmpname = tempfile.mkstemp(prefix = f.replace('/', '_')+'.',
+                                       dir = dir)
+        os.close(fd)
+        ui.debug('backup %r as %r\n' % (f, tmpname))
+        util.copyfile(repo.wjoin(f), tmpname)
+        backups[f] = tmpname
+
+    return backups
+
+def interactiveshelve(ui, repo, name, pats, opts):
+    """interactively select changes to set aside"""
+    if not ui.interactive:
+        raise util.Abort(_('shelve --interactive can only be run interactively'))
+    try:
+        record = extensions.find('record')
+    except KeyError:
+        raise util.Abort(_("'record' extension not loaded"))
+
+    def shelvefunc(ui, repo, message, match, opts):
+        files = []
+        if match.files():
+            changes = None
+        else:
+            changes = repo.status(match = match)[:3]
+            modified, added, removed = changes
+            files = modified + added + removed
+            match = cmdutil.matchfiles(repo, files)
+        diffopts = repo.attic.diffopts( {'git':True, 'nodates':True})
+        chunks = patch.diff(repo, repo.dirstate.parents()[0], match = match,
+                            changes = changes, opts = diffopts)
+        fp = cStringIO.StringIO()
+        fp.write(''.join(chunks))
+        fp.seek(0)
+
+        # 1. filter patch, so we have intending-to apply subset of it
+        ac = record.parsepatch(fp)
+        chunks = record.filterpatch(ui, ac)
+        # and a not-intending-to apply subset of it
+        rc = refilterpatch(ac, chunks)
+        del fp
+
+        contenders = {}
+        for h in chunks:
+            try: contenders.update(dict.fromkeys(h.files()))
+            except AttributeError: pass
+
+        newfiles = [f for f in files if f in contenders]
+
+        if not newfiles:
+            ui.status(_('no changes to shelve\n'))
+            return 0
+
+        modified = dict.fromkeys(changes[0])
+        backups = {}
+        backupdir = repo.join('shelve-backups')
+
+        try:
+            bkfiles = [f for f in newfiles if f in modified]
+            backups = makebackup(ui, repo, backupdir, bkfiles)
+
+            # patch to shelve
+            sp = cStringIO.StringIO()
+            for c in chunks:
+                if c.filename() in backups:
+                    c.write(sp)
+            doshelve = sp.tell()
+            sp.seek(0)
+
+            # patch to apply to shelved files
+            fp = cStringIO.StringIO()
+            for c in rc:
+                if c.filename() in backups:
+                    c.write(fp)
+            dopatch = fp.tell()
+            fp.seek(0)
+
+            try:
+                # 3a. apply filtered patch to clean repo (clean)
+                if backups:
+                    hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
+
+                # 3b. apply filtered patch to clean repo (apply)
+                if dopatch:
+                    ui.debug('applying patch\n')
+                    ui.debug(fp.getvalue())
+                    patch.internalpatch(fp, ui, 1, repo.root)
+                del fp
+
+                # 3c. apply filtered patch to clean repo (shelve)
+                if doshelve:
+                    ui.debug("saving patch to %s\n" % (name))
+                    s = repo.attic
+                    f = s.opener(name, 'w')
+                    f.write(sp.getvalue())
+                    del f
+                    s.currentpatch = name
+                    s.persiststate()
+                del sp
+            except:
+                try:
+                    for realname, tmpname in backups.iteritems():
+                        ui.debug('restoring %r to %r\n' % (tmpname, realname))
+                        util.copyfile(tmpname, repo.wjoin(realname))
+                except OSError:
+                    pass
+
+            return 0
+        finally:
+            try:
+                for realname, tmpname in backups.iteritems():
+                    ui.debug('removing backup for %r : %r\n' % (realname, tmpname))
+                    os.unlink(tmpname)
+                os.rmdir(backupdir)
+            except OSError:
+                pass
+    fancyopts.fancyopts([], commands.commitopts, opts)
+    return cmdutil.commit(ui, repo, shelvefunc, pats, opts)
+
+def shelve(ui, repo, name=None, *pats, **opts):
+    """saves a patch to the attic from the current changes
+    and removes them from the working copy.
+
+    Note that only those changes done in tracked files will be considered
+    so you may wanto to hg add untracked files with desired changes.
+    """
+    s = repo.attic
+    if not name and s.currentpatch == 'default':
+        name = 'default'
+    if opts['interactive']:
+        interactiveshelve(ui, repo, name, pats, opts)
+        repo.ui.status(_('Patch %s shelved\n' % (s.currentpatch)))
+    else:
+        makepatch(ui, repo, name, pats, opts)
+        if opts['refresh']:
+            if name:
+                s.applied = name
+                s.persiststate()
+            repo.ui.status(_('Patch %s refreshed\n') % (s.applied))
+        else:
+            s.cleanup(repo)
+            repo.ui.status(_('Patch %s shelved\n' % (s.currentpatch)))
+
+def unshelve(ui, repo, name = None, **opts):
+    """applies a patch from the attic to the working copy"""
+    s = repo.attic
+    force = opts['force']
+    if s.applied and not force:
+        raise util.Abort(_('cannot apply a patch over an already active patch'))
+    if not name:
+        name = s.currentpatch
+    if not name:
+        raise util.Abort(_('patch name must be supplied'))
+    try:
+        sim = float(opts.get('similarity') or 0)
+    except ValueError:
+        raise util.Abort(_('similarity must be a number'))
+    if sim < 0 or sim > 100:
+        raise util.Abort(_('similarity must be between 0 and 100'))
+    s.apply(repo, name, sim, **opts)
+    s.persiststate()
+    repo.ui.status(_('Patch %s unshelved\n') % (s.applied))
+    if opts['delete']:
+        s.remove(name)
+        repo.ui.status(_('patch removed\n'))
+        s.resetdefault()
+
+def listattic(ui, repo, **opts):
+    """lists the available patches in the attic"""
+    if opts['current']:
+        currentinfo(ui, repo)
+    elif opts['header']:
+        headerinfo(ui, repo, opts['header'])
+    else:
+        available = repo.attic.available()
+        repo.ui.write('\n'.join(available))
+        if len(available) > 0:
+            repo.ui.write('\n')
+
+def commitwrapper(orig, ui, repo, *args, **opts):
+    s = repo.attic
+    name = s.applied
+    if not name:
+        orig(ui, repo, *args, **opts)
+    else:
+        makepatch(ui, repo, name, [], opts)
+        (m, comments, u, d, hasdiff, gitdiff) = s.readheaders(name)
+        orig(ui, repo, message = '\n'.join(m),
+             logfile = None, user = u, date = d)
+
+        if not opts['keep']:
+            s.remove(name)
+        s.resetdefault()
+
+def reposetup(ui, repo):
+    if repo.local():
+        repo.attic = attic(ui, repo.join(''))
+
+def uisetup(ui):
+    'Replace commit with a decorator to take care of shelves'
+    entry = extensions.wrapcommand(commands.table, 'commit', commitwrapper)
+    entry[1].append(('k', 'keep', None, _('keep patch file if it is a shelf')))
+
+headeropts = [
+    ('U', 'currentuser', None, _('add \'From: <current user>\' to patch')),
+    ('u', 'user', '', _('add \'From: <given user>\' to patch')),
+    ('D', 'currentdate', None, _('add \'Date: <current date>\' to patch')),
+    ('d', 'date', '', _('add \'Date: <given date>\' to patch'))]
+
+cmdtable = {
+    'attic-shelve|shelve': (
+        shelve, [
+            ('e', 'edit', None, _('edit commit message')),
+            ('f', 'force', None,
+                _('force save to file given, overridding pre-existing file')),
+            ('g', 'git', None, _('use git extended diff format')),
+            ('r', 'refresh', None,
+                _('refresh the current patch without stowing it away')),
+            ('i', 'interactive', None,
+                _('use the \'record\' extension ' +
+                  'to create a patch interactively')),
+            ] + commands.walkopts + commands.commitopts + headeropts,
+        _('hg attic-shelve [options] [name]')),
+
+    'attic-display|attic|ls': (
+        listattic, [
+            ('c', 'current', None,
+                _('show information about the current patch being worked on')),
+            ('d', 'header', '',
+                _('show information about <given patch name>'))
+        ],
+        _('hg attic-display [-c | -d name]')),
+
+    'attic-unshelve|unshelve': (
+        unshelve, [
+            ('f', 'force', None, _('force patch over existing changes')),
+            ('n', 'dry-run', None,
+                _('do not mark files as added or removed, and print changes')),
+            ('', 'delete', None,
+                _("don't keep the patch in the attic after applying it")),
+            ('s', 'similarity', '',
+                _('guess renamed files by similarity (0<=s<=100)'))],
+        _('hg attic-unshelve [-f] [-n] [-s #] [name]'))
+}
-Thanks for grabbing the hgattic extension. I hope you find it useful.
-
-To enable this extension add the following to your hgrc:
-
-    [extensions]
-    hgattic = /path/to/hgattic/repo/attic.py
-
-If you need any help, feel free to contact me via email: after.fallout@gmail.com (english please :-), my french and german aren't very good yet). I live in Montana, USA (GMT -7) and most days I frequently check my email between 7am and 10pm.
-
-Patches/contributions are welcome, preferably (in no order):
-1. create a fork/queue on bitbucket
-2. email patch (attachments please, Thunderbird and Gmail both mess with whitespace)
-3. just ask for write access to the repository on bitbucket
-
-
-If you choose to email a patch, you can add the following to your hgrc for patchbomb:
-    [defaults]
-    email = -a
-    
-    [email]
-    from = your name <your@email>
-    to = after.fallout@gmail.com
-
-
-#Known issues
-
-None
-
-#Potential future work
-
-* fix bugs
-* regex/glob option to attic-display to filter out files from the list 
-* create commands to do basic work with the attic repo (commit, push, 
-  pull, addrem, init, ...)
-* override import to provide default path for patches from attic (so
-  you don't have to unshelve before finishing them or manually provide
-  the path to import)
-* if mq is in use:
-  * override qnew to move an attic patch into mq 
-  * provide a new command to pull patches out of mq and into attic
-    (perhaps just "hg shelve -q")    
-  * possibly allow shelving/unshelving entire queue series into 
-    folders in attic:
-    
-    "hg shelve -q foldername --all" would:
-    1. pop all patches in mq
-    2. create folder "foldername" in attic
-    3. move all patches and the series file into this folder
-    
-    "hg shelve -q foldername --applied"
-    1. split series into two parts: applied and unapplied
-    2. pop all patches in mq
-    3. create folder "foldername" in attic
-    4. move all patches in applied list into the attic and 
-       remove them from series
-    
-    "hg shelve -q foldername --unapplied"
-    (same thing with unapplied patches)
-    
-    "hg unshelve -q foldername"
-    1. "hg shelve -q defaultfolder --all"
-    2. move contents of foldername into the patches dir
-* provide new commands for dealing with patches like import/export (don't 
-  know if this is really necessary)
+Thanks for grabbing the hgattic extension. I hope you find it useful.
+
+To enable this extension add the following to your hgrc:
+
+    [extensions]
+    hgattic = /path/to/hgattic/repo/attic.py
+
+If you need any help, feel free to contact me via email: after.fallout@gmail.com (english please :-), my french and german aren't very good yet). I live in Montana, USA (GMT -7) and most days I frequently check my email between 7am and 10pm.
+
+Patches/contributions are welcome, preferably (in no order):
+1. create a fork/queue on bitbucket
+2. email patch (attachments please, Thunderbird and Gmail both mess with whitespace)
+3. just ask for write access to the repository on bitbucket
+
+
+If you choose to email a patch, you can add the following to your hgrc for patchbomb:
+    [defaults]
+    email = -a
+    
+    [email]
+    from = your name <your@email>
+    to = after.fallout@gmail.com
+
+
+#Known issues
+
+None
+
+#Potential future work
+
+* fix bugs
+* regex/glob option to attic-display to filter out files from the list 
+* create commands to do basic work with the attic repo (commit, push, 
+  pull, addrem, init, ...)
+* override import to provide default path for patches from attic (so
+  you don't have to unshelve before finishing them or manually provide
+  the path to import)
+* if mq is in use:
+  * override qnew to move an attic patch into mq 
+  * provide a new command to pull patches out of mq and into attic
+    (perhaps just "hg shelve -q")    
+  * possibly allow shelving/unshelving entire queue series into 
+    folders in attic:
+    
+    "hg shelve -q foldername --all" would:
+    1. pop all patches in mq
+    2. create folder "foldername" in attic
+    3. move all patches and the series file into this folder
+    
+    "hg shelve -q foldername --applied"
+    1. split series into two parts: applied and unapplied
+    2. pop all patches in mq
+    3. create folder "foldername" in attic
+    4. move all patches in applied list into the attic and 
+       remove them from series
+    
+    "hg shelve -q foldername --unapplied"
+    (same thing with unapplied patches)
+    
+    "hg unshelve -q foldername"
+    1. "hg shelve -q defaultfolder --all"
+    2. move contents of foldername into the patches dir
+* provide new commands for dealing with patches like import/export (don't 
+  know if this is really necessary)
-@echo off
-rem runtests.bat - command line test runner
-rem
-rem Copyright 2009 Bill Barry <after.fallout@gmail.com>
-rem
-rem This software may be used and distributed according to the terms
-rem of the GNU General Public License, incorporated herein by reference.
-
-rem This program will run all the test files in a tests subdirectory.
-rem   A test is a batch script in the form "testSSSS.bat" with an output file
-rem   in the form "testSSSS.out"
-rem
-rem   A test succeeds if it generates the expected output file.
-rem   If a test fails, you can rerun with -v to get diff output.
-rem
-rem   diff is required to run this and must be available in your path
-setlocal
-pushd tests
-
-if "%1" == "-v" set verbose=1
-
-set /A numtests= 0
-set /A numfails= 0
-set /A numpass= 0
-
-for %%t in ("test*.bat") do call :run %%~nt
-
-echo %numtests% tests taken
-echo %numpass% tests passed
-if %numfails% NEQ 0 echo %numfails% tests failed
-
-goto :end
-
-:run
-set /A numtests += 1
-call %1.bat > testoutput 2>&1
-diff -u %1.out testoutput > diffresults.txt 
-for %%R in ("diffresults.txt") do call :checksize %%~zR %1 
-erase testoutput
-erase diffresults.txt
-goto :eof
-
-:checksize
-if "%1" == "0" call :testpassed %2 
-if "%1" NEQ "0" call :testfailed %2
-goto :eof
-
-:testfailed
-echo failed %1
-set /A numfails += 1
-if "%verbose%" == "1" echo diff output:
-if "%verbose%" == "1" type diffresults.txt
-goto :eof
-
-:testpassed
-echo passed %1
-set /A numpass += 1
-goto :eof
-
-:end
-popd
-endlocal
-
-:eof
-echo on
+@echo off
+rem runtests.bat - command line test runner
+rem
+rem Copyright 2009 Bill Barry <after.fallout@gmail.com>
+rem
+rem This software may be used and distributed according to the terms
+rem of the GNU General Public License, incorporated herein by reference.
+
+rem This program will run all the test files in a tests subdirectory.
+rem   A test is a batch script in the form "testSSSS.bat" with an output file
+rem   in the form "testSSSS.out"
+rem
+rem   A test succeeds if it generates the expected output file.
+rem   If a test fails, you can rerun with -v to get diff output.
+rem
+rem   diff is required to run this and must be available in your path
+setlocal
+pushd tests
+
+if "%1" == "-v" set verbose=1
+
+set /A numtests= 0
+set /A numfails= 0
+set /A numpass= 0
+
+for %%t in ("test*.bat") do call :run %%~nt
+
+echo %numtests% tests taken
+echo %numpass% tests passed
+if %numfails% NEQ 0 echo %numfails% tests failed
+
+goto :end
+
+:run
+set /A numtests += 1
+call %1.bat > testoutput 2>&1
+diff -u %1.out testoutput > diffresults.txt 
+for %%R in ("diffresults.txt") do call :checksize %%~zR %1 
+erase testoutput
+erase diffresults.txt
+goto :eof
+
+:checksize
+if "%1" == "0" call :testpassed %2 
+if "%1" NEQ "0" call :testfailed %2
+goto :eof
+
+:testfailed
+echo failed %1
+set /A numfails += 1
+if "%verbose%" == "1" echo diff output:
+if "%verbose%" == "1" type diffresults.txt
+goto :eof
+
+:testpassed
+echo passed %1
+set /A numpass += 1
+goto :eof
+
+:end
+popd
+endlocal
+
+:eof
+echo on

tests/ignore-manual_test-shelve-interactive.bat

-@echo off
-
-rem setup hgattic in a temp test directory
-setlocal
-cd ..
-cd > tmpfile
-set /p atticdir= < tmpfile
-erase tmpfile
-cd tests
-mkdir test
-cd test
-call hg init
-cd .hg
-echo [extensions] >tmp
-echo hgattic=%atticdir%\attic.py >>tmp
-move tmp hgrc
-cd ..
-
-rem replace here with test content
-echo ### echo a ^> a.txt
-echo a > a.txt
-echo ### echo b ^> b.txt
-echo b > b.txt
-echo ### call hg addrem
-call hg addrem
-call hg ci -m "commit"
-echo ### echo a ^>^> a.txt
-echo a >> a.txt
-echo ### echo b ^>^> b.txt
-echo b >> b.txt
-echo ### call hg st
-call hg st
-echo ### call hg shelve --interactive --git a
-call hg shelve --interactive --git a
-echo ### echo hgext.record= ^>^>.hg\hgrc
-echo hgext.record= >>.hg\hgrc
-echo ### call hg shelve --interactive --git a
-echo ### answer f then s
-call hg shelve --interactive --git a
-echo ### call hg st
-call hg st
-echo ### files in attic
-for %%f in (.hg\attic\*.*) do echo %%f
-echo ### call hg unshelve (should fail)
-call hg unshelve
-echo ### call hg shelve b
-call hg shelve b
-echo ### call hg unshelve a
-call hg unshelve a
-echo ### call hg st
-call hg st
-rem end test content
-
-rem cleanup
-cd ..
-rmdir /S /Q test
-endlocal
+@echo off
+
+rem setup hgattic in a temp test directory
+setlocal
+cd ..
+cd > tmpfile
+set /p atticdir= < tmpfile
+erase tmpfile
+cd tests
+mkdir test
+cd test
+call hg init
+cd .hg
+echo [extensions] >tmp
+echo hgattic=%atticdir%\attic.py >>tmp
+move tmp hgrc
+cd ..
+
+rem replace here with test content
+echo ### echo a ^> a.txt
+echo a > a.txt
+echo ### echo b ^> b.txt
+echo b > b.txt
+echo ### call hg addrem
+call hg addrem
+call hg ci -m "commit"
+echo ### echo a ^>^> a.txt
+echo a >> a.txt
+echo ### echo b ^>^> b.txt
+echo b >> b.txt
+echo ### call hg st
+call hg st
+echo ### call hg shelve --interactive --git a
+call hg shelve --interactive --git a
+echo ### echo hgext.record= ^>^>.hg\hgrc
+echo hgext.record= >>.hg\hgrc
+echo ### call hg shelve --interactive --git a
+echo ### answer f then s
+call hg shelve --interactive --git a
+echo ### call hg st
+call hg st
+echo ### files in attic
+for %%f in (.hg\attic\*.*) do echo %%f
+echo ### call hg unshelve (should fail)
+call hg unshelve
+echo ### call hg shelve b
+call hg shelve b
+echo ### call hg unshelve a
+call hg unshelve a
+echo ### call hg st
+call hg st
+rem end test content
+
+rem cleanup
+cd ..
+rmdir /S /Q test
+endlocal

tests/test-current.bat

-@echo off
-
-rem setup hgattic in a temp test directory
-setlocal
-cd ..
-cd > tmpfile
-set /p atticdir= < tmpfile
-erase tmpfile
-cd tests
-mkdir test
-cd test
-call hg init
-cd .hg
-echo [extensions] >tmp
-echo hgattic=%atticdir%\attic.py >>tmp
-move tmp hgrc
-cd ..
-
-rem replace here with test content
-echo ### call hg attic --current
-call hg attic --current
-echo ### echo a ^> a.txt
-echo a > a.txt
-echo ### call hg addrem
-call hg addrem
-echo ### call hg st
-call hg st
-echo ### call hg shelve --git -q a
-call hg shelve --git -q a
-echo ### call hg attic -c
-call hg attic -c
-echo ### call hg unshelve -q
-call hg unshelve -q
-echo ### call hg attic -c
-call hg attic -c
-echo ### call hg shelve -r -q -m "commit message"
-call hg shelve -r -q -m "commit message"
-echo ### call hg attic -c
-call hg attic -c
-echo ### call hg shelve -r -q -m "another message" -u "test user <asdf@asdf.com>"
-call hg shelve -r -q -m "another message" -u "test user <asdf@asdf.com>"
-echo ### call hg attic -c
-call hg attic -c
-echo ### call hg shelve -r -q -d "0 0"
-call hg shelve -r -q -d "0 0"
-echo ### call hg attic -c
-call hg attic -c
-echo ### call hg shelve
-call hg shelve
-echo ### echo b ^> b.txt
-echo b > b.txt
-echo ### call hg shelve -m "new message" b
-call hg shelve -m "new message" b
-echo ### call hg attic -i a
-call hg attic --header a
-echo ### call hg attic -i b
-call hg attic -d b
-rem end test content
-
-rem cleanup
-cd ..
-rmdir /S /Q test
-endlocal
+@echo off
+
+rem setup hgattic in a temp test directory
+setlocal
+cd ..
+cd > tmpfile
+set /p atticdir= < tmpfile
+erase tmpfile
+cd tests
+mkdir test
+cd test
+call hg init
+cd .hg
+echo [extensions] >tmp
+echo hgattic=%atticdir%\attic.py >>tmp
+move tmp hgrc
+cd ..
+
+rem replace here with test content
+echo ### call hg attic --current
+call hg attic --current
+echo ### echo a ^> a.txt
+echo a > a.txt
+echo ### call hg addrem
+call hg addrem
+echo ### call hg st
+call hg st
+echo ### call hg shelve --git -q a
+call hg shelve --git -q a
+echo ### call hg attic -c
+call hg attic -c
+echo ### call hg unshelve -q
+call hg unshelve -q
+echo ### call hg attic -c
+call hg attic -c
+echo ### call hg shelve -r -q -m "commit message"
+call hg shelve -r -q -m "commit message"
+echo ### call hg attic -c
+call hg attic -c
+echo ### call hg shelve -r -q -m "another message" -u "test user <asdf@asdf.com>"
+call hg shelve -r -q -m "another message" -u "test user <asdf@asdf.com>"
+echo ### call hg attic -c
+call hg attic -c
+echo ### call hg shelve -r -q -d "0 0"
+call hg shelve -r -q -d "0 0"
+echo ### call hg attic -c
+call hg attic -c
+echo ### call hg shelve
+call hg shelve
+echo ### echo b ^> b.txt
+echo b > b.txt
+echo ### call hg shelve -m "new message" b
+call hg shelve -m "new message" b
+echo ### call hg attic -i a
+call hg attic --header a
+echo ### call hg attic -i b
+call hg attic -d b
+rem end test content
+
+rem cleanup
+cd ..
+rmdir /S /Q test
+endlocal

tests/test-current.out

- ### call hg attic --current
+ ### call hg attic --current
 no patch active; default: default
-### echo a > a.txt
-### call hg addrem
+### echo a > a.txt
+### call hg addrem
 adding a.txt
-### call hg st
+### call hg st
 A a.txt
-### call hg shelve --git -q a
-### call hg attic -c
+### call hg shelve --git -q a
+### call hg attic -c
 no patch active; default: a
-### call hg unshelve -q
-### call hg attic -c
+### call hg unshelve -q
+### call hg attic -c
 active patch: a
 user: None
 date: None
 message: None
-### call hg shelve -r -q -m "commit message"
-### call hg attic -c
+### call hg shelve -r -q -m "commit message"
+### call hg attic -c
 active patch: a
 user: None
 date: None
 message: 
 commit message
-### call hg shelve -r -q -m "another message" -u "test user <asdf@asdf.com>"
-### call hg attic -c
+### call hg shelve -r -q -m "another message" -u "test user <asdf@asdf.com>"
+### call hg attic -c
 active patch: a
 user: test user <asdf@asdf.com>
 date: None
 message: 
 another message
-### call hg shelve -r -q -d "0 0"
-### call hg attic -c
+### call hg shelve -r -q -d "0 0"
+### call hg attic -c
 active patch: a
 user: test user <asdf@asdf.com>
 date: 0 0
 message: 
 another message
-### call hg shelve
+### call hg shelve
 Patch a shelved
-### echo b > b.txt
-### call hg shelve -m "new message" b
+### echo b > b.txt
+### call hg shelve -m "new message" b
 Patch b shelved
-### call hg attic -i a
+### call hg attic -i a
 user: test user <asdf@asdf.com>
 date: 0 0
 message: 
 another message
-### call hg attic -i b
+### call hg attic -i b
 user: None
 date: None
 message: 

tests/test-display.bat

-@echo off
-
-rem setup hgattic in a temp test directory
-setlocal
-cd ..
-cd > tmpfile
-set /p atticdir= < tmpfile
-erase tmpfile
-cd tests
-mkdir test
-cd test
-call hg init
-cd .hg
-echo [extensions] >tmp
-echo hgattic=%atticdir%\attic.py >>tmp
-move tmp hgrc
-cd ..
-
-rem replace here with test content
-echo a > a.txt
-call hg addrem
-call hg shelve -q a
-echo b > b.txt
-call hg addrem
-call hg shelve -q b
-echo ### hg attic
-call hg attic
-rem end test content
-
-rem cleanup
-cd ..
-rmdir /S /Q test
-endlocal
+@echo off
+
+rem setup hgattic in a temp test directory
+setlocal
+cd ..
+cd > tmpfile
+set /p atticdir= < tmpfile
+erase tmpfile
+cd tests
+mkdir test
+cd test
+call hg init
+cd .hg
+echo [extensions] >tmp
+echo hgattic=%atticdir%\attic.py >>tmp
+move tmp hgrc
+cd ..
+
+rem replace here with test content
+echo a > a.txt
+call hg addrem
+call hg shelve -q a
+echo b > b.txt
+call hg addrem
+call hg shelve -q b
+echo ### hg attic
+call hg attic
+rem end test content
+
+rem cleanup
+cd ..
+rmdir /S /Q test
+endlocal

tests/test-display.out

- adding a.txt
-adding b.txt
-### hg attic
-a
-b
+ adding a.txt
+adding b.txt
+### hg attic
+a
+b

tests/test-finish.bat

-@echo off
-
-rem setup hgattic in a temp test directory
-setlocal
-cd ..
-cd > tmpfile
-set /p atticdir= < tmpfile
-erase tmpfile
-cd tests
-mkdir test
-cd test
-call hg init
-cd .hg
-echo [extensions] >tmp
-echo hgattic=%atticdir%\attic.py >>tmp
-move tmp hgrc
-cd ..
-
-rem replace here with test content
-echo a > a.txt
-call hg addrem
-call hg st
-call hg shelve --git -q a
-call hg unshelve -q
-echo ### hg ci -m "commit message"
-call hg ci -m "commit message"
-echo ### hg log --template "{rev} {desc} {files}\n"
-call hg log --template "{rev} {desc} {files}\n"
-
-echo c >> a.txt
-call hg shelve --git -q -m "commit message 3" a
-call hg unshelve -q
-echo ### hg ci
-call hg ci
-echo ### hg log --template "{rev} {desc} {files}\n"
-call hg log --template "{rev} {desc} {files}\n"
-
-echo d >> a.txt
-call hg shelve --git -q -m "commit message 4" a
-call hg unshelve -q
-echo ### hg ci -m "commit message 5"
-call hg ci -m "commit message 5"
-echo ### hg log --template "{rev} {desc} {files}\n"
-call hg log --template "{rev} {desc} {files}\n"
-rem end test content
-
-rem cleanup
-cd ..
-rmdir /S /Q test
-endlocal
+@echo off
+
+rem setup hgattic in a temp test directory
+setlocal
+cd ..
+cd > tmpfile
+set /p atticdir= < tmpfile
+erase tmpfile
+cd tests
+mkdir test
+cd test
+call hg init
+cd .hg
+echo [extensions] >tmp
+echo hgattic=%atticdir%\attic.py >>tmp
+move tmp hgrc
+cd ..
+
+rem replace here with test content
+echo a > a.txt
+call hg addrem
+call hg st
+call hg shelve --git -q a
+call hg unshelve -q
+echo ### hg ci -m "commit message"
+call hg ci -m "commit message"
+echo ### hg log --template "{rev} {desc} {files}\n"
+call hg log --template "{rev} {desc} {files}\n"
+
+echo c >> a.txt
+call hg shelve --git -q -m "commit message 3" a
+call hg unshelve -q
+echo ### hg ci
+call hg ci
+echo ### hg log --template "{rev} {desc} {files}\n"
+call hg log --template "{rev} {desc} {files}\n"
+
+echo d >> a.txt
+call hg shelve --git -q -m "commit message 4" a
+call hg unshelve -q
+echo ### hg ci -m "commit message 5"
+call hg ci -m "commit message 5"
+echo ### hg log --template "{rev} {desc} {files}\n"
+call hg log --template "{rev} {desc} {files}\n"
+rem end test content
+
+rem cleanup
+cd ..
+rmdir /S /Q test
+endlocal

tests/test-finish.out

  adding a.txt
 A a.txt
-### hg ci -m "commit message"
-### hg log --template "{rev} {desc} {files}\n"
+### hg ci -m "commit message"
+### hg log --template "{rev} {desc} {files}\n"
 0 commit message a.txt
-### hg ci
-### hg log --template "{rev} {desc} {files}\n"
+### hg ci
+### hg log --template "{rev} {desc} {files}\n"
 1 commit message 3 a.txt
 0 commit message a.txt
-### hg ci -m "commit message 5"
-### hg log --template "{rev} {desc} {files}\n"
+### hg ci -m "commit message 5"
+### hg log --template "{rev} {desc} {files}\n"
 2 commit message 5 a.txt
 1 commit message 3 a.txt
 0 commit message a.txt

tests/test-hgattic.bat

-@echo off
-
-rem setup hgattic in a temp test directory
-setlocal
-cd ..
-cd > tmpfile
-set /p atticdir= < tmpfile
-erase tmpfile
-cd tests
-mkdir test
-cd test
-call hg init
-cd .hg
-echo [extensions] >tmp
-echo hgattic=%atticdir%\attic.py >>tmp
-move tmp hgrc
-cd ..
-
-rem replace here with test content
-call hg help hgattic
-rem end test content
-
-rem cleanup
-cd ..
-rmdir /S /Q test
-endlocal
+@echo off
+
+rem setup hgattic in a temp test directory
+setlocal
+cd ..
+cd > tmpfile
+set /p atticdir= < tmpfile
+erase tmpfile
+cd tests
+mkdir test
+cd test
+call hg init
+cd .hg
+echo [extensions] >tmp
+echo hgattic=%atticdir%\attic.py >>tmp
+move tmp hgrc
+cd ..
+
+rem replace here with test content
+call hg help hgattic
+rem end test content
+
+rem cleanup
+cd ..
+rmdir /S /Q test
+endlocal

tests/test-rebuild.bat

-@echo off
-
-rem setup hgattic in a temp test directory
-setlocal
-cd ..
-cd > tmpfile
-set /p atticdir= < tmpfile
-erase tmpfile
-cd tests
-mkdir test
-cd test
-call hg init
-cd .hg
-echo [extensions] >tmp
-echo hgattic=%atticdir%\attic.py >>tmp
-move tmp hgrc
-cd ..
-
-rem replace here with test content
-echo ### echo a ^> a.txt
-echo a > a.txt
-echo ### call hg addrem
-call hg addrem
-echo ### call hg st
-call hg st
-echo ### call hg shelve --git -q a
-call hg shelve --git -q a
-echo ### call hg unshelve -q
-call hg unshelve -q
-echo ### type .hg\attic\a
-type .hg\attic\a
-echo ### echo b ^>^> a.txt
-echo b >> a.txt
-echo ### call hg shelve --refresh
-call hg shelve --refresh
-echo ### type .hg\attic\a
-type .hg\attic\a
-echo ### call hg shelve -r -m test
-call hg shelve -r -m test
-echo ### type .hg\attic\a
-type .hg\attic\a
-echo ### call hg shelve -r b
-call hg shelve -r b
-echo ### call hg shelve -r -f b
-call hg shelve -r -f b
-rem end test content
-
-rem cleanup
-cd ..
-rmdir /S /Q test
-endlocal
+@echo off
+
+rem setup hgattic in a temp test directory
+setlocal
+cd ..
+cd > tmpfile
+set /p atticdir= < tmpfile
+erase tmpfile
+cd tests
+mkdir test
+cd test
+call hg init
+cd .hg
+echo [extensions] >tmp
+echo hgattic=%atticdir%\attic.py >>tmp
+move tmp hgrc
+cd ..
+
+rem replace here with test content
+echo ### echo a ^> a.txt
+echo a > a.txt
+echo ### call hg addrem
+call hg addrem
+echo ### call hg st
+call hg st
+echo ### call hg shelve --git -q a
+call hg shelve --git -q a
+echo ### call hg unshelve -q
+call hg unshelve -q
+echo ### type .hg\attic\a
+type .hg\attic\a
+echo ### echo b ^>^> a.txt
+echo b >> a.txt
+echo ### call hg shelve --refresh
+call hg shelve --refresh
+echo ### type .hg\attic\a
+type .hg\attic\a
+echo ### call hg shelve -r -m test
+call hg shelve -r -m test
+echo ### type .hg\attic\a
+type .hg\attic\a
+echo ### call hg shelve -r b
+call hg shelve -r b
+echo ### call hg shelve -r -f b
+call hg shelve -r -f b
+rem end test content
+
+rem cleanup
+cd ..
+rmdir /S /Q test
+endlocal

tests/test-rebuild.out

- ### echo a > a.txt
-### call hg addrem
+ ### echo a > a.txt
+### call hg addrem
 adding a.txt
-### call hg st
+### call hg st
 A a.txt
-### call hg shelve --git -q a
-### call hg unshelve -q
-### type .hg\attic\a