record: move dorecord from record to cmdutil
authorLaurent Charignon <lcharignon@fb.com>
Tue, 10 Mar 2015 17:14:33 -0700
changeset 24272 26a1c617e047
parent 24271 18792f2e38bb
child 24273 ce847603040b
record: move dorecord from record to cmdutil Part of a serie of patches to move record from hgext to core
hgext/keyword.py
hgext/record.py
mercurial/cmdutil.py
--- a/hgext/keyword.py	Tue Mar 10 17:09:07 2015 -0700
+++ b/hgext/keyword.py	Tue Mar 10 17:14:33 2015 -0700
@@ -737,13 +737,7 @@
     extensions.wrapfunction(patch, 'diff', kw_diff)
     extensions.wrapfunction(cmdutil, 'amend', kw_amend)
     extensions.wrapfunction(cmdutil, 'copy', kw_copy)
+    extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord)
     for c in 'annotate changeset rev filediff diff'.split():
         extensions.wrapfunction(webcommands, c, kwweb_skip)
-    for name in recordextensions.split():
-        try:
-            record = extensions.find(name)
-            extensions.wrapfunction(record, 'dorecord', kw_dorecord)
-        except KeyError:
-            pass
-
     repo.__class__ = kwrepo
--- a/hgext/record.py	Tue Mar 10 17:09:07 2015 -0700
+++ b/hgext/record.py	Tue Mar 10 17:14:33 2015 -0700
@@ -8,10 +8,8 @@
 '''commands to interactively select changes for commit/qrefresh'''
 
 from mercurial.i18n import _
-from mercurial import cmdutil, commands, extensions, patch
+from mercurial import cmdutil, commands, extensions
 from mercurial import util
