# HG changeset patch # User Angel Ezquerra # Date 1332927737 -7200 # Node ID a740fa28d71830ec091aac936611a91fb8f5393b # Parent ba42eb722bb39e702c54b7a6fa7a703d4ba2cec3 revert: move bulk of revert command from commands to cmdutil This revision has no functionality change. The code on the original commands.revert() function has been split. The first part of the original code, which checks that the command inputs are correct remains in commands.revert(). The rest of the function, which performs the actual revert operation has been moved into cmdutil.revert(). The purpose of this change is to make it easier to perform a revert operation, from other parts of the code. This may be used to implement reverting of subrepos. diff -r ba42eb722bb3 -r a740fa28d718 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Tue Mar 27 16:17:46 2012 -0500 +++ b/mercurial/cmdutil.py Wed Mar 28 11:42:17 2012 +0200 @@ -1325,6 +1325,183 @@ return text +def revert(ui, repo, ctx, parents, *pats, **opts): + parent, p2 = parents + node = ctx.node() + + 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() + def command(table): '''returns a function object bound to table which can be used as a decorator for populating table as a command table''' 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'))])