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/context.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/context.py')
-rw-r--r-- | mercurial/context.py | 330 |
1 files changed, 66 insertions, 264 deletions
diff --git a/mercurial/context.py b/mercurial/context.py index 88ea3e4..d1c195b 100644 --- a/mercurial/context.py +++ b/mercurial/context.py @@ -5,10 +5,9 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid, nullrev, short, hex, bin +from node import nullid, nullrev, short, hex from i18n import _ -import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding, phases -import copies +import ancestor, bdiff, error, util, scmutil, subrepo, patch, encoding import match as matchmod import os, errno, stat @@ -22,88 +21,12 @@ class changectx(object): if changeid == '': changeid = '.' self._repo = repo - - if isinstance(changeid, int): + if isinstance(changeid, (long, int)): self._rev = changeid - self._node = repo.changelog.node(changeid) - return - if isinstance(changeid, long): - changeid = str(changeid) - if changeid == '.': - self._node = repo.dirstate.p1() - self._rev = repo.changelog.rev(self._node) - return - if changeid == 'null': - self._node = nullid - self._rev = nullrev - return - if changeid == 'tip': - self._rev = len(repo.changelog) - 1 - self._node = repo.changelog.node(self._rev) - return - if len(changeid) == 20: - try: - self._node = changeid - self._rev = repo.changelog.rev(changeid) - return - except LookupError: - pass - - try: - r = int(changeid) - if str(r) != changeid: - raise ValueError - l = len(repo.changelog) - if r < 0: - r += l - if r < 0 or r >= l: - raise ValueError - self._rev = r - self._node = repo.changelog.node(r) - return - except (ValueError, OverflowError): - pass - - if len(changeid) == 40: - try: - self._node = bin(changeid) - self._rev = repo.changelog.rev(self._node) - return - except (TypeError, LookupError): - pass - - if changeid in repo._bookmarks: - self._node = repo._bookmarks[changeid] - self._rev = repo.changelog.rev(self._node) - return - if changeid in repo._tagscache.tags: - self._node = repo._tagscache.tags[changeid] - self._rev = repo.changelog.rev(self._node) - return - try: - self._node = repo.branchtip(changeid) - self._rev = repo.changelog.rev(self._node) - return - except error.RepoLookupError: - pass - - self._node = repo.changelog._partialmatch(changeid) - if self._node is not None: - self._rev = repo.changelog.rev(self._node) - return - - # lookup failed - # check if it might have come from damaged dirstate - if changeid in repo.dirstate.parents(): - raise error.Abort(_("working directory has unknown parent '%s'!") - % short(changeid)) - try: - if len(changeid) == 20: - changeid = hex(changeid) - except TypeError: - pass - raise error.RepoLookupError( - _("unknown revision '%s'") % changeid) + self._node = self._repo.changelog.node(changeid) + else: + self._node = self._repo.lookup(changeid) + self._rev = self._repo.changelog.rev(self._node) def __str__(self): return short(self.node()) @@ -134,7 +57,7 @@ class changectx(object): @propertycache def _changeset(self): - return self._repo.changelog.read(self.rev()) + return self._repo.changelog.read(self.node()) @propertycache def _manifest(self): @@ -188,22 +111,14 @@ class changectx(object): return self._changeset[4] def branch(self): return encoding.tolocal(self._changeset[5].get("branch")) - def closesbranch(self): - return 'close' in self._changeset[5] def extra(self): return self._changeset[5] def tags(self): return self._repo.nodetags(self._node) def bookmarks(self): return self._repo.nodebookmarks(self._node) - def phase(self): - return self._repo._phasecache.phase(self._repo, self._rev) - def phasestr(self): - return phases.phasenames[self.phase()] - def mutable(self): - return self.phase() > phases.public def hidden(self): - return self._rev in self._repo.hiddenrevs + return self._rev in self._repo.changelog.hiddenrevs def parents(self): """return contexts for each parent changeset""" @@ -223,48 +138,13 @@ class changectx(object): return [changectx(self._repo, x) for x in c] def ancestors(self): - for a in self._repo.changelog.ancestors([self._rev]): + for a in self._repo.changelog.ancestors(self._rev): yield changectx(self._repo, a) def descendants(self): - for d in self._repo.changelog.descendants([self._rev]): + for d in self._repo.changelog.descendants(self._rev): yield changectx(self._repo, d) - def obsolete(self): - """True if the changeset is obsolete""" - return (self.node() in self._repo.obsstore.precursors - and self.phase() > phases.public) - - def extinct(self): - """True if the changeset is extinct""" - # We should just compute a cache a check againts it. - # see revset implementation for details - # - # But this naive implementation does not require cache - if self.phase() <= phases.public: - return False - if not self.obsolete(): - return False - for desc in self.descendants(): - if not desc.obsolete(): - return False - return True - - def unstable(self): - """True if the changeset is not obsolete but it's ancestor are""" - # We should just compute /(obsolete()::) - obsolete()/ - # and keep it in a cache. - # - # But this naive implementation does not require cache - if self.phase() <= phases.public: - return False - if self.obsolete(): - return False - for anc in self.ancestors(): - if anc.obsolete(): - return True - return False - def _fileinfo(self, path): if '_manifest' in self.__dict__: try: @@ -274,8 +154,7 @@ class changectx(object): _('not found in manifest')) if '_manifestdelta' in self.__dict__ or path in self.files(): if path in self._manifestdelta: - return (self._manifestdelta[path], - self._manifestdelta.flags(path)) + return self._manifestdelta[path], self._manifestdelta.flags(path) node, flag = self._repo.manifest.find(self._changeset[0], path) if not node: raise error.LookupError(self._node, path, @@ -316,15 +195,14 @@ class changectx(object): # follow that here, too fset.discard('.') for fn in self: - if fn in fset: - # specified pattern is the exact name - fset.remove(fn) + for ffn in fset: + # match if the file is the exact name or a directory + if ffn == fn or fn.startswith("%s/" % ffn): + fset.remove(ffn) + break if match(fn): yield fn for fn in sorted(fset): - if fn in self._dirs: - # specified pattern is a directory - continue if match.bad(fn, _('no such file in rev %s') % self) and match(fn): yield fn @@ -347,22 +225,6 @@ class changectx(object): return patch.diff(self._repo, ctx2.node(), self.node(), match=match, opts=diffopts) - @propertycache - def _dirs(self): - dirs = set() - for f in self._manifest: - pos = f.rfind('/') - while pos != -1: - f = f[:pos] - if f in dirs: - break # dirs already contains this and above - dirs.add(f) - pos = f.rfind('/') - return dirs - - def dirs(self): - return self._dirs - class filectx(object): """A filecontext object makes access to data related to a particular filerevision convenient.""" @@ -501,22 +363,12 @@ class filectx(object): def size(self): return self._filelog.size(self._filerev) - def isbinary(self): - try: - return util.binary(self.data()) - except IOError: - return False - def cmp(self, fctx): """compare with other file context returns True if different than fctx. """ - if (fctx._filerev is None - and (self._repo._encodefilterpats - # if file data starts with '\1\n', empty metadata block is - # prepended, which adds 4 bytes to filelog.size(). - or self.size() - 4 == fctx.size()) + if (fctx._filerev is None and self._repo._encodefilterpats or self.size() == fctx.size()): return self._filelog.cmp(self._filenode, fctx.data()) @@ -574,7 +426,7 @@ class filectx(object): return [filectx(self._repo, self._path, fileid=x, filelog=self._filelog) for x in c] - def annotate(self, follow=False, linenumber=None, diffopts=None): + def annotate(self, follow=False, linenumber=None): '''returns a list of tuples of (ctx, line) for each line in the file, where ctx is the filectx of the node where that line was last changed. @@ -601,13 +453,8 @@ class filectx(object): without_linenumber) def pair(parent, child): - blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts, - refine=True) - for (a1, a2, b1, b2), t in blocks: - # Changed blocks ('!') or blocks made only of blank lines ('~') - # belong to the child. - if t == '=': - child[0][b1:b2] = parent[0][a1:a2] + for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]): + child[0][b1:b2] = parent[0][a1:a2] return child getlog = util.lrucachefunc(lambda x: self._repo.file(x)) @@ -672,27 +519,27 @@ class filectx(object): return zip(hist[base][0], hist[base][1].splitlines(True)) - def ancestor(self, fc2, actx): + def ancestor(self, fc2, actx=None): """ find the common ancestor file context, if any, of self, and fc2 - actx must be the changectx of the common ancestor + If actx is given, it must be the changectx of the common ancestor of self's and fc2's respective changesets. """ + if actx is None: + actx = self.changectx().ancestor(fc2.changectx()) + + # the trivial case: changesets are unrelated, files must be too + if not actx: + return None + # the easy case: no (relevant) renames if fc2.path() == self.path() and self.path() in actx: return actx[self.path()] - - # the next easiest cases: unambiguous predecessor (name trumps - # history) - if self.path() in actx and fc2.path() not in actx: - return actx[self.path()] - if fc2.path() in actx and self.path() not in actx: - return actx[fc2.path()] + acache = {} # prime the ancestor cache for the working directory - acache = {} for c in (self, fc2): if c._filerev is None: pl = [(n.path(), n.filenode()) for n in c.parents()] @@ -721,26 +568,17 @@ class filectx(object): return None - def ancestors(self, followfirst=False): + def ancestors(self): visit = {} c = self - cut = followfirst and 1 or None while True: - for parent in c.parents()[:cut]: + for parent in c.parents(): visit[(parent.rev(), parent.node())] = parent if not visit: break c = visit.pop(max(visit)) yield c - def copies(self, c2): - if not util.safehasattr(self, "_copycache"): - self._copycache = {} - sc2 = str(c2) - if sc2 not in self._copycache: - self._copycache[sc2] = copies.pathcopies(c2) - return self._copycache[sc2] - class workingctx(changectx): """A workingctx object makes access to data related to the current working directory convenient. @@ -794,47 +632,15 @@ class workingctx(changectx): def __contains__(self, key): return self._repo.dirstate[key] not in "?r" - def _buildflagfunc(self): - # Create a fallback function for getting file flags when the - # filesystem doesn't support them - - copiesget = self._repo.dirstate.copies().get - - if len(self._parents) < 2: - # when we have one parent, it's easy: copy from parent - man = self._parents[0].manifest() - def func(f): - f = copiesget(f, f) - return man.flags(f) - else: - # merges are tricky: we try to reconstruct the unstored - # result from the merge (issue1802) - p1, p2 = self._parents - pa = p1.ancestor(p2) - m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest() - - def func(f): - f = copiesget(f, f) # may be wrong for merges with copies - fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f) - if fl1 == fl2: - return fl1 - if fl1 == fla: - return fl2 - if fl2 == fla: - return fl1 - return '' # punt for conflicts - - return func - - @propertycache - def _flagfunc(self): - return self._repo.dirstate.flagfunc(self._buildflagfunc) - @propertycache def _manifest(self): """generate a manifest corresponding to the working directory""" + if self._unknown is None: + self.status(unknown=True) + man = self._parents[0].manifest().copy() + copied = self._repo.dirstate.copies() if len(self._parents) > 1: man2 = self.p2().manifest() def getman(f): @@ -843,11 +649,13 @@ class workingctx(changectx): return man2 else: getman = lambda f: man - - copied = self._repo.dirstate.copies() - ff = self._flagfunc + def cf(f): + f = copied.get(f, f) + return getman(f).flags(f) + ff = self._repo.dirstate.flagfunc(cf) modified, added, removed, deleted = self._status - for i, l in (("a", added), ("m", modified)): + unknown = self._unknown + for i, l in (("a", added), ("m", modified), ("u", unknown)): for f in l: orig = copied.get(f, f) man[f] = getman(orig).get(orig, nullid) + i @@ -934,8 +742,6 @@ class workingctx(changectx): return self._clean def branch(self): return encoding.tolocal(self._extra['branch']) - def closesbranch(self): - return 'close' in self._extra def extra(self): return self._extra @@ -951,15 +757,6 @@ class workingctx(changectx): b.extend(p.bookmarks()) return b - def phase(self): - phase = phases.draft # default phase to draft - for p in self.parents(): - phase = max(phase, p.phase()) - return phase - - def hidden(self): - return False - def children(self): return [] @@ -970,10 +767,23 @@ class workingctx(changectx): except KeyError: return '' - try: - return self._flagfunc(path) - except OSError: + orig = self._repo.dirstate.copies().get(path, path) + + def findflag(ctx): + mnode = ctx.changeset()[0] + node, flag = self._repo.manifest.find(mnode, orig) + ff = self._repo.dirstate.flagfunc(lambda x: flag or '') + try: + return ff(path) + except OSError: + pass + + flag = findflag(self._parents[0]) + if flag is None and len(self.parents()) > 1: + flag = findflag(self._parents[1]) + if flag is None or self._repo.dirstate[path] == 'r': return '' + return flag def filectx(self, path, filelog=None): """get a file context from the working directory""" @@ -988,15 +798,14 @@ class workingctx(changectx): return sorted(self._repo.dirstate.walk(match, self.substate.keys(), True, False)) - def dirty(self, missing=False, merge=True, branch=True): + def dirty(self, missing=False): "check whether a working directory is modified" # check subrepos first for s in self.substate: if self.sub(s).dirty(): return True # check current working dir - return ((merge and self.p2()) or - (branch and self.branch() != self.p1().branch()) or + return (self.p2() or self.branch() != self.p1().branch() or self.modified() or self.added() or self.removed() or (missing and self.deleted())) @@ -1035,26 +844,22 @@ class workingctx(changectx): finally: wlock.release() - def forget(self, files, prefix=""): - join = lambda f: os.path.join(prefix, f) + def forget(self, files): wlock = self._repo.wlock() try: - rejected = [] for f in files: - if f not in self._repo.dirstate: - self._repo.ui.warn(_("%s not tracked!\n") % join(f)) - rejected.append(f) - elif self._repo.dirstate[f] != 'a': + if self._repo.dirstate[f] != 'a': self._repo.dirstate.remove(f) + elif f not in self._repo.dirstate: + self._repo.ui.warn(_("%s not tracked!\n") % f) else: self._repo.dirstate.drop(f) - return rejected finally: wlock.release() def ancestors(self): for a in self._repo.changelog.ancestors( - [p.rev() for p in self._parents]): + *[p.rev() for p in self._parents]): yield changectx(self._repo, a) def undelete(self, list): @@ -1088,9 +893,6 @@ class workingctx(changectx): finally: wlock.release() - def dirs(self): - return set(self._repo.dirstate.dirs()) - class workingfilectx(filectx): """A workingfilectx object makes access to data related to a particular file in the working directory convenient.""" |