cmdutil.py
197 lines
| 7.1 KiB
| text/x-python
|
PythonLexer
/ mercurial / cmdutil.py
|
r2957 | # cmdutil.py - help for command processing in mercurial | ||
|
r2874 | # | ||
# 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 _ | ||||
|
r2958 | demandload(globals(), 'mdiff util') | ||
|
r2874 | demandload(globals(), 'os sys') | ||
|
r3090 | revrangesep = ':' | ||
def revpair(ui, repo, revs): | ||||
'''return pair of nodes, given list of revisions. second item can | ||||
be None, meaning use working dir.''' | ||||
|
r3525 | |||
def revfix(repo, val, defval): | ||||
if not val and val != 0: | ||||
val = defval | ||||
return repo.lookup(val) | ||||
|
r3090 | if not revs: | ||
return repo.dirstate.parents()[0], None | ||||
end = None | ||||
if len(revs) == 1: | ||||
|
r3525 | if revrangesep in revs[0]: | ||
start, end = revs[0].split(revrangesep, 1) | ||||
|
r3090 | start = revfix(repo, start, 0) | ||
end = revfix(repo, end, repo.changelog.count() - 1) | ||||
else: | ||||
|
r3525 | start = revfix(repo, revs[0], None) | ||
|
r3090 | 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')) | ||||
|
r3525 | return start, end | ||
|
r3090 | |||
def revrange(ui, repo, revs): | ||||
"""Yield revision as strings from a list of revision specifications.""" | ||||
|
r3525 | |||
def revfix(repo, val, defval): | ||||
if not val and val != 0: | ||||
return defval | ||||
return repo.changelog.rev(repo.lookup(val)) | ||||
|
r3526 | seen, l = {}, [] | ||
|
r3090 | 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 | ||||
|
r3526 | l.append(rev) | ||
|
r3090 | else: | ||
rev = revfix(repo, spec, None) | ||||
if rev in seen: | ||||
continue | ||||
seen[rev] = 1 | ||||
|
r3526 | l.append(rev) | ||
return l | ||||
|
r3090 | |||
|
r2874 | 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: | ||||
|
r3072 | raise util.Abort(_("invalid format spec '%%%s' in output file name") % | ||
inst.args[0]) | ||||
|
r2874 | |||
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) | ||||
|
r2882 | |||
def matchpats(repo, pats=[], opts={}, head=''): | ||||
cwd = repo.getcwd() | ||||
if not pats and cwd: | ||||
|
r2885 | 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', [])] | ||||
|
r2882 | cwd = '' | ||
return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'), | ||||
opts.get('exclude'), head) | ||||
|
r3528 | def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None): | ||
|
r2882 | files, matchfn, anypats = matchpats(repo, pats, opts, head) | ||
|
r3531 | exact = dict.fromkeys(files) | ||
|
r3528 | 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 | ||||
|
r2883 | |||
|
r2958 | 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): | ||||
|
r2883 | if dry_run is None: | ||
dry_run = opts.get('dry_run') | ||||
|
r2958 | if similarity is None: | ||
similarity = float(opts.get('similarity') or 0) | ||||
|
r2883 | add, remove = [], [] | ||
|
r2958 | mapping = {} | ||
|
r2883 | for src, abs, rel, exact in walk(repo, pats, opts): | ||
if src == 'f' and repo.dirstate.state(abs) == '?': | ||||
add.append(abs) | ||||
|
r2958 | mapping[abs] = rel, exact | ||
|
r2883 | 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) | ||||
|
r2958 | mapping[abs] = rel, exact | ||
|
r2883 | 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) | ||||
|
r2958 | 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) | ||||