Mercurial > hg
view mercurial/cmdutil.py @ 3185:b3e103c388fc
mq: don't write applied patches into series twice in restore
author | Brendan Cully <brendan@kublai.com> |
---|---|
date | Wed, 27 Sep 2006 14:50:20 -0700 |
parents | eeaf9bcdfa25 |
children | a33a9af1ec03 |
line wrap: on
line source
# cmdutil.py - help for command processing in mercurial # # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. from demandload import demandload from node import * from i18n import gettext as _ demandload(globals(), 'mdiff util') demandload(globals(), 'os sys') revrangesep = ':' def revfix(repo, val, defval): '''turn user-level id of changeset into rev number. user-level id can be tag, changeset, rev number, or negative rev number relative to number of revs (-1 is tip, etc).''' if not val: return defval try: num = int(val) if str(num) != val: raise ValueError if num < 0: num += repo.changelog.count() if num < 0: num = 0 elif num >= repo.changelog.count(): raise ValueError except ValueError: try: num = repo.changelog.rev(repo.lookup(val)) except KeyError: raise util.Abort(_('invalid revision identifier %s') % val) return num def revpair(ui, repo, revs): '''return pair of nodes, given list of revisions. second item can be None, meaning use working dir.''' if not revs: return repo.dirstate.parents()[0], None end = None if len(revs) == 1: start = revs[0] if revrangesep in start: start, end = start.split(revrangesep, 1) start = revfix(repo, start, 0) end = revfix(repo, end, repo.changelog.count() - 1) else: start = revfix(repo, start, None) elif len(revs) == 2: if revrangesep in revs[0] or revrangesep in revs[1]: raise util.Abort(_('too many revisions specified')) start = revfix(repo, revs[0], None) end = revfix(repo, revs[1], None) else: raise util.Abort(_('too many revisions specified')) if end is not None: end = repo.lookup(str(end)) return repo.lookup(str(start)), end def revrange(ui, repo, revs): """Yield revision as strings from a list of revision specifications.""" seen = {} for spec in revs: if revrangesep in spec: start, end = spec.split(revrangesep, 1) start = revfix(repo, start, 0) end = revfix(repo, end, repo.changelog.count() - 1) step = start > end and -1 or 1 for rev in xrange(start, end+step, step): if rev in seen: continue seen[rev] = 1 yield str(rev) else: rev = revfix(repo, spec, None) if rev in seen: continue seen[rev] = 1 yield str(rev) def make_filename(repo, pat, node, total=None, seqno=None, revwidth=None, pathname=None): node_expander = { 'H': lambda: hex(node), 'R': lambda: str(repo.changelog.rev(node)), 'h': lambda: short(node), } expander = { '%': lambda: '%', 'b': lambda: os.path.basename(repo.root), } try: if node: expander.update(node_expander) if node and revwidth is not None: expander['r'] = (lambda: str(repo.changelog.rev(node)).zfill(revwidth)) if total is not None: expander['N'] = lambda: str(total) if seqno is not None: expander['n'] = lambda: str(seqno) if total is not None and seqno is not None: expander['n'] = lambda:str(seqno).zfill(len(str(total))) if pathname is not None: expander['s'] = lambda: os.path.basename(pathname) expander['d'] = lambda: os.path.dirname(pathname) or '.' expander['p'] = lambda: pathname newname = [] patlen = len(pat) i = 0 while i < patlen: c = pat[i] if c == '%': i += 1 c = pat[i] c = expander[c]() newname.append(c) i += 1 return ''.join(newname) except KeyError, inst: raise util.Abort(_("invalid format spec '%%%s' in output file name") % inst.args[0]) def make_file(repo, pat, node=None, total=None, seqno=None, revwidth=None, mode='wb', pathname=None): if not pat or pat == '-': return 'w' in mode and sys.stdout or sys.stdin if hasattr(pat, 'write') and 'w' in mode: return pat if hasattr(pat, 'read') and 'r' in mode: return pat return open(make_filename(repo, pat, node, total, seqno, revwidth, pathname), mode) def matchpats(repo, pats=[], opts={}, head=''): cwd = repo.getcwd() if not pats and cwd: opts['include'] = [os.path.join(cwd, i) for i in opts.get('include', [])] opts['exclude'] = [os.path.join(cwd, x) for x in opts.get('exclude', [])] cwd = '' return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'), opts.get('exclude'), head) def makewalk(repo, pats=[], opts={}, node=None, head='', badmatch=None): files, matchfn, anypats = matchpats(repo, pats, opts, head) exact = dict(zip(files, files)) def walk(): for src, fn in repo.walk(node=node, files=files, match=matchfn, badmatch=badmatch): yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact return files, matchfn, walk() def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None): files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch) for r in results: yield r def findrenames(repo, added=None, removed=None, threshold=0.5): if added is None or removed is None: added, removed = repo.status()[1:3] changes = repo.changelog.read(repo.dirstate.parents()[0]) mf = repo.manifest.read(changes[0]) for a in added: aa = repo.wread(a) bestscore, bestname = None, None for r in removed: rr = repo.file(r).read(mf[r]) delta = mdiff.textdiff(aa, rr) if len(delta) < len(aa): myscore = 1.0 - (float(len(delta)) / len(aa)) if bestscore is None or myscore > bestscore: bestscore, bestname = myscore, r if bestname and bestscore >= threshold: yield bestname, a, bestscore def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None, similarity=None): if dry_run is None: dry_run = opts.get('dry_run') if similarity is None: similarity = float(opts.get('similarity') or 0) add, remove = [], [] mapping = {} for src, abs, rel, exact in walk(repo, pats, opts): if src == 'f' and repo.dirstate.state(abs) == '?': add.append(abs) mapping[abs] = rel, exact if repo.ui.verbose or not exact: repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): remove.append(abs) mapping[abs] = rel, exact if repo.ui.verbose or not exact: repo.ui.status(_('removing %s\n') % ((pats and rel) or abs)) if not dry_run: repo.add(add, wlock=wlock) repo.remove(remove, wlock=wlock) if similarity > 0: for old, new, score in findrenames(repo, add, remove, similarity): oldrel, oldexact = mapping[old] newrel, newexact = mapping[new] if repo.ui.verbose or not oldexact or not newexact: repo.ui.status(_('recording removal of %s as rename to %s ' '(%d%% similar)\n') % (oldrel, newrel, score * 100)) if not dry_run: repo.copy(old, new, wlock=wlock)