Mercurial > public > mercurial-scm > hg-stable
diff mercurial/context.py @ 6876:077f1e637cd8
Merge with stable
Simplify the copy search algorithm
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Sun, 10 Aug 2008 18:38:43 -0500 |
parents | 54b7c79575fa |
children | 8fee8ff13d37 |
line wrap: on
line diff
--- a/mercurial/context.py Sun Aug 10 18:01:03 2008 -0500 +++ b/mercurial/context.py Sun Aug 10 18:38:43 2008 -0500 @@ -5,35 +5,36 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from node import nullid, nullrev, short +from node import nullid, nullrev, short, hex from i18n import _ import ancestor, bdiff, revlog, util, os, errno class changectx(object): """A changecontext object makes access to data related to a particular changeset convenient.""" - def __init__(self, repo, changeid=None): + def __init__(self, repo, changeid=''): """changeid is a revision number, node, or tag""" + if changeid == '': + changeid = '.' self._repo = repo - - if not changeid and changeid != 0: - p1, p2 = self._repo.dirstate.parents() - self._rev = self._repo.changelog.rev(p1) - if self._rev == -1: - changeid = 'tip' - else: - self._node = p1 - return - self._node = self._repo.lookup(changeid) self._rev = self._repo.changelog.rev(self._node) def __str__(self): return short(self.node()) + def __int__(self): + return self.rev() + def __repr__(self): return "<changectx %s>" % str(self) + def __hash__(self): + try: + return hash(self._rev) + except AttributeError: + return id(self) + def __eq__(self, other): try: return self._rev == other._rev @@ -57,6 +58,12 @@ md = self._repo.manifest.readdelta(self._changeset[0]) self._manifestdelta = md return self._manifestdelta + elif name == '_parents': + p = self._repo.changelog.parents(self._node) + if p[1] == nullid: + p = p[:-1] + self._parents = [changectx(self._repo, x) for x in p] + return self._parents else: raise AttributeError, name @@ -67,9 +74,7 @@ return self.filectx(key) def __iter__(self): - a = self._manifest.keys() - a.sort() - for f in a: + for f in util.sort(self._manifest): yield f def changeset(self): return self._changeset @@ -77,6 +82,7 @@ def rev(self): return self._rev def node(self): return self._node + def hex(self): return hex(self._node) def user(self): return self._changeset[1] def date(self): return self._changeset[2] def files(self): return self._changeset[3] @@ -87,14 +93,21 @@ def parents(self): """return contexts for each parent changeset""" - p = self._repo.changelog.parents(self._node) - return [changectx(self._repo, x) for x in p] + return self._parents def children(self): """return contexts for each child changeset""" c = self._repo.changelog.children(self._node) return [changectx(self._repo, x) for x in c] + def ancestors(self): + 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): + yield changectx(self._repo, d) + def _fileinfo(self, path): if '_manifest' in self.__dict__: try: @@ -115,7 +128,7 @@ def filenode(self, path): return self._fileinfo(path)[0] - def fileflags(self, path): + def flags(self, path): try: return self._fileinfo(path)[1] except revlog.LookupError: @@ -128,15 +141,6 @@ return filectx(self._repo, path, fileid=fileid, changectx=self, filelog=filelog) - def filectxs(self): - """generate a file context for each file in this changeset's - manifest""" - mf = self.manifest() - m = mf.keys() - m.sort() - for f in m: - yield self.filectx(f, fileid=mf[f]) - def ancestor(self, c2): """ return the ancestor context of self and c2 @@ -144,6 +148,23 @@ n = self._repo.changelog.ancestor(self._node, c2._node) return changectx(self._repo, n) + def walk(self, match): + fdict = dict.fromkeys(match.files()) + # for dirstate.walk, files=['.'] means "walk the whole tree". + # follow that here, too + fdict.pop('.', None) + for fn in self: + for ffn in fdict: + # match if the file is the exact name or a directory + if ffn == fn or fn.startswith("%s/" % ffn): + del fdict[ffn] + break + if match(fn): + yield fn + for fn in util.sort(fdict): + if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn): + yield fn + class filectx(object): """A filecontext object makes access to data related to a particular filerevision convenient.""" @@ -210,6 +231,12 @@ def __repr__(self): return "<filectx %s>" % str(self) + def __hash__(self): + try: + return hash((self._path, self._fileid)) + except AttributeError: + return id(self) + def __eq__(self, other): try: return (self._path == other._path @@ -228,9 +255,7 @@ def filerev(self): return self._filerev def filenode(self): return self._filenode - def fileflags(self): return self._changectx.fileflags(self._path) - def isexec(self): return 'x' in self.fileflags() - def islink(self): return 'l' in self.fileflags() + def flags(self): return self._changectx.flags(self._path) def filelog(self): return self._filelog def rev(self): @@ -376,12 +401,11 @@ # sort by revision (per file) which is a topological order visit = [] for f in files: - fn = [(n.rev(), n) for n in needed.keys() if n._path == f] + fn = [(n.rev(), n) for n in needed if n._path == f] visit.extend(fn) - visit.sort() + hist = {} - - for r, f in visit: + for r, f in util.sort(visit): curr = decorate(f.data(), f) for p in parents(f): if p != nullid: @@ -432,11 +456,41 @@ class workingctx(changectx): """A workingctx object makes access to data related to - the current working directory convenient.""" - def __init__(self, repo): + the current working directory convenient. + parents - a pair of parent nodeids, or None to use the dirstate. + date - any valid date string or (unixtime, offset), or None. + user - username string, or None. + extra - a dictionary of extra values, or None. + changes - a list of file lists as returned by localrepo.status() + or None to use the repository status. + """ + def __init__(self, repo, parents=None, text="", user=None, date=None, + extra=None, changes=None): self._repo = repo self._rev = None self._node = None + self._text = text + if date: + self._date = util.parsedate(date) + if user: + self._user = user + if parents: + self._parents = [changectx(self._repo, p) for p in parents] + if changes: + self._status = list(changes) + + self._extra = {} + if extra: + self._extra = extra.copy() + if 'branch' not in self._extra: + branch = self._repo.dirstate.branch() + try: + branch = branch.decode('UTF-8').encode('UTF-8') + except UnicodeDecodeError: + raise util.Abort(_('branch name not in UTF-8!')) + self._extra['branch'] = branch + if self._extra['branch'] == '': + self._extra['branch'] = 'default' def __str__(self): return str(self._parents[0]) + "+" @@ -444,16 +498,28 @@ def __nonzero__(self): return True + def __contains__(self, key): + return self._dirstate[key] not in "?r" + def __getattr__(self, name): - if name == '_parents': - self._parents = self._repo.parents() - return self._parents if name == '_status': - self._status = self._repo.status() + self._status = self._repo.status(unknown=True) return self._status + elif name == '_user': + self._user = self._repo.ui.username() + return self._user + elif name == '_date': + self._date = util.makedate() + return self._date if name == '_manifest': self._buildmanifest() return self._manifest + elif name == '_parents': + p = self._repo.dirstate.parents() + if p[1] == nullid: + p = p[:-1] + self._parents = [changectx(self._repo, x) for x in p] + return self._parents else: raise AttributeError, name @@ -462,16 +528,14 @@ man = self._parents[0].manifest().copy() copied = self._repo.dirstate.copies() - is_exec = util.execfunc(self._repo.root, - lambda p: man.execf(copied.get(p,p))) - is_link = util.linkfunc(self._repo.root, - lambda p: man.linkf(copied.get(p,p))) + cf = lambda x: man.flags(copied.get(x, x)) + ff = self._repo.dirstate.flagfunc(cf) modified, added, removed, deleted, unknown = self._status[:5] for i, l in (("a", added), ("m", modified), ("u", unknown)): for f in l: man[f] = man.get(copied.get(f, f), nullid) + i try: - man.set(f, is_exec(f), is_link(f)) + man.set(f, ff(f)) except OSError: pass @@ -483,13 +547,11 @@ def manifest(self): return self._manifest - def user(self): return self._repo.ui.username() - def date(self): return util.makedate() - def description(self): return "" + def user(self): return self._user or self._repo.ui.username() + def date(self): return self._date + def description(self): return self._text def files(self): - f = self.modified() + self.added() + self.removed() - f.sort() - return f + return util.sort(self._status[0] + self._status[1] + self._status[2]) def modified(self): return self._status[0] def added(self): return self._status[1] @@ -497,21 +559,18 @@ def deleted(self): return self._status[3] def unknown(self): return self._status[4] def clean(self): return self._status[5] - def branch(self): return self._repo.dirstate.branch() + def branch(self): return self._extra['branch'] + def extra(self): return self._extra def tags(self): t = [] [t.extend(p.tags()) for p in self.parents()] return t - def parents(self): - """return contexts for each parent changeset""" - return self._parents - def children(self): return [] - def fileflags(self, path): + def flags(self, path): if '_manifest' in self.__dict__: try: return self._manifest.flags(path) @@ -521,12 +580,9 @@ pnode = self._parents[0].changeset()[0] orig = self._repo.dirstate.copies().get(path, path) node, flag = self._repo.manifest.find(pnode, orig) - is_link = util.linkfunc(self._repo.root, - lambda p: flag and 'l' in flag) - is_exec = util.execfunc(self._repo.root, - lambda p: flag and 'x' in flag) try: - return (is_link(path) and 'l' or '') + (is_exec(path) and 'x' or '') + ff = self._repo.dirstate.flagfunc(lambda x: flag or '') + return ff(path) except OSError: pass @@ -543,6 +599,9 @@ """return the ancestor context of self and c2""" return self._parents[0].ancestor(c2) # punt on two parents for now + def walk(self, match): + return util.sort(self._repo.dirstate.walk(match, True, False).keys()) + class workingfilectx(filectx): """A workingfilectx object makes access to data related to a particular file in the working directory convenient.""" @@ -625,3 +684,92 @@ return (t, tz) def cmp(self, text): return self._repo.wread(self._path) == text + +class memctx(object): + """A memctx is a subset of changectx supposed to be built on memory + and passed to commit functions. + + NOTE: this interface and the related memfilectx are experimental and + may change without notice. + + parents - a pair of parent nodeids. + filectxfn - a callable taking (repo, memctx, path) arguments and + returning a memctx object. + date - any valid date string or (unixtime, offset), or None. + user - username string, or None. + extra - a dictionary of extra values, or None. + """ + def __init__(self, repo, parents, text, files, filectxfn, user=None, + date=None, extra=None): + self._repo = repo + self._rev = None + self._node = None + self._text = text + self._date = date and util.parsedate(date) or util.makedate() + self._user = user + parents = [(p or nullid) for p in parents] + p1, p2 = parents + self._parents = [changectx(self._repo, p) for p in (p1, p2)] + files = util.sort(list(files)) + self._status = [files, [], [], [], []] + self._filectxfn = filectxfn + + self._extra = extra and extra.copy() or {} + if 'branch' not in self._extra: + self._extra['branch'] = 'default' + elif self._extra.get('branch') == '': + self._extra['branch'] = 'default' + + def __str__(self): + return str(self._parents[0]) + "+" + + def __int__(self): + return self._rev + + def __nonzero__(self): + return True + + def user(self): return self._user or self._repo.ui.username() + def date(self): return self._date + def description(self): return self._text + def files(self): return self.modified() + def modified(self): return self._status[0] + def added(self): return self._status[1] + def removed(self): return self._status[2] + def deleted(self): return self._status[3] + def unknown(self): return self._status[4] + def clean(self): return self._status[5] + def branch(self): return self._extra['branch'] + def extra(self): return self._extra + def flags(self, f): return self[f].flags() + + def parents(self): + """return contexts for each parent changeset""" + return self._parents + + def filectx(self, path, filelog=None): + """get a file context from the working directory""" + return self._filectxfn(self._repo, self, path) + +class memfilectx(object): + """A memfilectx is a subset of filectx supposed to be built by client + code and passed to commit functions. + """ + def __init__(self, path, data, islink, isexec, copied): + """copied is the source file path, or None.""" + self._path = path + self._data = data + self._flags = (islink and 'l' or '') + (isexec and 'x' or '') + self._copied = None + if copied: + self._copied = (copied, nullid) + + def __nonzero__(self): return True + def __str__(self): return "%s@%s" % (self.path(), self._changectx) + def path(self): return self._path + def data(self): return self._data + def flags(self): return self._flags + def isexec(self): return 'x' in self._flags + def islink(self): return 'l' in self._flags + def renamed(self): return self._copied +