diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2011-10-01 20:49:36 +0000 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-09-27 13:27:51 +0000 |
commit | 921ced43c48c1d170452a7b251b94cc96ec8dd44 (patch) | |
tree | 3c4a89176ea67fe4c7bf7b375488361a823c95fa /mercurial/cmdutil.py | |
parent | 9039c805b0a7e36220101323f82735f08a104b37 (diff) | |
download | mercurial-tarball-master.tar.gz |
Imported from /srv/lorry/lorry-area/mercurial-tarball/mercurial-1.9.3.tar.gz.HEADmercurial-1.9.3master
Diffstat (limited to 'mercurial/cmdutil.py')
-rw-r--r-- | mercurial/cmdutil.py | 789 |
1 files changed, 51 insertions, 738 deletions
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py index 7ccbb62..8f9a9a7 100644 --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -8,9 +8,9 @@ from node import hex, nullid, nullrev, short from i18n import _ import os, sys, errno, re, tempfile -import util, scmutil, templater, patch, error, templatekw, revlog, copies +import util, scmutil, templater, patch, error, templatekw, revlog import match as matchmod -import subrepo, context, repair, bookmarks, graphmod, revset +import subrepo def parsealiases(cmd): return cmd.lstrip("^").split("|") @@ -23,14 +23,7 @@ def findpossible(cmd, table, strict=False): """ choice = {} debugchoice = {} - - if cmd in table: - # short-circuit exact matches, "log" alias beats "^log|history" - keys = [cmd] - else: - keys = table.keys() - - for e in keys: + for e in table.keys(): aliases = parsealiases(e) found = None if cmd in aliases: @@ -82,10 +75,6 @@ def bailifchanged(repo): modified, added, removed, deleted = repo.status()[:4] if modified or added or removed or deleted: raise util.Abort(_("outstanding uncommitted changes")) - ctx = repo[None] - for s in ctx.substate: - if ctx.sub(s).dirty(): - raise util.Abort(_("uncommitted changes in subrepo %s") % s) def logmessage(ui, opts): """ get the log message according to -m and -l option """ @@ -120,13 +109,12 @@ def loglimit(opts): limit = None return limit -def makefilename(repo, pat, node, desc=None, +def makefilename(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), - 'm': lambda: re.sub('[^\w]', '_', str(desc)) } expander = { '%': lambda: '%', @@ -166,14 +154,14 @@ def makefilename(repo, pat, node, desc=None, raise util.Abort(_("invalid format spec '%%%s' in output filename") % inst.args[0]) -def makefileobj(repo, pat, node=None, desc=None, total=None, +def makefileobj(repo, pat, node=None, total=None, seqno=None, revwidth=None, mode='wb', pathname=None): writable = mode not in ('r', 'rb') if not pat or pat == '-': fp = writable and repo.ui.fout or repo.ui.fin - if util.safehasattr(fp, 'fileno'): + if hasattr(fp, 'fileno'): return os.fdopen(os.dup(fp.fileno()), mode) else: # if this fp can't be duped properly, return @@ -189,11 +177,11 @@ def makefileobj(repo, pat, node=None, desc=None, total=None, return getattr(self.f, attr) return wrappedfileobj(fp) - if util.safehasattr(pat, 'write') and writable: + if hasattr(pat, 'write') and writable: return pat - if util.safehasattr(pat, 'read') and 'r' in mode: + if hasattr(pat, 'read') and 'r' in mode: return pat - return open(makefilename(repo, pat, node, desc, total, seqno, revwidth, + return open(makefilename(repo, pat, node, total, seqno, revwidth, pathname), mode) @@ -268,11 +256,6 @@ def copy(ui, repo, pats, opts, rename=False): # otarget: ossep def copyfile(abssrc, relsrc, otarget, exact): abstarget = scmutil.canonpath(repo.root, cwd, otarget) - if '/' in abstarget: - # We cannot normalize abstarget itself, this would prevent - # case only renames, like a => A. - abspath, absname = abstarget.rsplit('/', 1) - abstarget = repo.dirstate.normalize(abspath) + '/' + absname reltarget = repo.pathto(abstarget, cwd) target = repo.wjoin(abstarget) src = repo.wjoin(abssrc) @@ -290,16 +273,6 @@ def copy(ui, repo, pats, opts, rename=False): # check for overwrites exists = os.path.lexists(target) - samefile = False - if exists and abssrc != abstarget: - if (repo.dirstate.normalize(abssrc) == - repo.dirstate.normalize(abstarget)): - if not rename: - ui.warn(_("%s: can't copy - same file\n") % reltarget) - return - exists = False - samefile = True - if not after and exists or after and state in 'mn': if not opts['force']: ui.warn(_('%s: not overwriting - file exists\n') % @@ -322,12 +295,7 @@ def copy(ui, repo, pats, opts, rename=False): targetdir = os.path.dirname(target) or '.' if not os.path.isdir(targetdir): os.makedirs(targetdir) - if samefile: - tmp = target + "~hgrename" - os.rename(src, tmp) - os.rename(tmp, target) - else: - util.copyfile(src, target) + util.copyfile(src, target) srcexists = True except IOError, inst: if inst.errno == errno.ENOENT: @@ -350,7 +318,7 @@ def copy(ui, repo, pats, opts, rename=False): scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget, dryrun=dryrun, cwd=cwd) if rename and not dryrun: - if not after and srcexists and not samefile: + if not after and srcexists: util.unlinkpath(repo.wjoin(abssrc)) wctx.forget([abssrc]) @@ -548,13 +516,11 @@ def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False, shouldclose = False if not fp: - desc_lines = ctx.description().rstrip().split('\n') - desc = desc_lines[0] #Commit always has a first line. - fp = makefileobj(repo, template, node, desc=desc, total=total, - seqno=seqno, revwidth=revwidth, mode='ab') + fp = makefileobj(repo, template, node, total=total, seqno=seqno, + revwidth=revwidth, mode='ab') if fp != template: shouldclose = True - if fp != sys.stdout and util.safehasattr(fp, 'name'): + if fp != sys.stdout and hasattr(fp, 'name'): repo.ui.note("%s\n" % fp.name) fp.write("# HG changeset patch\n") @@ -608,17 +574,10 @@ def diffordiffstat(ui, repo, diffopts, node1, node2, match, ctx1 = repo[node1] ctx2 = repo[node2] for subpath, sub in subrepo.itersubrepos(ctx1, ctx2): - tempnode2 = node2 - try: - if node2 is not None: - tempnode2 = ctx2.substate[subpath][1] - except KeyError: - # A subrepo that existed in node1 was deleted between node1 and - # node2 (inclusive). Thus, ctx2's substate won't contain that - # subpath. The best we can do is to ignore it. - tempnode2 = None + if node2 is not None: + node2 = ctx2.substate[subpath][1] submatch = matchmod.narrowmatcher(subpath, match) - sub.diff(diffopts, tempnode2, submatch, changes=changes, + sub.diff(diffopts, node2, submatch, changes=changes, stat=stat, fp=fp, prefix=prefix) class changeset_printer(object): @@ -692,9 +651,6 @@ class changeset_printer(object): for tag in self.repo.nodetags(changenode): self.ui.write(_("tag: %s\n") % tag, label='log.tag') - if self.ui.debugflag and ctx.phase(): - self.ui.write(_("phase: %s\n") % _(ctx.phasestr()), - label='log.phase') for parent in parents: self.ui.write(_("parent: %d:%s\n") % parent, label='log.parent') @@ -910,10 +866,7 @@ def show_changeset(ui, repo, opts, buffered=False): if not (tmpl or style): tmpl = ui.config('ui', 'logtemplate') if tmpl: - try: - tmpl = templater.parsestring(tmpl) - except SyntaxError: - tmpl = templater.parsestring(tmpl, quoted=False) + tmpl = templater.parsestring(tmpl) else: style = util.expandpath(ui.config('ui', 'style', '')) @@ -952,26 +905,12 @@ def finddate(ui, repo, date): for ctx in walkchangerevs(repo, m, {'rev': None}, prep): rev = ctx.rev() if rev in results: - ui.status(_("found revision %s from %s\n") % + ui.status(_("Found revision %s from %s\n") % (rev, util.datestr(results[rev]))) return str(rev) raise util.Abort(_("revision matching date not found")) -def increasingwindows(start, end, windowsize=8, sizelimit=512): - if start < end: - while start < end: - yield start, min(windowsize, end - start) - start += windowsize - if windowsize < sizelimit: - windowsize *= 2 - else: - while start > end: - yield start, min(windowsize, start - end - 1) - start -= windowsize - if windowsize < sizelimit: - windowsize *= 2 - def walkchangerevs(repo, match, opts, prepare): '''Iterate over files and the revs in which they changed. @@ -987,6 +926,20 @@ def walkchangerevs(repo, match, opts, prepare): yielding each context, the iterator will first call the prepare function on each context in the window in forward order.''' + def increasing_windows(start, end, windowsize=8, sizelimit=512): + if start < end: + while start < end: + yield start, min(windowsize, end - start) + start += windowsize + if windowsize < sizelimit: + windowsize *= 2 + else: + while start > end: + yield start, min(windowsize, start - end - 1) + start -= windowsize + if windowsize < sizelimit: + windowsize *= 2 + follow = opts.get('follow') or opts.get('follow_first') if not len(repo): @@ -996,13 +949,13 @@ def walkchangerevs(repo, match, opts, prepare): defrange = '%s:0' % repo['.'].rev() else: defrange = '-1:0' - revs = scmutil.revrange(repo, opts.get('rev') or [defrange]) + revs = scmutil.revrange(repo, opts['rev'] or [defrange]) if not revs: return [] wanted = set() slowpath = match.anypats() or (match.files() and opts.get('removed')) fncache = {} - change = repo.changectx + change = util.cachefunc(repo.changectx) # First step is to fill wanted, the set of revisions that we want to yield. # When it does not induce extra cost, we also fill fncache for revisions in @@ -1014,7 +967,7 @@ def walkchangerevs(repo, match, opts, prepare): wanted = set(revs) copies = [] - if not slowpath and match.files(): + if not slowpath: # We only have to read through the filelog to find wanted revisions minrev, maxrev = min(revs), max(revs) @@ -1047,15 +1000,8 @@ def walkchangerevs(repo, match, opts, prepare): return reversed(revs) def iterfiles(): - pctx = repo['.'] for filename in match.files(): - if follow: - if filename not in pctx: - raise util.Abort(_('cannot follow file not in parent ' - 'revision: "%s"') % filename) - yield filename, pctx[filename].filenode() - else: - yield filename, None + yield filename, None for filename_node in copies: yield filename_node for file_, node in iterfiles(): @@ -1158,7 +1104,7 @@ def walkchangerevs(repo, match, opts, prepare): # it might be worthwhile to do this in the iterator if the rev range # is descending and the prune args are all within that range for rev in opts.get('prune', ()): - rev = repo[rev].rev() + rev = repo.changelog.rev(repo.lookup(rev)) ff = followfilter() stop = min(revs[0], revs[-1]) for x in xrange(rev, stop - 1, -1): @@ -1176,7 +1122,7 @@ def walkchangerevs(repo, match, opts, prepare): def want(rev): return rev in wanted - for i, window in increasingwindows(0, len(revs)): + for i, window in increasing_windows(0, len(revs)): nrevs = [rev for rev in revs[i:i + window] if want(rev)] for rev in sorted(nrevs): fns = fncache.get(rev) @@ -1192,278 +1138,7 @@ def walkchangerevs(repo, match, opts, prepare): yield change(rev) return iterate() -def _makegraphfilematcher(repo, pats, followfirst): - # When displaying a revision with --patch --follow FILE, we have - # to know which file of the revision must be diffed. With - # --follow, we want the names of the ancestors of FILE in the - # revision, stored in "fcache". "fcache" is populated by - # reproducing the graph traversal already done by --follow revset - # and relating linkrevs to file names (which is not "correct" but - # good enough). - fcache = {} - fcacheready = [False] - pctx = repo['.'] - wctx = repo[None] - - def populate(): - for fn in pats: - for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)): - for c in i: - fcache.setdefault(c.linkrev(), set()).add(c.path()) - - def filematcher(rev): - if not fcacheready[0]: - # Lazy initialization - fcacheready[0] = True - populate() - return scmutil.match(wctx, fcache.get(rev, []), default='path') - - return filematcher - -def _makegraphlogrevset(repo, pats, opts, revs): - """Return (expr, filematcher) where expr is a revset string built - from log options and file patterns or None. If --stat or --patch - are not passed filematcher is None. Otherwise it is a callable - taking a revision number and returning a match objects filtering - the files to be detailed when displaying the revision. - """ - opt2revset = { - 'no_merges': ('not merge()', None), - 'only_merges': ('merge()', None), - '_ancestors': ('ancestors(%(val)s)', None), - '_fancestors': ('_firstancestors(%(val)s)', None), - '_descendants': ('descendants(%(val)s)', None), - '_fdescendants': ('_firstdescendants(%(val)s)', None), - '_matchfiles': ('_matchfiles(%(val)s)', None), - 'date': ('date(%(val)r)', None), - 'branch': ('branch(%(val)r)', ' or '), - '_patslog': ('filelog(%(val)r)', ' or '), - '_patsfollow': ('follow(%(val)r)', ' or '), - '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '), - 'keyword': ('keyword(%(val)r)', ' or '), - 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '), - 'user': ('user(%(val)r)', ' or '), - } - - opts = dict(opts) - # follow or not follow? - follow = opts.get('follow') or opts.get('follow_first') - followfirst = opts.get('follow_first') and 1 or 0 - # --follow with FILE behaviour depends on revs... - startrev = revs[0] - followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0 - - # branch and only_branch are really aliases and must be handled at - # the same time - opts['branch'] = opts.get('branch', []) + opts.get('only_branch', []) - opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']] - # pats/include/exclude are passed to match.match() directly in - # _matchfile() revset but walkchangerevs() builds its matcher with - # scmutil.match(). The difference is input pats are globbed on - # platforms without shell expansion (windows). - pctx = repo[None] - match, pats = scmutil.matchandpats(pctx, pats, opts) - slowpath = match.anypats() or (match.files() and opts.get('removed')) - if not slowpath: - for f in match.files(): - if follow and f not in pctx: - raise util.Abort(_('cannot follow file not in parent ' - 'revision: "%s"') % f) - filelog = repo.file(f) - if not len(filelog): - # A zero count may be a directory or deleted file, so - # try to find matching entries on the slow path. - if follow: - raise util.Abort( - _('cannot follow nonexistent file: "%s"') % f) - slowpath = True - if slowpath: - # See walkchangerevs() slow path. - # - if follow: - raise util.Abort(_('can only follow copies/renames for explicit ' - 'filenames')) - # pats/include/exclude cannot be represented as separate - # revset expressions as their filtering logic applies at file - # level. For instance "-I a -X a" matches a revision touching - # "a" and "b" while "file(a) and not file(b)" does - # not. Besides, filesets are evaluated against the working - # directory. - matchargs = ['r:', 'd:relpath'] - for p in pats: - matchargs.append('p:' + p) - for p in opts.get('include', []): - matchargs.append('i:' + p) - for p in opts.get('exclude', []): - matchargs.append('x:' + p) - matchargs = ','.join(('%r' % p) for p in matchargs) - opts['_matchfiles'] = matchargs - else: - if follow: - fpats = ('_patsfollow', '_patsfollowfirst') - fnopats = (('_ancestors', '_fancestors'), - ('_descendants', '_fdescendants')) - if pats: - # follow() revset inteprets its file argument as a - # manifest entry, so use match.files(), not pats. - opts[fpats[followfirst]] = list(match.files()) - else: - opts[fnopats[followdescendants][followfirst]] = str(startrev) - else: - opts['_patslog'] = list(pats) - - filematcher = None - if opts.get('patch') or opts.get('stat'): - if follow: - filematcher = _makegraphfilematcher(repo, pats, followfirst) - else: - filematcher = lambda rev: match - - expr = [] - for op, val in opts.iteritems(): - if not val: - continue - if op not in opt2revset: - continue - revop, andor = opt2revset[op] - if '%(val)' not in revop: - expr.append(revop) - else: - if not isinstance(val, list): - e = revop % {'val': val} - else: - e = '(' + andor.join((revop % {'val': v}) for v in val) + ')' - expr.append(e) - - if expr: - expr = '(' + ' and '.join(expr) + ')' - else: - expr = None - return expr, filematcher - -def getgraphlogrevs(repo, pats, opts): - """Return (revs, expr, filematcher) where revs is an iterable of - revision numbers, expr is a revset string built from log options - and file patterns or None, and used to filter 'revs'. If --stat or - --patch are not passed filematcher is None. Otherwise it is a - callable taking a revision number and returning a match objects - filtering the files to be detailed when displaying the revision. - """ - def increasingrevs(repo, revs, matcher): - # The sorted input rev sequence is chopped in sub-sequences - # which are sorted in ascending order and passed to the - # matcher. The filtered revs are sorted again as they were in - # the original sub-sequence. This achieve several things: - # - # - getlogrevs() now returns a generator which behaviour is - # adapted to log need. First results come fast, last ones - # are batched for performances. - # - # - revset matchers often operate faster on revision in - # changelog order, because most filters deal with the - # changelog. - # - # - revset matchers can reorder revisions. "A or B" typically - # returns returns the revision matching A then the revision - # matching B. We want to hide this internal implementation - # detail from the caller, and sorting the filtered revision - # again achieves this. - for i, window in increasingwindows(0, len(revs), windowsize=1): - orevs = revs[i:i + window] - nrevs = set(matcher(repo, sorted(orevs))) - for rev in orevs: - if rev in nrevs: - yield rev - - if not len(repo): - return iter([]), None, None - # Default --rev value depends on --follow but --follow behaviour - # depends on revisions resolved from --rev... - follow = opts.get('follow') or opts.get('follow_first') - if opts.get('rev'): - revs = scmutil.revrange(repo, opts['rev']) - else: - if follow and len(repo) > 0: - revs = scmutil.revrange(repo, ['.:0']) - else: - revs = range(len(repo) - 1, -1, -1) - if not revs: - return iter([]), None, None - expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs) - if expr: - matcher = revset.match(repo.ui, expr) - revs = increasingrevs(repo, revs, matcher) - if not opts.get('hidden'): - # --hidden is still experimental and not worth a dedicated revset - # yet. Fortunately, filtering revision number is fast. - revs = (r for r in revs if r not in repo.hiddenrevs) - else: - revs = iter(revs) - return revs, expr, filematcher - -def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None, - filematcher=None): - seen, state = [], graphmod.asciistate() - for rev, type, ctx, parents in dag: - char = 'o' - if ctx.node() in showparents: - char = '@' - elif ctx.obsolete(): - char = 'x' - copies = None - if getrenamed and ctx.rev(): - copies = [] - for fn in ctx.files(): - rename = getrenamed(fn, ctx.rev()) - if rename: - copies.append((fn, rename[0])) - revmatchfn = None - if filematcher is not None: - revmatchfn = filematcher(ctx.rev()) - displayer.show(ctx, copies=copies, matchfn=revmatchfn) - lines = displayer.hunk.pop(rev).split('\n') - if not lines[-1]: - del lines[-1] - displayer.flush(rev) - edges = edgefn(type, char, lines, seen, rev, parents) - for type, char, lines, coldata in edges: - graphmod.ascii(ui, state, type, char, lines, coldata) - displayer.close() - -def graphlog(ui, repo, *pats, **opts): - # Parameters are identical to log command ones - revs, expr, filematcher = getgraphlogrevs(repo, pats, opts) - revs = sorted(revs, reverse=1) - limit = loglimit(opts) - if limit is not None: - revs = revs[:limit] - revdag = graphmod.dagwalker(repo, revs) - - getrenamed = None - if opts.get('copies'): - endrev = None - if opts.get('rev'): - endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1 - getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) - displayer = show_changeset(ui, repo, opts, buffered=True) - showparents = [ctx.node() for ctx in repo[None].parents()] - displaygraph(ui, revdag, displayer, showparents, - graphmod.asciiedges, getrenamed, filematcher) - -def checkunsupportedgraphflags(pats, opts): - for op in ["newest_first"]: - if op in opts and opts[op]: - raise util.Abort(_("-G/--graph option is incompatible with --%s") - % op.replace("_", "-")) - -def graphrevs(repo, nodes, opts): - limit = loglimit(opts) - nodes.reverse() - if limit is not None: - nodes = nodes[:limit] - return graphmod.nodes(repo, nodes) - -def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly): +def add(ui, repo, match, dryrun, listsubrepos, prefix): join = lambda f: os.path.join(prefix, f) bad = [] oldbad = match.bad @@ -1473,82 +1148,31 @@ def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly): cca = None abort, warn = scmutil.checkportabilityalert(ui) if abort or warn: - cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate) + cca = scmutil.casecollisionauditor(ui, abort, wctx) for f in repo.walk(match): exact = match.exact(f) - if exact or not explicitonly and f not in repo.dirstate: + if exact or f not in repo.dirstate: if cca: cca(f) names.append(f) if ui.verbose or not exact: ui.status(_('adding %s\n') % match.rel(join(f))) - for subpath in wctx.substate: - sub = wctx.sub(subpath) - try: - submatch = matchmod.narrowmatcher(subpath, match) - if listsubrepos: - bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix, - False)) - else: - bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix, - True)) - except error.LookupError: - ui.status(_("skipping missing subrepository: %s\n") - % join(subpath)) + if listsubrepos: + for subpath in wctx.substate: + sub = wctx.sub(subpath) + try: + submatch = matchmod.narrowmatcher(subpath, match) + bad.extend(sub.add(ui, submatch, dryrun, prefix)) + except error.LookupError: + ui.status(_("skipping missing subrepository: %s\n") + % join(subpath)) if not dryrun: rejected = wctx.add(names, prefix) bad.extend(f for f in rejected if f in match.files()) return bad -def forget(ui, repo, match, prefix, explicitonly): - join = lambda f: os.path.join(prefix, f) - bad = [] - oldbad = match.bad - match.bad = lambda x, y: bad.append(x) or oldbad(x, y) - wctx = repo[None] - forgot = [] - s = repo.status(match=match, clean=True) - forget = sorted(s[0] + s[1] + s[3] + s[6]) - if explicitonly: - forget = [f for f in forget if match.exact(f)] - - for subpath in wctx.substate: - sub = wctx.sub(subpath) - try: - submatch = matchmod.narrowmatcher(subpath, match) - subbad, subforgot = sub.forget(ui, submatch, prefix) - bad.extend([subpath + '/' + f for f in subbad]) - forgot.extend([subpath + '/' + f for f in subforgot]) - except error.LookupError: - ui.status(_("skipping missing subrepository: %s\n") - % join(subpath)) - - if not explicitonly: - for f in match.files(): - if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))): - if f not in forgot: - if os.path.exists(match.rel(join(f))): - ui.warn(_('not removing %s: ' - 'file is already untracked\n') - % match.rel(join(f))) - bad.append(f) - - for f in forget: - if ui.verbose or not match.exact(f): - ui.status(_('removing %s\n') % match.rel(join(f))) - - rejected = wctx.forget(forget, prefix) - bad.extend(f for f in rejected if f in match.files()) - forgot.extend(forget) - return bad, forgot - -def duplicatecopies(repo, rev, p1): - "Reproduce copies found in the source revision in the dirstate for grafts" - for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems(): - repo.dirstate.copy(src, dst) - def commit(ui, repo, commitfunc, pats, opts): '''commit the specified files or all outstanding changes''' date = opts.get('date') @@ -1564,136 +1188,6 @@ def commit(ui, repo, commitfunc, pats, opts): return commitfunc(ui, repo, message, scmutil.match(repo[None], pats, opts), opts) -def amend(ui, repo, commitfunc, old, extra, pats, opts): - ui.note(_('amending changeset %s\n') % old) - base = old.p1() - - wlock = repo.wlock() - try: - # First, do a regular commit to record all changes in the working - # directory (if there are any) - ui.callhooks = False - try: - node = commit(ui, repo, commitfunc, pats, opts) - finally: - ui.callhooks = True - ctx = repo[node] - - # Participating changesets: - # - # node/ctx o - new (intermediate) commit that contains changes from - # | working dir to go into amending commit (or a workingctx - # | if there were no changes) - # | - # old o - changeset to amend - # | - # base o - parent of amending changeset - - # Update extra dict from amended commit (e.g. to preserve graft source) - extra.update(old.extra()) - - # Also update it from the intermediate commit or from the wctx - extra.update(ctx.extra()) - - files = set(old.files()) - - # Second, we use either the commit we just did, or if there were no - # changes the parent of the working directory as the version of the - # files in the final amend commit - if node: - ui.note(_('copying changeset %s to %s\n') % (ctx, base)) - - user = ctx.user() - date = ctx.date() - message = ctx.description() - # Recompute copies (avoid recording a -> b -> a) - copied = copies.pathcopies(base, ctx) - - # Prune files which were reverted by the updates: if old introduced - # file X and our intermediate commit, node, renamed that file, then - # those two files are the same and we can discard X from our list - # of files. Likewise if X was deleted, it's no longer relevant - files.update(ctx.files()) - - def samefile(f): - if f in ctx.manifest(): - a = ctx.filectx(f) - if f in base.manifest(): - b = base.filectx(f) - return (not a.cmp(b) - and a.flags() == b.flags()) - else: - return False - else: - return f not in base.manifest() - files = [f for f in files if not samefile(f)] - - def filectxfn(repo, ctx_, path): - try: - fctx = ctx[path] - flags = fctx.flags() - mctx = context.memfilectx(fctx.path(), fctx.data(), - islink='l' in flags, - isexec='x' in flags, - copied=copied.get(path)) - return mctx - except KeyError: - raise IOError - else: - ui.note(_('copying changeset %s to %s\n') % (old, base)) - - # Use version of files as in the old cset - def filectxfn(repo, ctx_, path): - try: - return old.filectx(path) - except KeyError: - raise IOError - - # See if we got a message from -m or -l, if not, open the editor - # with the message of the changeset to amend - user = opts.get('user') or old.user() - date = opts.get('date') or old.date() - message = logmessage(ui, opts) - if not message: - cctx = context.workingctx(repo, old.description(), user, date, - extra, - repo.status(base.node(), old.node())) - message = commitforceeditor(repo, cctx, []) - - new = context.memctx(repo, - parents=[base.node(), nullid], - text=message, - files=files, - filectxfn=filectxfn, - user=user, - date=date, - extra=extra) - newid = repo.commitctx(new) - if newid != old.node(): - # Reroute the working copy parent to the new changeset - repo.setparents(newid, nullid) - - # Move bookmarks from old parent to amend commit - bms = repo.nodebookmarks(old.node()) - if bms: - for bm in bms: - repo._bookmarks[bm] = newid - bookmarks.write(repo) - - # Strip the intermediate commit (if there was one) and the amended - # commit - lock = repo.lock() - try: - if node: - ui.note(_('stripping intermediate changeset %s\n') % ctx) - ui.note(_('stripping amended changeset %s\n') % old) - repair.strip(ui, repo, old.node(), topic='amend-backup') - finally: - lock.release() - finally: - wlock.release() - return newid - def commiteditor(repo, ctx, subs): if ctx.description(): return ctx.description() @@ -1734,187 +1228,6 @@ def commitforceeditor(repo, ctx, subs): 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 ctx.substate: - return - path_ = path + '/' - for f in names: - if f.startswith(path_): - return - ui.warn("%s: %s\n" % (m.rel(path), msg)) - - m = scmutil.match(ctx, pats, opts) - m.bad = badfn - for abs in ctx.walk(m): - if abs not in names: - names[abs] = m.rel(abs), m.exact(abs) - - # get the list of subrepos that must be reverted - targetsubs = [s for s in ctx.substate if m(s)] - 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) - - if targetsubs: - # Revert the subrepos on the revert list - for sub in targetsubs: - ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts) - 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''' |