# HG changeset patch # User Pierre-Yves David # Date 1408088783 25200 # Node ID 00bc3152307497337b3bf37a11f105cff8275ed5 # Parent edfb9a0ad085481ceedd33560684f1e6f01e3aa5 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(.)' diff --git a/README b/README --- a/README +++ b/README @@ -57,6 +57,10 @@ Changelog ========= +4.2.0 -- + +- uncommit: add a --rev argument + 4.1.0 -- 2014-08-08 - amend: add -D/--current-date option diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -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")) diff --git a/tests/test-tutorial.t b/tests/test-tutorial.t --- a/tests/test-tutorial.t +++ b/tests/test-tutorial.t @@ -446,11 +446,16 @@ 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. options: -a --all uncommit all changes when no arguments given + -r --rev VALUE revert commit content to REV instead -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns diff --git a/tests/test-uncommit.t b/tests/test-uncommit.t --- a/tests/test-uncommit.t +++ b/tests/test-uncommit.t @@ -336,3 +336,25 @@ [8] touncommit $ hg uncommit aa 1 new unstable changesets + +Test uncommiting agains a different base + + $ hg cat b --rev . + b + b + $ hg cat b --rev .^ + b + $ hg cat b --rev 0 + b: no such file in rev 07f494440405 + [1] + $ hg uncommit --rev 0 b + $ hg cat b --rev . + b: no such file in rev 5b27f6b17da2 + [1] + +Test uncommiting precursors + + $ hg uncommit --hidden --rev 'precursors(.)' b + $ hg cat b --rev . + b + b