diff hgext/evolve.py @ 1058:00bc31523074

uncommit: add a --rev argument The uncommit command now taks a --rev argument. This lets restore the file content in the current commit to another revision (instead of just is parent content). This still does not touch the working directory content.\ This allow the longly awaited: hg uncommit --hidden --rev 'precursors(.)'
author Pierre-Yves David <pierre-yves.david@fb.com>
date Fri, 15 Aug 2014 00:46:23 -0700
parents cfe1b9d3e5d9
children 742c1ce79a96
line wrap: on
line diff
--- a/hgext/evolve.py	Thu Aug 14 15:26:55 2014 -0700
+++ b/hgext/evolve.py	Fri Aug 15 00:46:23 2014 -0700
@@ -1859,25 +1859,45 @@
     _alias, commitcmd = cmdutil.findcmd('commit', commands.table)
     return commitcmd[0](ui, repo, *pats, **opts)
 
-def _commitfiltered(repo, ctx, match):
+
+def _touchedbetween(repo, source, dest, match=None):
+    touched = set()
+    for files in repo.status(source, dest, match=match)[:3]:
+        touched.update(files)
+    return touched
+
+def _commitfiltered(repo, ctx, match, target=None):
     """Recommit ctx with changed files not in match. Return the new
     node identifier, or None if nothing changed.
     """
     base = ctx.p1()
-    m, a, r = repo.status(base, ctx)[:3]
-    allfiles = set(m + a + r)
-    files = set(f for f in allfiles if not match(f))
-    if files == allfiles:
+    if target is None:
+        target = base
+    # ctx
+    initialfiles = _touchedbetween(repo, base, ctx)
+    if base == target:
+        affected = set(f for f in initialfiles if match(f))
+        newcontent = set()
+    else:
+        affected = _touchedbetween(repo, target, ctx, match=match)
+        newcontent = _touchedbetween(repo, target, base, match=match)
+    # The commit touchs all existing files
+    # + all file that needs a new content
+    # - the file affected bny uncommit with the same content than base.
+    files = (initialfiles - affected) | newcontent
+    if not newcontent and files == initialfiles:
         return None
 
     # Filter copies
-    copied = copies.pathcopies(base, ctx)
+    copied = copies.pathcopies(target, ctx)
     copied = dict((src, dst) for src, dst in copied.iteritems()
                   if dst in files)
-    def filectxfn(repo, memctx, path):
-        if path not in ctx:
+    def filectxfn(repo, memctx, path, contentctx=ctx, redirect=newcontent):
+        if path in redirect:
+            return filectxfn(repo, memctx, path, contentctx=target, redirect=())
+        if path not in contentctx:
             raise IOError()
-        fctx = ctx[path]
+        fctx = contentctx[path]
         flags = fctx.flags()
         mctx = memfilectx(repo, fctx.path(), fctx.data(),
                           islink='l' in flags,
@@ -1943,6 +1963,7 @@
 
 @command('^uncommit',
     [('a', 'all', None, _('uncommit all changes when no arguments given')),
+     ('r', 'rev', '', _('revert commit content to REV instead')),
      ] + commands.walkopts,
     _('[OPTION]... [NAME]'))
 def uncommit(ui, repo, *pats, **opts):
@@ -1956,6 +1977,10 @@
     The --include option specifies patterns to uncommit.
     The --exclude option specifies patterns to keep in the commit.
 
+    The --rev argument let you change the commit file to a content of another
+    revision. It still does not change the content of your file in the working
+    directory.
+
     Return 0 if changed files are uncommitted.
     """
 
@@ -1976,13 +2001,18 @@
         oldphase = old.phase()
         updatebookmarks = _bookmarksupdater(repo, old.node())
 
+
+        rev = None
+        if opts.get('rev'):
+            rev = scmutil.revsingle(repo, opts.get('rev'))
+
         # Recommit the filtered changeset
         tr = repo.transaction('uncommit')
         newid = None
         if (pats or opts.get('include') or opts.get('exclude')
             or opts.get('all')):
             match = scmutil.match(old, pats, opts)
-            newid = _commitfiltered(repo, old, match)
+            newid = _commitfiltered(repo, old, match, target=rev)
         if newid is None:
             raise util.Abort(_('nothing to uncommit'),
                              hint=_("use --all to uncommit all files"))