diff -r 5ff1fc726848 -r c6bca38c1cbf eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/context.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/context.py Sat Jan 08 11:20:57 2011 +0530 @@ -0,0 +1,1098 @@ +# context.py - changeset and file context objects for mercurial +# +# Copyright 2006, 2007 Matt Mackall +# +# 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 +from i18n import _ +import ancestor, bdiff, error, util, subrepo, patch +import os, errno, stat + +propertycache = util.propertycache + +class changectx(object): + """A changecontext object makes access to data related to a particular + changeset convenient.""" + def __init__(self, repo, changeid=''): + """changeid is a revision number, node, or tag""" + if changeid == '': + changeid = '.' + self._repo = repo + if isinstance(changeid, (long, int)): + self._rev = 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()) + + def __int__(self): + return self.rev() + + def __repr__(self): + return "" % 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 + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def __nonzero__(self): + return self._rev != nullrev + + @propertycache + def _changeset(self): + return self._repo.changelog.read(self.node()) + + @propertycache + def _manifest(self): + return self._repo.manifest.read(self._changeset[0]) + + @propertycache + def _manifestdelta(self): + return self._repo.manifest.readdelta(self._changeset[0]) + + @propertycache + def _parents(self): + p = self._repo.changelog.parentrevs(self._rev) + if p[1] == nullrev: + p = p[:-1] + return [changectx(self._repo, x) for x in p] + + @propertycache + def substate(self): + return subrepo.state(self, self._repo.ui) + + def __contains__(self, key): + return key in self._manifest + + def __getitem__(self, key): + return self.filectx(key) + + def __iter__(self): + for f in sorted(self._manifest): + yield f + + def changeset(self): + return self._changeset + def manifest(self): + return self._manifest + def manifestnode(self): + return self._changeset[0] + + 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] + def description(self): + return self._changeset[4] + def branch(self): + return self._changeset[5].get("branch") + def extra(self): + return self._changeset[5] + def tags(self): + return self._repo.nodetags(self._node) + + def parents(self): + """return contexts for each parent changeset""" + return self._parents + + def p1(self): + return self._parents[0] + + def p2(self): + if len(self._parents) == 2: + return self._parents[1] + return changectx(self._repo, -1) + + 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: + return self._manifest[path], self._manifest.flags(path) + except KeyError: + raise error.LookupError(self._node, path, + _('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) + node, flag = self._repo.manifest.find(self._changeset[0], path) + if not node: + raise error.LookupError(self._node, path, + _('not found in manifest')) + + return node, flag + + def filenode(self, path): + return self._fileinfo(path)[0] + + def flags(self, path): + try: + return self._fileinfo(path)[1] + except error.LookupError: + return '' + + def filectx(self, path, fileid=None, filelog=None): + """get a file context from this changeset""" + if fileid is None: + fileid = self.filenode(path) + return filectx(self._repo, path, fileid=fileid, + changectx=self, filelog=filelog) + + def ancestor(self, c2): + """ + return the ancestor context of self and c2 + """ + # deal with workingctxs + n2 = c2._node + if n2 == None: + n2 = c2._parents[0]._node + n = self._repo.changelog.ancestor(self._node, n2) + return changectx(self._repo, n) + + def walk(self, match): + fset = set(match.files()) + # for dirstate.walk, files=['.'] means "walk the whole tree". + # follow that here, too + fset.discard('.') + for fn in self: + 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 match.bad(fn, _('no such file in rev %s') % self) and match(fn): + yield fn + + def sub(self, path): + return subrepo.subrepo(self, path) + + def diff(self, ctx2=None, match=None, **opts): + """Returns a diff generator for the given contexts and matcher""" + if ctx2 is None: + ctx2 = self.p1() + if ctx2 is not None and not isinstance(ctx2, changectx): + ctx2 = self._repo[ctx2] + diffopts = patch.diffopts(self._repo.ui, opts) + return patch.diff(self._repo, ctx2.node(), self.node(), + match=match, opts=diffopts) + +class filectx(object): + """A filecontext object makes access to data related to a particular + filerevision convenient.""" + def __init__(self, repo, path, changeid=None, fileid=None, + filelog=None, changectx=None): + """changeid can be a changeset revision, node, or tag. + fileid can be a file revision or node.""" + self._repo = repo + self._path = path + + assert (changeid is not None + or fileid is not None + or changectx is not None), \ + ("bad args: changeid=%r, fileid=%r, changectx=%r" + % (changeid, fileid, changectx)) + + if filelog: + self._filelog = filelog + + if changeid is not None: + self._changeid = changeid + if changectx is not None: + self._changectx = changectx + if fileid is not None: + self._fileid = fileid + + @propertycache + def _changectx(self): + return changectx(self._repo, self._changeid) + + @propertycache + def _filelog(self): + return self._repo.file(self._path) + + @propertycache + def _changeid(self): + if '_changectx' in self.__dict__: + return self._changectx.rev() + else: + return self._filelog.linkrev(self._filerev) + + @propertycache + def _filenode(self): + if '_fileid' in self.__dict__: + return self._filelog.lookup(self._fileid) + else: + return self._changectx.filenode(self._path) + + @propertycache + def _filerev(self): + return self._filelog.rev(self._filenode) + + @propertycache + def _repopath(self): + return self._path + + def __nonzero__(self): + try: + self._filenode + return True + except error.LookupError: + # file is missing + return False + + def __str__(self): + return "%s@%s" % (self.path(), short(self.node())) + + def __repr__(self): + return "" % str(self) + + def __hash__(self): + try: + return hash((self._path, self._filenode)) + except AttributeError: + return id(self) + + def __eq__(self, other): + try: + return (self._path == other._path + and self._filenode == other._filenode) + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def filectx(self, fileid): + '''opens an arbitrary revision of the file without + opening a new filelog''' + return filectx(self._repo, self._path, fileid=fileid, + filelog=self._filelog) + + def filerev(self): + return self._filerev + def filenode(self): + return self._filenode + def flags(self): + return self._changectx.flags(self._path) + def filelog(self): + return self._filelog + + def rev(self): + if '_changectx' in self.__dict__: + return self._changectx.rev() + if '_changeid' in self.__dict__: + return self._changectx.rev() + return self._filelog.linkrev(self._filerev) + + def linkrev(self): + return self._filelog.linkrev(self._filerev) + def node(self): + return self._changectx.node() + def hex(self): + return hex(self.node()) + def user(self): + return self._changectx.user() + def date(self): + return self._changectx.date() + def files(self): + return self._changectx.files() + def description(self): + return self._changectx.description() + def branch(self): + return self._changectx.branch() + def extra(self): + return self._changectx.extra() + def manifest(self): + return self._changectx.manifest() + def changectx(self): + return self._changectx + + def data(self): + return self._filelog.read(self._filenode) + def path(self): + return self._path + def size(self): + return self._filelog.size(self._filerev) + + def cmp(self, fctx): + """compare with other file context + + returns True if different than fctx. + """ + if (fctx._filerev is None and self._repo._encodefilterpats + or self.size() == fctx.size()): + return self._filelog.cmp(self._filenode, fctx.data()) + + return True + + def renamed(self): + """check if file was actually renamed in this changeset revision + + If rename logged in file revision, we report copy for changeset only + if file revisions linkrev points back to the changeset in question + or both changeset parents contain different file revisions. + """ + + renamed = self._filelog.renamed(self._filenode) + if not renamed: + return renamed + + if self.rev() == self.linkrev(): + return renamed + + name = self.path() + fnode = self._filenode + for p in self._changectx.parents(): + try: + if fnode == p.filenode(name): + return None + except error.LookupError: + pass + return renamed + + def parents(self): + p = self._path + fl = self._filelog + pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)] + + r = self._filelog.renamed(self._filenode) + if r: + pl[0] = (r[0], r[1], None) + + return [filectx(self._repo, p, fileid=n, filelog=l) + for p, n, l in pl if n != nullid] + + def children(self): + # hard for renames + c = self._filelog.children(self._filenode) + return [filectx(self._repo, self._path, fileid=x, + filelog=self._filelog) for x in c] + + 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. + This returns tuples of ((ctx, linenumber), line) for each line, + if "linenumber" parameter is NOT "None". + In such tuples, linenumber means one at the first appearance + in the managed file. + To reduce annotation cost, + this returns fixed value(False is used) as linenumber, + if "linenumber" parameter is "False".''' + + def decorate_compat(text, rev): + return ([rev] * len(text.splitlines()), text) + + def without_linenumber(text, rev): + return ([(rev, False)] * len(text.splitlines()), text) + + def with_linenumber(text, rev): + size = len(text.splitlines()) + return ([(rev, i) for i in xrange(1, size + 1)], text) + + decorate = (((linenumber is None) and decorate_compat) or + (linenumber and with_linenumber) or + without_linenumber) + + def pair(parent, child): + 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)) + def getctx(path, fileid): + log = path == self._path and self._filelog or getlog(path) + return filectx(self._repo, path, fileid=fileid, filelog=log) + getctx = util.lrucachefunc(getctx) + + def parents(f): + # we want to reuse filectx objects as much as possible + p = f._path + if f._filerev is None: # working dir + pl = [(n.path(), n.filerev()) for n in f.parents()] + else: + pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)] + + if follow: + r = f.renamed() + if r: + pl[0] = (r[0], getlog(r[0]).rev(r[1])) + + return [getctx(p, n) for p, n in pl if n != nullrev] + + # use linkrev to find the first changeset where self appeared + if self.rev() != self.linkrev(): + base = self.filectx(self.filerev()) + else: + base = self + + # find all ancestors + needed = {base: 1} + visit = [base] + files = [base._path] + while visit: + f = visit.pop(0) + for p in parents(f): + if p not in needed: + needed[p] = 1 + visit.append(p) + if p._path not in files: + files.append(p._path) + else: + # count how many times we'll use this + needed[p] += 1 + + # sort by revision (per file) which is a topological order + visit = [] + for f in files: + visit.extend(n for n in needed if n._path == f) + + hist = {} + for f in sorted(visit, key=lambda x: x.rev()): + curr = decorate(f.data(), f) + for p in parents(f): + curr = pair(hist[p], curr) + # trim the history of unneeded revs + needed[p] -= 1 + if not needed[p]: + del hist[p] + hist[f] = curr + + return zip(hist[f][0], hist[f][1].splitlines(True)) + + def ancestor(self, fc2, actx=None): + """ + find the common ancestor file context, if any, of self, and fc2 + + 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()] + acache = {} + + # prime the ancestor cache for the working directory + for c in (self, fc2): + if c._filerev is None: + pl = [(n.path(), n.filenode()) for n in c.parents()] + acache[(c._path, None)] = pl + + flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog} + def parents(vertex): + if vertex in acache: + return acache[vertex] + f, n = vertex + if f not in flcache: + flcache[f] = self._repo.file(f) + fl = flcache[f] + pl = [(f, p) for p in fl.parents(n) if p != nullid] + re = fl.renamed(n) + if re: + pl.append(re) + acache[vertex] = pl + return pl + + a, b = (self._path, self._filenode), (fc2._path, fc2._filenode) + v = ancestor.ancestor(a, b, parents) + if v: + f, n = v + return filectx(self._repo, f, fileid=n, filelog=flcache[f]) + + return None + + def ancestors(self): + seen = set(str(self)) + visit = [self] + while visit: + for parent in visit.pop(0).parents(): + s = str(parent) + if s not in seen: + visit.append(parent) + seen.add(s) + yield parent + +class workingctx(changectx): + """A workingctx object makes access to data related to + the current working directory convenient. + 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, 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 changes: + self._status = list(changes[:4]) + self._unknown = changes[4] + self._ignored = changes[5] + self._clean = changes[6] + else: + self._unknown = None + self._ignored = None + self._clean = None + + 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]) + "+" + + def __nonzero__(self): + return True + + def __contains__(self, key): + return self._repo.dirstate[key] not in "?r" + + @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): + if f in man: + return man + return man2 + else: + getman = lambda f: man + 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 + 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 + try: + man.set(f, ff(f)) + except OSError: + pass + + for f in deleted + removed: + if f in man: + del man[f] + + return man + + @propertycache + def _status(self): + return self._repo.status()[:4] + + @propertycache + def _user(self): + return self._repo.ui.username() + + @propertycache + def _date(self): + return util.makedate() + + @propertycache + def _parents(self): + 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 + + def status(self, ignored=False, clean=False, unknown=False): + """Explicit status query + Unless this method is used to query the working copy status, the + _status property will implicitly read the status using its default + arguments.""" + stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown) + self._unknown = self._ignored = self._clean = None + if unknown: + self._unknown = stat[4] + if ignored: + self._ignored = stat[5] + if clean: + self._clean = stat[6] + self._status = stat[:4] + return stat + + def manifest(self): + return self._manifest + 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 sorted(self._status[0] + self._status[1] + self._status[2]) + + 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): + assert self._unknown is not None # must call status first + return self._unknown + def ignored(self): + assert self._ignored is not None # must call status first + return self._ignored + def clean(self): + assert self._clean is not None # must call status first + return self._clean + 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 children(self): + return [] + + def flags(self, path): + if '_manifest' in self.__dict__: + try: + return self._manifest.flags(path) + except KeyError: + return '' + + 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""" + return workingfilectx(self._repo, path, workingctx=self, + filelog=filelog) + + def ancestor(self, c2): + """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 sorted(self._repo.dirstate.walk(match, self.substate.keys(), + True, False)) + + 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 (self.p2() or self.branch() != self.p1().branch() or + self.modified() or self.added() or self.removed() or + (missing and self.deleted())) + + def add(self, list, prefix=""): + join = lambda f: os.path.join(prefix, f) + wlock = self._repo.wlock() + ui, ds = self._repo.ui, self._repo.dirstate + try: + rejected = [] + for f in list: + p = self._repo.wjoin(f) + try: + st = os.lstat(p) + except: + ui.warn(_("%s does not exist!\n") % join(f)) + rejected.append(f) + continue + if st.st_size > 10000000: + ui.warn(_("%s: up to %d MB of RAM may be required " + "to manage this file\n" + "(use 'hg revert %s' to cancel the " + "pending addition)\n") + % (f, 3 * st.st_size // 1000000, join(f))) + if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)): + ui.warn(_("%s not added: only files and symlinks " + "supported currently\n") % join(f)) + rejected.append(p) + elif ds[f] in 'amn': + ui.warn(_("%s already tracked!\n") % join(f)) + elif ds[f] == 'r': + ds.normallookup(f) + else: + ds.add(f) + return rejected + finally: + wlock.release() + + def forget(self, list): + wlock = self._repo.wlock() + try: + for f in list: + if self._repo.dirstate[f] != 'a': + self._repo.ui.warn(_("%s not added!\n") % f) + else: + self._repo.dirstate.forget(f) + finally: + wlock.release() + + def ancestors(self): + for a in self._repo.changelog.ancestors( + *[p.rev() for p in self._parents]): + yield changectx(self._repo, a) + + def remove(self, list, unlink=False): + if unlink: + for f in list: + try: + util.unlink(self._repo.wjoin(f)) + except OSError, inst: + if inst.errno != errno.ENOENT: + raise + wlock = self._repo.wlock() + try: + for f in list: + if unlink and os.path.lexists(self._repo.wjoin(f)): + self._repo.ui.warn(_("%s still exists!\n") % f) + elif self._repo.dirstate[f] == 'a': + self._repo.dirstate.forget(f) + elif f not in self._repo.dirstate: + self._repo.ui.warn(_("%s not tracked!\n") % f) + else: + self._repo.dirstate.remove(f) + finally: + wlock.release() + + def undelete(self, list): + pctxs = self.parents() + wlock = self._repo.wlock() + try: + for f in list: + if self._repo.dirstate[f] != 'r': + self._repo.ui.warn(_("%s not removed!\n") % f) + else: + fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f] + t = fctx.data() + self._repo.wwrite(f, t, fctx.flags()) + self._repo.dirstate.normal(f) + finally: + wlock.release() + + def copy(self, source, dest): + p = self._repo.wjoin(dest) + if not os.path.lexists(p): + self._repo.ui.warn(_("%s does not exist!\n") % dest) + elif not (os.path.isfile(p) or os.path.islink(p)): + self._repo.ui.warn(_("copy failed: %s is not a file or a " + "symbolic link\n") % dest) + else: + wlock = self._repo.wlock() + try: + if self._repo.dirstate[dest] in '?r': + self._repo.dirstate.add(dest) + self._repo.dirstate.copy(source, dest) + finally: + wlock.release() + +class workingfilectx(filectx): + """A workingfilectx object makes access to data related to a particular + file in the working directory convenient.""" + def __init__(self, repo, path, filelog=None, workingctx=None): + """changeid can be a changeset revision, node, or tag. + fileid can be a file revision or node.""" + self._repo = repo + self._path = path + self._changeid = None + self._filerev = self._filenode = None + + if filelog: + self._filelog = filelog + if workingctx: + self._changectx = workingctx + + @propertycache + def _changectx(self): + return workingctx(self._repo) + + def __nonzero__(self): + return True + + def __str__(self): + return "%s@%s" % (self.path(), self._changectx) + + def data(self): + return self._repo.wread(self._path) + def renamed(self): + rp = self._repo.dirstate.copied(self._path) + if not rp: + return None + return rp, self._changectx._parents[0]._manifest.get(rp, nullid) + + def parents(self): + '''return parent filectxs, following copies if necessary''' + def filenode(ctx, path): + return ctx._manifest.get(path, nullid) + + path = self._path + fl = self._filelog + pcl = self._changectx._parents + renamed = self.renamed() + + if renamed: + pl = [renamed + (None,)] + else: + pl = [(path, filenode(pcl[0], path), fl)] + + for pc in pcl[1:]: + pl.append((path, filenode(pc, path), fl)) + + return [filectx(self._repo, p, fileid=n, filelog=l) + for p, n, l in pl if n != nullid] + + def children(self): + return [] + + def size(self): + return os.lstat(self._repo.wjoin(self._path)).st_size + def date(self): + t, tz = self._changectx.date() + try: + return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz) + except OSError, err: + if err.errno != errno.ENOENT: + raise + return (t, tz) + + def cmp(self, fctx): + """compare with other file context + + returns True if different than fctx. + """ + # fctx should be a filectx (not a wfctx) + # invert comparison to reuse the same code path + return fctx.cmp(self) + +class memctx(object): + """Use memctx to perform in-memory commits via localrepo.commitctx(). + + Revision information is supplied at initialization time while + related files data and is made available through a callback + mechanism. 'repo' is the current localrepo, 'parents' is a + sequence of two parent revisions identifiers (pass None for every + missing parent), 'text' is the commit message and 'files' lists + names of files touched by the revision (normalized and relative to + repository root). + + filectxfn(repo, memctx, path) is a callable receiving the + repository, the current memctx object and the normalized path of + requested file, relative to repository root. It is fired by the + commit function for every file in 'files', but calls order is + undefined. If the file is available in the revision being + committed (updated or added), filectxfn returns a memfilectx + object. If the file was removed, filectxfn raises an + IOError. Moved files are represented by marking the source file + removed and the new file added with copy information (see + memfilectx). + + user receives the committer name and defaults to current + repository username, date is the commit date in any format + supported by util.parsedate() and defaults to current date, extra + is a dictionary of metadata or is left empty. + """ + 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 = sorted(set(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 __getitem__(self, key): + return self.filectx(key) + + def p1(self): + return self._parents[0] + def p2(self): + return self._parents[1] + + 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 ignored(self): + return self._status[5] + def clean(self): + return self._status[6] + 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) + + def commit(self): + """commit context to the repo""" + return self._repo.commitctx(self) + +class memfilectx(object): + """memfilectx represents an in-memory file to commit. + + See memctx for more details. + """ + def __init__(self, path, data, islink=False, isexec=False, copied=None): + """ + path is the normalized file path relative to repository root. + data is the file content as a string. + islink is True if the file is a symbolic link. + isexec is True if the file is executable. + copied is the source file path if current file was copied in the + revision being committed, 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