diff mercurial/commands.py @ 4258:47ba52121433

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.
author Brendan Cully <brendan@kublai.com>
date Thu, 22 Mar 2007 10:44:59 -0700
parents fe0fe0b4d73b
children bda63383d529
line wrap: on
line diff
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1477,15 +1477,21 @@ 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. This may happen due to character set
+    problems or other deficiencies in the text patch format.
+
     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 +1505,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 +1527,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 +2793,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')),