diff -r ba42eb722bb3 -r a740fa28d718 mercurial/commands.py --- a/mercurial/commands.py Tue Mar 27 16:17:46 2012 -0500 +++ b/mercurial/commands.py Wed Mar 28 11:42:17 2012 +0200 @@ -4779,7 +4779,6 @@ hint=_('use "hg update" or see "hg help revert"')) ctx = scmutil.revsingle(repo, opts.get('rev')) - node = ctx.node() if not pats and not opts.get('all'): msg = _("no files or directories specified") @@ -4788,6 +4787,7 @@ " or 'hg update -C .' to abort the merge") raise util.Abort(msg, hint=hint) dirty = util.any(repo.status()) + node = ctx.node() if node != parent: if dirty: hint = _("uncommitted changes, use --all to discard all" @@ -4801,178 +4801,7 @@ hint = _("use --all to revert all files") raise util.Abort(msg, hint=hint) - mf = ctx.manifest() - if node == parent: - pmf = mf - else: - pmf = None - - # need all matching names in dirstate and manifest of target rev, - # so have to walk both. do not print errors if files exist in one - # but not other. - - names = {} - - wlock = repo.wlock() - try: - # walk dirstate. - - m = scmutil.match(repo[None], pats, opts) - m.bad = lambda x, y: False - for abs in repo.walk(m): - names[abs] = m.rel(abs), m.exact(abs) - - # walk target manifest. - - def badfn(path, msg): - if path in names: - return - if path in repo[node].substate: - ui.warn("%s: %s\n" % (m.rel(path), - 'reverting subrepos is unsupported')) - return - path_ = path + '/' - for f in names: - if f.startswith(path_): - return - ui.warn("%s: %s\n" % (m.rel(path), msg)) - - m = scmutil.match(repo[node], pats, opts) - m.bad = badfn - for abs in repo[node].walk(m): - if abs not in names: - names[abs] = m.rel(abs), m.exact(abs) - - m = scmutil.matchfiles(repo, names) - changes = repo.status(match=m)[:4] - modified, added, removed, deleted = map(set, changes) - - # if f is a rename, also revert the source - cwd = repo.getcwd() - for f in added: - src = repo.dirstate.copied(f) - if src and src not in names and repo.dirstate[src] == 'r': - removed.add(src) - names[src] = (repo.pathto(src, cwd), True) - - def removeforget(abs): - if repo.dirstate[abs] == 'a': - return _('forgetting %s\n') - return _('removing %s\n') - - revert = ([], _('reverting %s\n')) - add = ([], _('adding %s\n')) - remove = ([], removeforget) - undelete = ([], _('undeleting %s\n')) - - disptable = ( - # dispatch table: - # file state - # action if in target manifest - # action if not in target manifest - # make backup if in target manifest - # make backup if not in target manifest - (modified, revert, remove, True, True), - (added, revert, remove, True, False), - (removed, undelete, None, False, False), - (deleted, revert, remove, False, False), - ) - - for abs, (rel, exact) in sorted(names.items()): - mfentry = mf.get(abs) - target = repo.wjoin(abs) - def handle(xlist, dobackup): - xlist[0].append(abs) - if (dobackup and not opts.get('no_backup') and - os.path.lexists(target)): - bakname = "%s.orig" % rel - ui.note(_('saving current version of %s as %s\n') % - (rel, bakname)) - if not opts.get('dry_run'): - util.rename(target, bakname) - if ui.verbose or not exact: - msg = xlist[1] - if not isinstance(msg, basestring): - msg = msg(abs) - ui.status(msg % rel) - for table, hitlist, misslist, backuphit, backupmiss in disptable: - if abs not in table: - continue - # file has changed in dirstate - if mfentry: - handle(hitlist, backuphit) - elif misslist is not None: - handle(misslist, backupmiss) - break - else: - if abs not in repo.dirstate: - if mfentry: - handle(add, True) - elif exact: - ui.warn(_('file not managed: %s\n') % rel) - continue - # file has not changed in dirstate - if node == parent: - if exact: - ui.warn(_('no changes needed to %s\n') % rel) - continue - if pmf is None: - # only need parent manifest in this unlikely case, - # so do not read by default - pmf = repo[parent].manifest() - if abs in pmf and mfentry: - # if version of file is same in parent and target - # manifests, do nothing - if (pmf[abs] != mfentry or - pmf.flags(abs) != mf.flags(abs)): - handle(revert, False) - else: - handle(remove, False) - - if not opts.get('dry_run'): - def checkout(f): - fc = ctx[f] - repo.wwrite(f, fc.data(), fc.flags()) - - audit_path = scmutil.pathauditor(repo.root) - for f in remove[0]: - if repo.dirstate[f] == 'a': - repo.dirstate.drop(f) - continue - audit_path(f) - try: - util.unlinkpath(repo.wjoin(f)) - except OSError: - pass - repo.dirstate.remove(f) - - normal = None - if node == parent: - # We're reverting to our parent. If possible, we'd like status - # to report the file as clean. We have to use normallookup for - # merges to avoid losing information about merged/dirty files. - if p2 != nullid: - normal = repo.dirstate.normallookup - else: - normal = repo.dirstate.normal - for f in revert[0]: - checkout(f) - if normal: - normal(f) - - for f in add[0]: - checkout(f) - repo.dirstate.add(f) - - normal = repo.dirstate.normallookup - if node == parent and p2 == nullid: - normal = repo.dirstate.normal - for f in undelete[0]: - checkout(f) - normal(f) - - finally: - wlock.release() + return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts) @command('rollback', dryrunopts + [('f', 'force', False, _('ignore safety measures'))])