[PATCH 6 of 6] record: use new mempatching system for simplicity and safety

Augie Fackler durin42 at gmail.com
Mon Apr 19 18:16:39 CDT 2010


# HG changeset patch
# User Augie Fackler <durin42 at gmail.com>
# Date 1271716506 18000
# Node ID 6566509b770ecf361379d618da0807cb852d0290
# Parent  9b8a41043ab3634a237292cf6de53dd967f2ca2f
record: use new mempatching system for simplicity and safety

diff --git a/hgext/record.py b/hgext/record.py
--- a/hgext/record.py
+++ b/hgext/record.py
@@ -9,7 +9,7 @@
 
 from mercurial.i18n import gettext, _
 from mercurial import cmdutil, commands, extensions, hg, mdiff, patch
-from mercurial import util
+from mercurial import util, context, lock, node
 import copy, cStringIO, errno, operator, os, re, tempfile
 
 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
@@ -382,30 +382,59 @@
 
       ? - display help'''
 
-    dorecord(ui, repo, commands.commit, *pats, **opts)
+    dorecord(ui, repo, *pats, **opts)
 
 
-def qrecord(ui, repo, patch, *pats, **opts):
+# TODO this has some commonality with hgext mq.queue.qimport - refactor
+# The big issue is that qimport enforces the imported rev must be qparent
+# if patches are already applied, and we're always importing the child
+# of qtip.
+def qrecord(ui, repo, patchname, *pats, **opts):
     '''interactively record a new patch
 
     See 'hg help qnew' & 'hg help record' for more information and
     usage.
     '''
-
     try:
         mq = extensions.find('mq')
     except KeyError:
         raise util.Abort(_("'mq' extension not loaded"))
 
-    def committomq(ui, repo, *pats, **opts):
-        mq.new(ui, repo, patch, *pats, **opts)
+    opts = opts.copy()
+    repo.mq.check_toppatch(repo)
+    if len(repo[None].parents()) > 1:
+        raise util.Abort(_('cannot manage merge changesets'))
+    r = repo['tip']
+    if not patchname:
+        patchname = mq.normname('%d.diff' % r.rev()+1)
 
-    opts = opts.copy()
-    opts['force'] = True    # always 'qnew -f'
-    dorecord(ui, repo, committomq, *pats, **opts)
+    repo.mq.check_reserved_name(patchname)
+    if patchname in repo.mq.series:
+        raise util.Abort(_('patch %s already in the series file') % patchname)
+    if os.path.exists(repo.mq.join(patchname)):
+        raise util.Abort(_('patch "%s" alread exists') % patchname)
 
+    dorecord(ui, repo, *pats, **opts)
 
-def dorecord(ui, repo, commitfunc, *pats, **opts):
+    r = repo['tip']
+    diffopts = repo.mq.diffopts({'git': opts['git']})
+    repo.mq.full_series.insert(repo.mq.full_series_end(), patchname)
+    patchf = repo.mq.opener(patchname, 'w')
+    cmdutil.export(repo, [r.node()], fp=patchf, opts=diffopts)
+    patchf.close()
+    se = mq.statusentry(r.node(), patchname)
+    repo.mq.applied.append(se)
+    repo.mq.parse_series()
+    repo.mq.applied_dirty = 1
+    repo.mq.series_dirty = 1
+    repo.mq.save_dirty()
+    qrepo = repo.mq.qrepo()
+    if qrepo:
+        qrepo.add(patchname)
+    repo.invalidatecaches()
+
+
+def dorecord(ui, repo, *pats, **opts):
     if not ui.interactive():
         raise util.Abort(_('running non-interactively, use commit instead'))
 
@@ -449,77 +478,46 @@
 
         modified = set(changes[0])
 
-        # 2. backup changed files, so we can restore them in the end
-        backups = {}
-        backupdir = repo.join('record-backups')
+        fp = cStringIO.StringIO()
+        for c in chunks:
+            c.write(fp)
+        dopatch = fp.tell()
+        fp.seek(0)
+
+        if dopatch:
+            try:
+                ui.debug('applying patch\n')
+                ui.debug(fp.getvalue())
+                patcher = patch.memapplydiff(
+                    ui, fp, repo[None].parents()[0], 1, eolmode=None)
+            except patch.PatchError, err:
+                s = str(err)
+                if s:
+                    raise util.Abort(s)
+                else:
+                    raise util.Abort(_('patch failed to apply'))
+        del fp
+        message = cmdutil.logmessage(opts)
+        if not message.strip():
+            raise util.Abort(_('empty commit message'))
+        ctx = context.memctx(repo, repo.dirstate.parents(),
+                             message,
+                             patcher.files.keys(),
+                             patcher.filectxfn,
+                             opts.get('user'), opts.get('date'),
+                             repo[None].extra())
+        l = repo.wlock()
         try:
-            os.mkdir(backupdir)
-        except OSError, err:
-            if err.errno != errno.EEXIST:
-                raise
-        try:
-            # backup continues
-            for f in newfiles:
-                if f not in modified:
-                    continue
-                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)
-                backups[f] = tmpname
-
-            fp = cStringIO.StringIO()
-            for c in chunks:
-                if c.filename() in backups:
-                    c.write(fp)
-            dopatch = fp.tell()
-            fp.seek(0)
-
-            # 3a. apply filtered patch to clean repo  (clean)
-            if backups:
-                hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
-
-            # 3b. (apply)
-            if dopatch:
-                try:
-                    ui.debug('applying patch\n')
-                    ui.debug(fp.getvalue())
-                    pfiles = {}
-                    patch.internalpatch(fp, ui, 1, repo.root, files=pfiles,
-                                        eolmode=None)
-                    patch.updatedir(ui, repo, pfiles)
-                except patch.PatchError, err:
-                    s = str(err)
-                    if s:
-                        raise util.Abort(s)
-                    else:
-                        raise util.Abort(_('patch failed to apply'))
-            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!
-
-            # it is important to first chdir to repo root -- we'll call a
-            # highlevel command with list of pathnames relative to repo root
-            cwd = os.getcwd()
-            os.chdir(repo.root)
-            try:
-                commitfunc(ui, repo, *newfiles, **opts)
-            finally:
-                os.chdir(cwd)
-
-            return 0
+            n = repo.commitctx(ctx)
+            repo.dirstate.setparents(n)
+            for f, action in patcher.files.iteritems():
+                if action is None:
+                    repo.dirstate.forget(f)
+                else:
+                    repo.dirstate.normallookup(f)
         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))
-                    os.unlink(tmpname)
-                os.rmdir(backupdir)
-            except OSError:
-                pass
+            l.release()
+        return 0
 
     # wrap ui.write so diff output can be labeled/colorized
     def wrapwrite(orig, *args, **kw):
@@ -561,4 +559,3 @@
     }
 
     cmdtable.update(qcmdtable)
-


More information about the Mercurial-devel mailing list