diff options
Diffstat (limited to 'eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/subrepo.py')
-rw-r--r-- | eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/subrepo.py | 610 |
1 files changed, 0 insertions, 610 deletions
diff --git a/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/subrepo.py b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/subrepo.py deleted file mode 100644 index 4761dca..0000000 --- a/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/subrepo.py +++ /dev/null @@ -1,610 +0,0 @@ -# subrepo.py - sub-repository handling for Mercurial -# -# Copyright 2009-2010 Matt Mackall <mpm@selenic.com> -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath -import stat, subprocess -from i18n import _ -import config, util, node, error, cmdutil -hg = None - -nullstate = ('', '', 'empty') - -def state(ctx, ui): - """return a state dict, mapping subrepo paths configured in .hgsub - to tuple: (source from .hgsub, revision from .hgsubstate, kind - (key in types dict)) - """ - p = config.config() - def read(f, sections=None, remap=None): - if f in ctx: - try: - data = ctx[f].data() - except IOError, err: - if err.errno != errno.ENOENT: - raise - # handle missing subrepo spec files as removed - ui.warn(_("warning: subrepo spec file %s not found\n") % f) - return - p.parse(f, data, sections, remap, read) - else: - raise util.Abort(_("subrepo spec file %s not found") % f) - - if '.hgsub' in ctx: - read('.hgsub') - - for path, src in ui.configitems('subpaths'): - p.set('subpaths', path, src, ui.configsource('subpaths', path)) - - rev = {} - if '.hgsubstate' in ctx: - try: - for l in ctx['.hgsubstate'].data().splitlines(): - revision, path = l.split(" ", 1) - rev[path] = revision - except IOError, err: - if err.errno != errno.ENOENT: - raise - - state = {} - for path, src in p[''].items(): - kind = 'hg' - if src.startswith('['): - if ']' not in src: - raise util.Abort(_('missing ] in subrepo source')) - kind, src = src.split(']', 1) - kind = kind[1:] - - for pattern, repl in p.items('subpaths'): - # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub - # does a string decode. - repl = repl.encode('string-escape') - # However, we still want to allow back references to go - # through unharmed, so we turn r'\\1' into r'\1'. Again, - # extra escapes are needed because re.sub string decodes. - repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl) - try: - src = re.sub(pattern, repl, src, 1) - except re.error, e: - raise util.Abort(_("bad subrepository pattern in %s: %s") - % (p.source('subpaths', pattern), e)) - - state[path] = (src.strip(), rev.get(path, ''), kind) - - return state - -def writestate(repo, state): - """rewrite .hgsubstate in (outer) repo with these subrepo states""" - repo.wwrite('.hgsubstate', - ''.join(['%s %s\n' % (state[s][1], s) - for s in sorted(state)]), '') - -def submerge(repo, wctx, mctx, actx): - """delegated from merge.applyupdates: merging of .hgsubstate file - in working context, merging context and ancestor context""" - if mctx == actx: # backwards? - actx = wctx.p1() - s1 = wctx.substate - s2 = mctx.substate - sa = actx.substate - sm = {} - - repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx)) - - def debug(s, msg, r=""): - if r: - r = "%s:%s:%s" % r - repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r)) - - for s, l in s1.items(): - a = sa.get(s, nullstate) - ld = l # local state with possible dirty flag for compares - if wctx.sub(s).dirty(): - ld = (l[0], l[1] + "+") - if wctx == actx: # overwrite - a = ld - - if s in s2: - r = s2[s] - if ld == r or r == a: # no change or local is newer - sm[s] = l - continue - elif ld == a: # other side changed - debug(s, "other changed, get", r) - wctx.sub(s).get(r) - sm[s] = r - elif ld[0] != r[0]: # sources differ - if repo.ui.promptchoice( - _(' subrepository sources for %s differ\n' - 'use (l)ocal source (%s) or (r)emote source (%s)?') - % (s, l[0], r[0]), - (_('&Local'), _('&Remote')), 0): - debug(s, "prompt changed, get", r) - wctx.sub(s).get(r) - sm[s] = r - elif ld[1] == a[1]: # local side is unchanged - debug(s, "other side changed, get", r) - wctx.sub(s).get(r) - sm[s] = r - else: - debug(s, "both sides changed, merge with", r) - wctx.sub(s).merge(r) - sm[s] = l - elif ld == a: # remote removed, local unchanged - debug(s, "remote removed, remove") - wctx.sub(s).remove() - else: - if repo.ui.promptchoice( - _(' local changed subrepository %s which remote removed\n' - 'use (c)hanged version or (d)elete?') % s, - (_('&Changed'), _('&Delete')), 0): - debug(s, "prompt remove") - wctx.sub(s).remove() - - for s, r in s2.items(): - if s in s1: - continue - elif s not in sa: - debug(s, "remote added, get", r) - mctx.sub(s).get(r) - sm[s] = r - elif r != sa[s]: - if repo.ui.promptchoice( - _(' remote changed subrepository %s which local removed\n' - 'use (c)hanged version or (d)elete?') % s, - (_('&Changed'), _('&Delete')), 0) == 0: - debug(s, "prompt recreate", r) - wctx.sub(s).get(r) - sm[s] = r - - # record merged .hgsubstate - writestate(repo, sm) - -def reporelpath(repo): - """return path to this (sub)repo as seen from outermost repo""" - parent = repo - while hasattr(parent, '_subparent'): - parent = parent._subparent - return repo.root[len(parent.root)+1:] - -def subrelpath(sub): - """return path to this subrepo as seen from outermost repo""" - if not hasattr(sub, '_repo'): - return sub._path - return reporelpath(sub._repo) - -def _abssource(repo, push=False, abort=True): - """return pull/push path of repo - either based on parent repo .hgsub info - or on the top repo config. Abort or return None if no source found.""" - if hasattr(repo, '_subparent'): - source = repo._subsource - if source.startswith('/') or '://' in source: - return source - parent = _abssource(repo._subparent, push, abort=False) - if parent: - if '://' in parent: - if parent[-1] == '/': - parent = parent[:-1] - r = urlparse.urlparse(parent + '/' + source) - r = urlparse.urlunparse((r[0], r[1], - posixpath.normpath(r[2]), - r[3], r[4], r[5])) - return r - else: # plain file system path - return posixpath.normpath(os.path.join(parent, repo._subsource)) - else: # recursion reached top repo - if hasattr(repo, '_subtoppath'): - return repo._subtoppath - if push and repo.ui.config('paths', 'default-push'): - return repo.ui.config('paths', 'default-push') - if repo.ui.config('paths', 'default'): - return repo.ui.config('paths', 'default') - if abort: - raise util.Abort(_("default path for subrepository %s not found") % - reporelpath(repo)) - -def itersubrepos(ctx1, ctx2): - """find subrepos in ctx1 or ctx2""" - # Create a (subpath, ctx) mapping where we prefer subpaths from - # ctx1. The subpaths from ctx2 are important when the .hgsub file - # has been modified (in ctx2) but not yet committed (in ctx1). - subpaths = dict.fromkeys(ctx2.substate, ctx2) - subpaths.update(dict.fromkeys(ctx1.substate, ctx1)) - for subpath, ctx in sorted(subpaths.iteritems()): - yield subpath, ctx.sub(subpath) - -def subrepo(ctx, path): - """return instance of the right subrepo class for subrepo in path""" - # subrepo inherently violates our import layering rules - # because it wants to make repo objects from deep inside the stack - # so we manually delay the circular imports to not break - # scripts that don't use our demand-loading - global hg - import hg as h - hg = h - - util.path_auditor(ctx._repo.root)(path) - state = ctx.substate.get(path, nullstate) - if state[2] not in types: - raise util.Abort(_('unknown subrepo type %s') % state[2]) - return types[state[2]](ctx, path, state[:2]) - -# subrepo classes need to implement the following abstract class: - -class abstractsubrepo(object): - - def dirty(self): - """returns true if the dirstate of the subrepo does not match - current stored state - """ - raise NotImplementedError - - def checknested(self, path): - """check if path is a subrepository within this repository""" - return False - - def commit(self, text, user, date): - """commit the current changes to the subrepo with the given - log message. Use given user and date if possible. Return the - new state of the subrepo. - """ - raise NotImplementedError - - def remove(self): - """remove the subrepo - - (should verify the dirstate is not dirty first) - """ - raise NotImplementedError - - def get(self, state): - """run whatever commands are needed to put the subrepo into - this state - """ - raise NotImplementedError - - def merge(self, state): - """merge currently-saved state with the new state.""" - raise NotImplementedError - - def push(self, force): - """perform whatever action is analogous to 'hg push' - - This may be a no-op on some systems. - """ - raise NotImplementedError - - def add(self, ui, match, dryrun, prefix): - return [] - - def status(self, rev2, **opts): - return [], [], [], [], [], [], [] - - def diff(self, diffopts, node2, match, prefix, **opts): - pass - - def outgoing(self, ui, dest, opts): - return 1 - - def incoming(self, ui, source, opts): - return 1 - - def files(self): - """return filename iterator""" - raise NotImplementedError - - def filedata(self, name): - """return file data""" - raise NotImplementedError - - def fileflags(self, name): - """return file flags""" - return '' - - def archive(self, archiver, prefix): - for name in self.files(): - flags = self.fileflags(name) - mode = 'x' in flags and 0755 or 0644 - symlink = 'l' in flags - archiver.addfile(os.path.join(prefix, self._path, name), - mode, symlink, self.filedata(name)) - - -class hgsubrepo(abstractsubrepo): - def __init__(self, ctx, path, state): - self._path = path - self._state = state - r = ctx._repo - root = r.wjoin(path) - create = False - if not os.path.exists(os.path.join(root, '.hg')): - create = True - util.makedirs(root) - self._repo = hg.repository(r.ui, root, create=create) - self._repo._subparent = r - self._repo._subsource = state[0] - - if create: - fp = self._repo.opener("hgrc", "w", text=True) - fp.write('[paths]\n') - - def addpathconfig(key, value): - if value: - fp.write('%s = %s\n' % (key, value)) - self._repo.ui.setconfig('paths', key, value) - - defpath = _abssource(self._repo, abort=False) - defpushpath = _abssource(self._repo, True, abort=False) - addpathconfig('default', defpath) - if defpath != defpushpath: - addpathconfig('default-push', defpushpath) - fp.close() - - def add(self, ui, match, dryrun, prefix): - return cmdutil.add(ui, self._repo, match, dryrun, True, - os.path.join(prefix, self._path)) - - def status(self, rev2, **opts): - try: - rev1 = self._state[1] - ctx1 = self._repo[rev1] - ctx2 = self._repo[rev2] - return self._repo.status(ctx1, ctx2, **opts) - except error.RepoLookupError, inst: - self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n') - % (inst, subrelpath(self))) - return [], [], [], [], [], [], [] - - def diff(self, diffopts, node2, match, prefix, **opts): - try: - node1 = node.bin(self._state[1]) - # We currently expect node2 to come from substate and be - # in hex format - if node2 is not None: - node2 = node.bin(node2) - cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts, - node1, node2, match, - prefix=os.path.join(prefix, self._path), - listsubrepos=True, **opts) - except error.RepoLookupError, inst: - self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n') - % (inst, subrelpath(self))) - - def archive(self, archiver, prefix): - abstractsubrepo.archive(self, archiver, prefix) - - rev = self._state[1] - ctx = self._repo[rev] - for subpath in ctx.substate: - s = subrepo(ctx, subpath) - s.archive(archiver, os.path.join(prefix, self._path)) - - def dirty(self): - r = self._state[1] - if r == '': - return True - w = self._repo[None] - if w.p1() != self._repo[r]: # version checked out change - return True - return w.dirty() # working directory changed - - def checknested(self, path): - return self._repo._checknested(self._repo.wjoin(path)) - - def commit(self, text, user, date): - self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self)) - n = self._repo.commit(text, user, date) - if not n: - return self._repo['.'].hex() # different version checked out - return node.hex(n) - - def remove(self): - # we can't fully delete the repository as it may contain - # local-only history - self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self)) - hg.clean(self._repo, node.nullid, False) - - def _get(self, state): - source, revision, kind = state - try: - self._repo.lookup(revision) - except error.RepoError: - self._repo._subsource = source - srcurl = _abssource(self._repo) - self._repo.ui.status(_('pulling subrepo %s from %s\n') - % (subrelpath(self), srcurl)) - other = hg.repository(self._repo.ui, srcurl) - self._repo.pull(other) - - def get(self, state): - self._get(state) - source, revision, kind = state - self._repo.ui.debug("getting subrepo %s\n" % self._path) - hg.clean(self._repo, revision, False) - - def merge(self, state): - self._get(state) - cur = self._repo['.'] - dst = self._repo[state[1]] - anc = dst.ancestor(cur) - if anc == cur: - self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self)) - hg.update(self._repo, state[1]) - elif anc == dst: - self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self)) - else: - self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self)) - hg.merge(self._repo, state[1], remind=False) - - def push(self, force): - # push subrepos depth-first for coherent ordering - c = self._repo[''] - subs = c.substate # only repos that are committed - for s in sorted(subs): - if not c.sub(s).push(force): - return False - - dsturl = _abssource(self._repo, True) - self._repo.ui.status(_('pushing subrepo %s to %s\n') % - (subrelpath(self), dsturl)) - other = hg.repository(self._repo.ui, dsturl) - return self._repo.push(other, force) - - def outgoing(self, ui, dest, opts): - return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts) - - def incoming(self, ui, source, opts): - return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts) - - def files(self): - rev = self._state[1] - ctx = self._repo[rev] - return ctx.manifest() - - def filedata(self, name): - rev = self._state[1] - return self._repo[rev][name].data() - - def fileflags(self, name): - rev = self._state[1] - ctx = self._repo[rev] - return ctx.flags(name) - - -class svnsubrepo(abstractsubrepo): - def __init__(self, ctx, path, state): - self._path = path - self._state = state - self._ctx = ctx - self._ui = ctx._repo.ui - - def _svncommand(self, commands, filename=''): - path = os.path.join(self._ctx._repo.origroot, self._path, filename) - cmd = ['svn'] + commands + [path] - cmd = [util.shellquote(arg) for arg in cmd] - cmd = util.quotecommand(' '.join(cmd)) - env = dict(os.environ) - # Avoid localized output, preserve current locale for everything else. - env['LC_MESSAGES'] = 'C' - p = subprocess.Popen(cmd, shell=True, bufsize=-1, - close_fds=util.closefds, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True, env=env) - stdout, stderr = p.communicate() - stderr = stderr.strip() - if stderr: - raise util.Abort(stderr) - return stdout - - def _wcrev(self): - output = self._svncommand(['info', '--xml']) - doc = xml.dom.minidom.parseString(output) - entries = doc.getElementsByTagName('entry') - if not entries: - return '0' - return str(entries[0].getAttribute('revision')) or '0' - - def _wcchanged(self): - """Return (changes, extchanges) where changes is True - if the working directory was changed, and extchanges is - True if any of these changes concern an external entry. - """ - output = self._svncommand(['status', '--xml']) - externals, changes = [], [] - doc = xml.dom.minidom.parseString(output) - for e in doc.getElementsByTagName('entry'): - s = e.getElementsByTagName('wc-status') - if not s: - continue - item = s[0].getAttribute('item') - props = s[0].getAttribute('props') - path = e.getAttribute('path') - if item == 'external': - externals.append(path) - if (item not in ('', 'normal', 'unversioned', 'external') - or props not in ('', 'none')): - changes.append(path) - for path in changes: - for ext in externals: - if path == ext or path.startswith(ext + os.sep): - return True, True - return bool(changes), False - - def dirty(self): - if self._wcrev() == self._state[1] and not self._wcchanged()[0]: - return False - return True - - def commit(self, text, user, date): - # user and date are out of our hands since svn is centralized - changed, extchanged = self._wcchanged() - if not changed: - return self._wcrev() - if extchanged: - # Do not try to commit externals - raise util.Abort(_('cannot commit svn externals')) - commitinfo = self._svncommand(['commit', '-m', text]) - self._ui.status(commitinfo) - newrev = re.search('Committed revision ([0-9]+).', commitinfo) - if not newrev: - raise util.Abort(commitinfo.splitlines()[-1]) - newrev = newrev.groups()[0] - self._ui.status(self._svncommand(['update', '-r', newrev])) - return newrev - - def remove(self): - if self.dirty(): - self._ui.warn(_('not removing repo %s because ' - 'it has changes.\n' % self._path)) - return - self._ui.note(_('removing subrepo %s\n') % self._path) - - def onerror(function, path, excinfo): - if function is not os.remove: - raise - # read-only files cannot be unlinked under Windows - s = os.stat(path) - if (s.st_mode & stat.S_IWRITE) != 0: - raise - os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE) - os.remove(path) - - path = self._ctx._repo.wjoin(self._path) - shutil.rmtree(path, onerror=onerror) - try: - os.removedirs(os.path.dirname(path)) - except OSError: - pass - - def get(self, state): - status = self._svncommand(['checkout', state[0], '--revision', state[1]]) - if not re.search('Checked out revision [0-9]+.', status): - raise util.Abort(status.splitlines()[-1]) - self._ui.status(status) - - def merge(self, state): - old = int(self._state[1]) - new = int(state[1]) - if new > old: - self.get(state) - - def push(self, force): - # push is a no-op for SVN - return True - - def files(self): - output = self._svncommand(['list']) - # This works because svn forbids \n in filenames. - return output.splitlines() - - def filedata(self, name): - return self._svncommand(['cat'], name) - - -types = { - 'hg': hgsubrepo, - 'svn': svnsubrepo, - } |