[PATCH 1 of 2] Add import --exact

Brendan Cully brendan at kublai.com
Wed Mar 21 16:11:33 CDT 2007


# HG changeset patch
# User Brendan Cully <brendan at kublai.com>
# Date 1174509892 25200
# Node ID f8cd0c9858859b636f121f8e23d2a5f47b79212d
# Parent  8269cd88c4725baaf519d356150e67770dc19f8c
Add import --exact.
When this option is set, import will apply the patch (which must
be generated by export) to the parents specified in the patch,
and check that the node produced by the patch matches the node
ID in the patch.

diff -r 8269cd88c472 -r f8cd0c985885 mercurial/commands.py
--- a/mercurial/commands.py	Wed Mar 21 12:15:28 2007 -0700
+++ b/mercurial/commands.py	Wed Mar 21 13:44:52 2007 -0700
@@ -1477,15 +1477,20 @@ def import_(ui, repo, patch1, *patches, 
     text/plain body parts before first diff are added to commit
     message.
 
-    If imported patch was generated by hg export, user and description
+    If the imported patch was generated by hg export, user and description
     from patch override values from message headers and body.  Values
     given on command line with -m and -u override these.
 
+    If --exact is specified, import will set the working directory
+    to the parent of each patch before applying it, and will abort
+    if the resulting changeset has a different ID than the one
+    recorded in the patch.
+
     To read a patch from standard input, use patch name "-".
     """
     patches = (patch1,) + patches
 
-    if not opts['force']:
+    if opts.get('exact') or not opts['force']:
         bail_if_changed(repo)
 
     d = opts["base"]
@@ -1499,10 +1504,10 @@ def import_(ui, repo, patch1, *patches, 
 
         if pf == '-':
             ui.status(_("applying patch from stdin\n"))
-            tmpname, message, user, date = patch.extract(ui, sys.stdin)
+            tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
         else:
             ui.status(_("applying %s\n") % p)
-            tmpname, message, user, date = patch.extract(ui, file(pf))
+            tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, file(pf))
 
         if tmpname is None:
             raise util.Abort(_('no diffs found'))
@@ -1521,12 +1526,27 @@ def import_(ui, repo, patch1, *patches, 
             ui.debug(_('message:\n%s\n') % message)
 
             files = {}
+            if opts.get('exact'):
+                if not nodeid or not p1:
+                    raise util.Abort(_('not a mercurial patch'))
+                p1 = repo.lookup(p1)
+                p2 = repo.lookup(p2 or hex(nullid))
+
+                wctx = repo.workingctx()
+                wp = repo.workingctx().parents()
+                if p1 != wp[0].node():
+                    hg.clean(repo, p1, wlock=wlock)
+                repo.dirstate.setparents(p1, p2)
             try:
                 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
                                    files=files)
             finally:
                 files = patch.updatedir(ui, repo, files, wlock=wlock)
-            repo.commit(files, message, user, date, wlock=wlock, lock=lock)
+            n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
+            if opts.get('exact'):
+                if hex(n) != nodeid:
+                    repo.rollback()
+                    raise util.Abort(_('patch is damaged or loses information'))
         finally:
             os.unlink(tmpname)
 
@@ -2772,7 +2792,9 @@ table = {
              'meaning as the corresponding patch option')),
           ('b', 'base', '', _('base path')),
           ('f', 'force', None,
-           _('skip check for outstanding uncommitted changes'))] + commitopts,
+           _('skip check for outstanding uncommitted changes')),
+          ('', 'exact', None,
+           _('apply patch to the nodes from which it was generated'))] + commitopts,
          _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
     "incoming|in": (incoming,
          [('M', 'no-merges', None, _('do not show merges')),
diff -r 8269cd88c472 -r f8cd0c985885 mercurial/patch.py
--- a/mercurial/patch.py	Wed Mar 21 12:15:28 2007 -0700
+++ b/mercurial/patch.py	Wed Mar 21 13:44:52 2007 -0700
@@ -33,11 +33,11 @@ def extract(ui, fileobj):
 def extract(ui, fileobj):
     '''extract patch from data read from fileobj.
 
-    patch can be normal patch or contained in email message.
-
-    return tuple (filename, message, user, date). any item in returned
-    tuple can be None.  if filename is None, fileobj did not contain
-    patch. caller must unlink filename when done.'''
+    patch can be a normal patch or contained in an email message.
+
+    return tuple (filename, message, user, date, node, p1, p2).
+    Any item in the returned tuple can be None. If filename is None,
+    fileobj did not contain a patch. Caller must unlink filename when done.'''
 
     # attempt to detect the start of a patch
     # (this heuristic is borrowed from quilt)
@@ -54,6 +54,8 @@ def extract(ui, fileobj):
         user = msg['From']
         # should try to parse msg['Date']
         date = None
+        nodeid = None
+        parents = []
 
         if message:
             if message.startswith('[PATCH'):
@@ -97,6 +99,10 @@ def extract(ui, fileobj):
                             ui.debug('From: %s\n' % user)
                         elif line.startswith("# Date "):
                             date = line[7:]
+                        elif line.startswith("# Node ID "):
+                            nodeid = line[10:]
+                        elif line.startswith("# Parent "):
+                            parents.append(line[10:])
                     elif line == '---' and 'git-send-email' in msg['X-Mailer']:
                         ignoretext = True
                     if not line.startswith('# ') and not ignoretext:
@@ -117,8 +123,10 @@ def extract(ui, fileobj):
     tmpfp.close()
     if not diffs_seen:
         os.unlink(tmpname)
-        return None, message, user, date
-    return tmpname, message, user, date
+        return None, message, user, date, None, None, None
+    p1 = parents and parents.pop(0) or None
+    p2 = parents and parents.pop(0) or None
+    return tmpname, message, user, date, nodeid, p1, p2
 
 GP_PATCH  = 1 << 0  # we have to run patch
 GP_FILTER = 1 << 1  # there's some copy/rename operation


More information about the Mercurial-devel mailing list