-from mercurial import merge as mergemod
-import cStringIO, errno, os, shutil, tempfile
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -50,7 +48,7 @@
 
     This command is not available when committing a merge.'''
 
-    dorecord(ui, repo, commands.commit, 'commit', False, *pats, **opts)
+    cmdutil.dorecord(ui, repo, commands.commit, 'commit', False, *pats, **opts)
 
 def qrefresh(origfn, ui, repo, *pats, **opts):
     if not opts['interactive']:
@@ -66,7 +64,7 @@
         mq.refresh(ui, repo, **opts)
 
     # backup all changed files
-    dorecord(ui, repo, committomq, 'qrefresh', True, *pats, **opts)
+    cmdutil.dorecord(ui, repo, committomq, 'qrefresh', True, *pats, **opts)
 
 # This command registration is replaced during uisetup().
 @command('qrecord',
@@ -91,180 +89,13 @@
         opts['checkname'] = False
         mq.new(ui, repo, patch, *pats, **opts)
 
-    dorecord(ui, repo, committomq, 'qnew', False, *pats, **opts)
+    cmdutil.dorecord(ui, repo, committomq, 'qnew', False, *pats, **opts)
 
 def qnew(origfn, ui, repo, patch, *args, **opts):
     if opts['interactive']:
         return qrecord(ui, repo, patch, *args, **opts)
     return origfn(ui, repo, patch, *args, **opts)
 
-def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, *pats, **opts):
-    if not ui.interactive():
-        raise util.Abort(_('running non-interactively, use %s instead') %
-                         cmdsuggest)
-
-    # make sure username is set before going interactive
-    if not opts.get('user'):
-        ui.username() # raise exception, username not provided
-
-    def recordfunc(ui, repo, message, match, opts):
-        """This is generic record driver.
-
-        Its job is to interactively filter local changes, and
-        accordingly prepare working directory into a state in which the
-        job can be delegated to a non-interactive commit command such as
-        'commit' or 'qrefresh'.
-
-        After the actual job is done by non-interactive command, the
-        working directory is restored to its original state.
-
-        In the end we'll record interesting changes, and everything else
-        will be left in place, so the user can continue working.
-        """
-
-        cmdutil.checkunfinished(repo, commit=True)
-        merge = len(repo[None].parents()) > 1
-        if merge:
-            raise util.Abort(_('cannot partially commit a merge '
-                               '(use "hg commit" instead)'))
-
-        status = repo.status(match=match)
-        diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
-        diffopts.nodates = True
-        diffopts.git = True
-        originalchunks = patch.diff(repo, changes=status, opts=diffopts)
-        fp = cStringIO.StringIO()
-        fp.write(''.join(originalchunks))
-        fp.seek(0)
-
-        # 1. filter patch, so we have intending-to apply subset of it
-        try:
-            chunks = patch.filterpatch(ui, patch.parsepatch(fp))
-        except patch.PatchError, err:
-            raise util.Abort(_('error parsing patch: %s') % err)
-
-        del fp
-
-        contenders = set()
-        for h in chunks:
-            try:
-                contenders.update(set(h.files()))
-            except AttributeError:
-                pass
-
-        changed = status.modified + status.added + status.removed
-        newfiles = [f for f in changed if f in contenders]
-        if not newfiles:
-            ui.status(_('no changes to record\n'))
-            return 0
-
-        newandmodifiedfiles = set()
-        for h in chunks:
-            ishunk = isinstance(h, patch.recordhunk)
-            isnew = h.filename() in status.added
-            if ishunk and isnew and not h in originalchunks:
-                newandmodifiedfiles.add(h.filename())
-
-        modified = set(status.modified)
-
-        # 2. backup changed files, so we can restore them in the end
-
-        if backupall:
-            tobackup = changed
-        else:
-            tobackup = [f for f in newfiles
-                        if f in modified or f in newandmodifiedfiles]
-
-        backups = {}
-        if tobackup:
-            backupdir = repo.join('record-backups')
-            try:
-                os.mkdir(backupdir)
-            except OSError, err:
-                if err.errno != errno.EEXIST:
-                    raise
-        try:
-            # backup continues
-            for f in tobackup:
-                fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
-                                               dir=backupdir)
-                os.close(fd)
-                ui.debug('backup %r as %r\n' % (f, tmpname))
-                util.copyfile(repo.wjoin(f), tmpname)
-                shutil.copystat(repo.wjoin(f), tmpname)
-                backups[f] = tmpname
-
-            fp = cStringIO.StringIO()
-            for c in chunks:
-                fname = c.filename()
-                if fname in backups or fname in newandmodifiedfiles:
-                    c.write(fp)
-            dopatch = fp.tell()
-            fp.seek(0)
-
-            [os.unlink(c) for c in newandmodifiedfiles]
-
-            # 3a. apply filtered patch to clean repo  (clean)
-            if backups:
-                # Equivalent to hg.revert
-                choices = lambda key: key in backups
-                mergemod.update(repo, repo.dirstate.p1(),
-                        False, True, choices)
-
-            # 3b. (apply)
-            if dopatch:
-                try:
-                    ui.debug('applying patch\n')
-                    ui.debug(fp.getvalue())
-                    patch.internalpatch(ui, repo, fp, 1, eolmode=None)
-                except patch.PatchError, err:
-                    raise util.Abort(str(err))
-            del fp
-
-            # 4. We prepared working directory according to filtered
-            #    patch. Now is the time to delegate the job to
-            #    commit/qrefresh or the like!
-
-            # Make all of the pathnames absolute.
-            newfiles = [repo.wjoin(nf) for nf in newfiles]
-            commitfunc(ui, repo, *newfiles, **opts)
-
-            return 0
-        finally:
-            # 5. finally restore backed-up files
-            try:
-                for realname, tmpname in backups.iteritems():
-                    ui.debug('restoring %r to %r\n' % (tmpname, realname))
-                    util.copyfile(tmpname, repo.wjoin(realname))
-                    # Our calls to copystat() here and above are a
-                    # hack to trick any editors that have f open that
-                    # we haven't modified them.
-                    #
-                    # Also note that this racy as an editor could
-                    # notice the file's mtime before we've finished
-                    # writing it.
-                    shutil.copystat(tmpname, repo.wjoin(realname))
-                    os.unlink(tmpname)
-                if tobackup:
-                    os.rmdir(backupdir)
-            except OSError:
-                pass
-
-    # wrap ui.write so diff output can be labeled/colorized
-    def wrapwrite(orig, *args, **kw):
-        label = kw.pop('label', '')
-        for chunk, l in patch.difflabel(lambda: args):
-            orig(chunk, label=label + l)
-    oldwrite = ui.write
-
-    def wrap(*args, **kwargs):
-        return wrapwrite(oldwrite, *args, **kwargs)
-    setattr(ui, 'write', wrap)
-
-    try:
-        return cmdutil.commit(ui, repo, recordfunc, pats, opts)
-    finally:
-        ui.write = oldwrite
 
 def uisetup(ui):
     try:
--- a/mercurial/cmdutil.py	Tue Mar 10 17:09:07 2015 -0700
+++ b/mercurial/cmdutil.py	Tue Mar 10 17:14:33 2015 -0700
@@ -7,7 +7,7 @@
 
 from node import hex, nullid, nullrev, short
 from i18n import _
-import os, sys, errno, re, tempfile
+import os, sys, errno, re, tempfile, cStringIO, shutil
 import util, scmutil, templater, patch, error, templatekw, revlog, copies
 import match as matchmod
 import context, repair, graphmod, revset, phases, obsolete, pathutil
@@ -19,6 +19,177 @@
 def parsealiases(cmd):
     return cmd.lstrip("^").split("|")
 
+def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, *pats, **opts):
+    import merge as mergemod
+    if not ui.interactive():
+        raise util.Abort(_('running non-interactively, use %s instead') %
+                         cmdsuggest)
+
+    # make sure username is set before going interactive
+    if not opts.get('user'):
+        ui.username() # raise exception, username not provided
+
+    def recordfunc(ui, repo, message, match, opts):
+        """This is generic record driver.
+
+        Its job is to interactively filter local changes, and
+        accordingly prepare working directory into a state in which the
+        job can be delegated to a non-interactive commit command such as
+        'commit' or 'qrefresh'.
+
+        After the actual job is done by non-interactive command, the
+        working directory is restored to its original state.
+
+        In the end we'll record interesting changes, and everything else
+        will be left in place, so the user can continue working.
+        """
+
+        checkunfinished(repo, commit=True)
+        merge = len(repo[None].parents()) > 1
+        if merge:
+            raise util.Abort(_('cannot partially commit a merge '
+                               '(use "hg commit" instead)'))
+
+        status = repo.status(match=match)
+        diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
+        diffopts.nodates = True
+        diffopts.git = True
+        originalchunks = patch.diff(repo, changes=status, opts=diffopts)
+        fp = cStringIO.StringIO()
+        fp.write(''.join(originalchunks))
+        fp.seek(0)
+
+        # 1. filter patch, so we have intending-to apply subset of it
+        try:
+            chunks = patch.filterpatch(ui, patch.parsepatch(fp))
+        except patch.PatchError, err:
+            raise util.Abort(_('error parsing patch: %s') % err)
+
+        del fp
+
+        contenders = set()
+        for h in chunks:
+            try:
+                contenders.update(set(h.files()))
+            except AttributeError:
+                pass
+
+        changed = status.modified + status.added + status.removed
+        newfiles = [f for f in changed if f in contenders]
+        if not newfiles:
+            ui.status(_('no changes to record\n'))
+            return 0
+
+        newandmodifiedfiles = set()
+        for h in chunks:
+            ishunk = isinstance(h, patch.recordhunk)
+            isnew = h.filename() in status.added
+            if ishunk and isnew and not h in originalchunks:
+                newandmodifiedfiles.add(h.filename())
+
+        modified = set(status.modified)
+
+        # 2. backup changed files, so we can restore them in the end
+
+        if backupall:
+            tobackup = changed
+        else:
+            tobackup = [f for f in newfiles
+                        if f in modified or f in newandmodifiedfiles]
+
+        backups = {}
+        if tobackup:
+            backupdir = repo.join('record-backups')
+            try:
+                os.mkdir(backupdir)
+            except OSError, err:
+                if err.errno != errno.EEXIST:
+                    raise
+        try:
+            # backup continues
+            for f in tobackup:
+                fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
+                                               dir=backupdir)
+                os.close(fd)
+                ui.debug('backup %r as %r\n' % (f, tmpname))
+                util.copyfile(repo.wjoin(f), tmpname)
+                shutil.copystat(repo.wjoin(f), tmpname)
+                backups[f] = tmpname
+
+            fp = cStringIO.StringIO()
+            for c in chunks:
+                fname = c.filename()
+                if fname in backups or fname in newandmodifiedfiles:
+                    c.write(fp)
+            dopatch = fp.tell()
+            fp.seek(0)
+
+            [os.unlink(c) for c in newandmodifiedfiles]
+
+            # 3a. apply filtered patch to clean repo  (clean)
+            if backups:
+                # Equivalent to hg.revert
+                choices = lambda key: key in backups
+                mergemod.update(repo, repo.dirstate.p1(),
+                        False, True, choices)
+
+
+            # 3b. (apply)
+            if dopatch:
+                try:
+                    ui.debug('applying patch\n')
+                    ui.debug(fp.getvalue())
+                    patch.internalpatch(ui, repo, fp, 1, eolmode=None)
+                except patch.PatchError, err:
+                    raise util.Abort(str(err))
+            del fp
+
+            # 4. We prepared working directory according to filtered
+            #    patch. Now is the time to delegate the job to
+            #    commit/qrefresh or the like!
+
+            # Make all of the pathnames absolute.
+            newfiles = [repo.wjoin(nf) for nf in newfiles]
+            commitfunc(ui, repo, *newfiles, **opts)
+
+            return 0
+        finally:
+            # 5. finally restore backed-up files
+            try:
+                for realname, tmpname in backups.iteritems():
+                    ui.debug('restoring %r to %r\n' % (tmpname, realname))
+                    util.copyfile(tmpname, repo.wjoin(realname))
+                    # Our calls to copystat() here and above are a
+                    # hack to trick any editors that have f open that
+                    # we haven't modified them.
+                    #
+                    # Also note that this racy as an editor could
+                    # notice the file's mtime before we've finished
+                    # writing it.
+                    shutil.copystat(tmpname, repo.wjoin(realname))
+                    os.unlink(tmpname)
+                if tobackup:
+                    os.rmdir(backupdir)
+            except OSError:
+                pass
+
+    # wrap ui.write so diff output can be labeled/colorized
+    def wrapwrite(orig, *args, **kw):
+        label = kw.pop('label', '')
+        for chunk, l in patch.difflabel(lambda: args):
+            orig(chunk, label=label + l)
+
+    oldwrite = ui.write
+    def wrap(*args, **kwargs):
+        return wrapwrite(oldwrite, *args, **kwargs)
+    setattr(ui, 'write', wrap)
+
+    try:
+        return commit(ui, repo, recordfunc, pats, opts)
+    finally:
+        ui.write = oldwrite
+
+
 def findpossible(cmd, table, strict=False):
     """
     Return cmd -> (aliases, command table entry)