X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/b2d72160e2a68991c66ea6017b871d7f42c0b29d..80a2c67a898b06c9c28bda89d7aa012676c2a23a:/lib/wlrepo/backend_mercurial.py diff --git a/lib/wlrepo/backend_mercurial.py b/lib/wlrepo/backend_mercurial.py index 38f01da6..35e4442e 100644 --- a/lib/wlrepo/backend_mercurial.py +++ b/lib/wlrepo/backend_mercurial.py @@ -1,339 +1,565 @@ -# -*- encoding: utf-8 -*- -__author__ = "Łukasz Rekucki" -__date__ = "$2009-09-18 10:49:24$" - -__doc__ = """RAL implementation over Mercurial""" - -import mercurial -from mercurial import localrepo as hglrepo -from mercurial import ui as hgui -import re -import wlrepo - -FILTER = re.compile(r"^pub_(.+)\.xml$", re.UNICODE) - -def default_filter(name): - m = FILTER.match(name) - if m is not None: - return name, m.group(1) - return None - -class MercurialLibrary(wlrepo.Library): - - def __init__(self, path, maincabinet="default", ** kwargs): - super(wlrepo.Library, self).__init__( ** kwargs) - - self._hgui = hgui.ui() - self._hgui.config('ui', 'quiet', 'true') - self._hgui.config('ui', 'interactive', 'false') - - import os.path - self._ospath = self._sanitize_string(os.path.realpath(path)) - - maincabinet = self._sanitize_string(maincabinet) - - if os.path.isdir(path): - try: - self._hgrepo = hglrepo.localrepository(self._hgui, path) - except mercurial.error.RepoError: - raise wlrepo.LibraryException("[HGLibrary]Not a valid repository at path '%s'." % path) - elif kwargs['create']: - os.makedirs(path) - try: - self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1) - except mercurial.error.RepoError: - raise wlrepo.LibraryException("[HGLibrary] Can't create a repository on path '%s'." % path) - else: - raise wlrepo.LibraryException("[HGLibrary] Can't open a library on path '%s'." % path) - - # fetch the main cabinet - lock = self._hgrepo.lock() - try: - btags = self._hgrepo.branchtags() - - if not self._has_branch(maincabinet): - raise wlrepo.LibraryException("[HGLibrary] No branch named '%s' to init main cabinet" % maincabinet) - - self._maincab = MercurialCabinet(self, maincabinet) - finally: - lock.release() - - @property - def main_cabinet(self): - return self._maincab - - def cabinet(self, docid, user, create=False): - bname = self._bname(user, docid) - - lock = self._lock(True) - try: - if self._has_branch(bname): - return MercurialCabinet(self, bname, doc=docid, user=user) - - if not create: - raise wlrepo.CabinetNotFound(docid, user) - - # check if the docid exists in the main cabinet - needs_touch = not self._maincab.exists(docid) - print "Creating branch: ", needs_touch - cab = MercurialCabinet(self, bname, doc=docid, user=user) - - fileid = cab._fileid(None) - - def cleanup_action(l): - if needs_touch: - print "Touch for file", docid - l._fileopener()(fileid, "w").write('') - l._fileadd(fileid) - - garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)] - print "Garbage: ", garbage - l._filesrm(garbage) - - # create the branch - self._create_branch(bname, before_commit=cleanup_action) - return MercurialCabinet(self, bname, doc=docid, user=user) - finally: - lock.release() - - # - # Private methods - # - - # - # Locking - # - - def _lock(self, write_mode=False): - return self._hgrepo.wlock() # no support for read/write mode yet - - def _transaction(self, write_mode, action): - lock = self._lock(write_mode) - try: - return action(self) - finally: - lock.release() - - # - # Basic repo manipulation - # - - def _checkout(self, rev, force=True): - return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None)) - - def _merge(self, rev): - """ Merge the revision into current working directory """ - return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None)) - - def _common_ancestor(self, revA, revB): - return self._hgrepo[revA].ancestor(self.repo[revB]) - - def _commit(self, message, user="library"): - return self._hgrepo.commit(\ - text=self._sanitize_string(message), \ - user=self._sanitize_string(user)) - - - def _fileexists(self, fileid): - return (fileid in self._hgrepo[None]) - - def _fileadd(self, fileid): - return self._hgrepo.add([fileid]) - - def _filesadd(self, fileid_list): - return self._hgrepo.add(fileid_list) - - def _filerm(self, fileid): - return self._hgrepo.remove([fileid]) - - def _filesrm(self, fileid_list): - return self._hgrepo.remove(fileid_list) - - def _filelist(self, filter=default_filter): - for name in self._hgrepo[None]: - result = filter(name) - if result is None: continue - - yield result - - def _fileopener(self): - return self._hgrepo.wopener - - # - # BASIC BRANCH routines - # - - def _bname(self, user, docid): - """Returns a branch name for a given document and user.""" - docid = self._sanitize_string(docid) - uname = self._sanitize_string(user) - return "personal_" + uname + "_file_" + docid; - - def _has_branch(self, name): - return self._hgrepo.branchmap().has_key(self._sanitize_string(name)) - - def _branch_tip(self, name): - name = self._sanitize_string(name) - return self._hgrepo.branchtags()[name] - - def _create_branch(self, name, parent=None, before_commit=None): - name = self._sanitize_string(name) - - if self._has_branch(name): return # just exit - - if parent is None: - parent = self._maincab - - parentrev = parent._hgtip() - - self._checkout(parentrev) - self._hgrepo.dirstate.setbranch(name) - - if before_commit: before_commit(self) - - print "commiting" - self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library') - - # revert back to main - self._checkout(self._maincab._hgtip()) - return self._branch_tip(name) - - def _switch_to_branch(self, branchname): - current = self._hgrepo[None].branch() - - if current == branchname: - return current # quick exit - - self._checkout(self._branch_tip(branchname)) - return branchname - - # - # Merges - # - - - - # - # Utils - # - - @staticmethod - def _sanitize_string(s): - if isinstance(s, unicode): # - return s.encode('utf-8') - else: # it's a string, so we have no idea what encoding it is - return s - -class MercurialCabinet(wlrepo.Cabinet): - - def __init__(self, library, branchname, doc=None, user=None): - if doc and user: - super(MercurialCabinet, self).__init__(library, doc=doc, user=user) - else: - super(MercurialCabinet, self).__init__(library, name=branchname) - - self._branchname = branchname - - def documents(self): - return self._execute_in_branch(action=lambda l, c: ( e[1] for e in l._filelist()) ) - - def retrieve(self, part=None, shelve=None): - fileid = self._fileid(part) - - if fileid is None: - raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.") - - return self._execute_in_branch(lambda l, c: MercurialDocument(c, fileid)) - - def create(self, name, initial_data=''): - fileid = self._fileid(name) - - if name is None: - raise ValueError("Can't create main doc for maincabinet.") - - def create_action(l, c): - if l._fileexists(fileid): - raise wlrepo.LibraryException("Can't create document '%s' in cabinet '%s' - it already exists" % (fileid, c.name)) - - fd = l._fileopener()(fileid, "w") - fd.write(initial_data) - l._fileadd(fileid) - l._commit("File '%d' created.") - - return MercurialDocument(c, fileid) - - return self._execute_in_branch(create_action) - - def exists(self, part=None, shelve=None): - fileid = self._fileid(part) - - if fileid is None: return false - return self._execute_in_branch(lambda l, c: l._fileexists(fileid)) - - def _execute_in_branch(self, action, write=False): - def switch_action(library): - old = library._switch_to_branch(self._branchname) - try: - return action(library, self) - finally: - library._switch_to_branch(old) - - return self._library._transaction(write_mode=write, action=switch_action) - - def _fileid(self, part): - fileid = None - - if self._maindoc == '': - if part is None: return None - fileid = part - else: - fileid = self._maindoc + (('$' + part) if part else '') - - return 'pub_' + fileid + '.xml' - - def _fileopener(self): - return self._library._fileopener() - - def _hgtip(self): - return self._library._branch_tip(self._branchname) - -class MercurialDocument(wlrepo.Document): - - def __init__(self, cabinet, fileid): - super(MercurialDocument, self).__init__(cabinet, fileid) - self._opener = self._cabinet._fileopener() - - def read(self): - return self._opener(self._name, "r").read() - - def write(self, data): - return self._opener(self._name, "w").write(data) - - -class MergeStatus(object): - def __init__(self, mstatus): - self.updated = mstatus[0] - self.merged = mstatus[1] - self.removed = mstatus[2] - self.unresolved = mstatus[3] - - def isclean(self): - return self.unresolved == 0 - -class UpdateStatus(object): - - def __init__(self, mstatus): - self.modified = mstatus[0] - self.added = mstatus[1] - self.removed = mstatus[2] - self.deleted = mstatus[3] - self.untracked = mstatus[4] - self.ignored = mstatus[5] - self.clean = mstatus[6] - - def has_changes(self): - return bool(len(self.modified) + len(self.added) + \ - len(self.removed) + len(self.deleted)) - - -__all__ = ["MercurialLibrary", "MercurialCabinet", "MercurialDocument"] \ No newline at end of file +## -*- encoding: utf-8 -*- +# +#__author__ = "Łukasz Rekucki" +#__date__ = "$2009-09-18 10:49:24$" +# +#__doc__ = """RAL implementation over Mercurial""" +# +#import mercurial +#from mercurial import localrepo as hglrepo +#from mercurial import ui as hgui +#from mercurial.node import nullid +#import re +#import wlrepo +# +#FILTER = re.compile(r"^pub_(.+)\.xml$", re.UNICODE) +# +#def default_filter(name): +# m = FILTER.match(name) +# if m is not None: +# return name, m.group(1) +# return None +# +#class MercurialLibrary(wlrepo.Library): +# +# def __init__(self, path, maincabinet="default", ** kwargs): +# super(wlrepo.Library, self).__init__( ** kwargs) +# +# self._hgui = hgui.ui() +# self._hgui.config('ui', 'quiet', 'true') +# self._hgui.config('ui', 'interactive', 'false') +# +# import os.path +# self._ospath = self._sanitize_string(os.path.realpath(path)) +# +# maincabinet = self._sanitize_string(maincabinet) +# +# if os.path.isdir(path): +# try: +# self._hgrepo = hglrepo.localrepository(self._hgui, path) +# except mercurial.error.RepoError: +# raise wlrepo.LibraryException("[HGLibrary] Not a valid repository at path '%s'." % path) +# elif kwargs.get('create', False): +# os.makedirs(path) +# try: +# self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1) +# except mercurial.error.RepoError: +# raise wlrepo.LibraryException("[HGLibrary] Can't create a repository on path '%s'." % path) +# else: +# raise wlrepo.LibraryException("[HGLibrary] Can't open a library on path '%s'." % path) +# +# # fetch the main cabinet +# lock = self._hgrepo.lock() +# try: +# btags = self._hgrepo.branchtags() +# +# if not self._has_branch(maincabinet): +# raise wlrepo.LibraryException("[HGLibrary] No branch named '%s' to init main cabinet" % maincabinet) +# +# self._maincab = MercurialCabinet(self, maincabinet) +# finally: +# lock.release() +# +# @property +# def ospath(self): +# return self._ospath +# +# @property +# def main_cabinet(self): +# return self._maincab +# +# def document(self, docid, user, part=None, shelve=None): +# return self.cabinet(docid, user, create=False).retrieve(part=part, shelve=shelve) +# +# def cabinet(self, docid, user, create=False): +# docid = self._sanitize_string(docid) +# user = self._sanitize_string(user) +# +# bname = self._bname(user, docid) +# +# lock = self._lock(True) +# try: +# if self._has_branch(bname): +# return MercurialCabinet(self, doc=docid, user=user) +# +# if not create: +# raise wlrepo.CabinetNotFound(bname) +# +# # check if the docid exists in the main cabinet +# needs_touch = not self._maincab.exists(docid) +# cab = MercurialCabinet(self, doc=docid, user=user) +# +# name, fileid = cab._filename(None) +# +# def cleanup_action(l): +# if needs_touch: +# l._fileopener()(fileid, "w").write('') +# l._fileadd(fileid) +# +# garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)] +# l._filesrm(garbage) +# print "removed: ", garbage +# +# # create the branch +# self._create_branch(bname, before_commit=cleanup_action) +# return MercurialCabinet(self, doc=docid, user=user) +# finally: +# lock.release() +# +# # +# # Private methods +# # +# +# # +# # Locking +# # +# +# def _lock(self, write_mode=False): +# return self._hgrepo.wlock() # no support for read/write mode yet +# +# def _transaction(self, write_mode, action): +# lock = self._lock(write_mode) +# try: +# return action(self) +# finally: +# lock.release() +# +# # +# # Basic repo manipulation +# # +# +# def _checkout(self, rev, force=True): +# return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None)) +# +# def _merge(self, rev): +# """ Merge the revision into current working directory """ +# return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None)) +# +# def _common_ancestor(self, revA, revB): +# return self._hgrepo[revA].ancestor(self.repo[revB]) +# +# def _commit(self, message, user=u"library"): +# return self._hgrepo.commit(text=message, user=user) +# +# +# def _fileexists(self, fileid): +# return (fileid in self._hgrepo[None]) +# +# def _fileadd(self, fileid): +# return self._hgrepo.add([fileid]) +# +# def _filesadd(self, fileid_list): +# return self._hgrepo.add(fileid_list) +# +# def _filerm(self, fileid): +# return self._hgrepo.remove([fileid]) +# +# def _filesrm(self, fileid_list): +# return self._hgrepo.remove(fileid_list) +# +# def _filelist(self, filter=default_filter): +# for name in self._hgrepo[None]: +# result = filter(name) +# if result is None: continue +# +# yield result +# +# def _fileopener(self): +# return self._hgrepo.wopener +# +# def _filectx(self, fileid, branchid): +# return self._hgrepo.filectx(fileid, changeid=branchid) +# +# def _changectx(self, nodeid): +# return self._hgrepo.changectx(nodeid) +# +# # +# # BASIC BRANCH routines +# # +# +# def _bname(self, user, docid): +# """Returns a branch name for a given document and user.""" +# docid = self._sanitize_string(docid) +# uname = self._sanitize_string(user) +# return "personal_" + uname + "_file_" + docid; +# +# def _has_branch(self, name): +# return self._hgrepo.branchmap().has_key(self._sanitize_string(name)) +# +# def _branch_tip(self, name): +# name = self._sanitize_string(name) +# return self._hgrepo.branchtags()[name] +# +# def _create_branch(self, name, parent=None, before_commit=None): +# name = self._sanitize_string(name) +# +# if self._has_branch(name): return # just exit +# +# if parent is None: +# parent = self._maincab +# +# parentrev = parent._hgtip() +# +# self._checkout(parentrev) +# self._hgrepo.dirstate.setbranch(name) +# +# if before_commit: before_commit(self) +# +# self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library') +# +# # revert back to main +# self._checkout(self._maincab._hgtip()) +# return self._branch_tip(name) +# +# def _switch_to_branch(self, branchname): +# current = self._hgrepo[None].branch() +# +# if current == branchname: +# return current # quick exit +# +# self._checkout(self._branch_tip(branchname)) +# return branchname +# +# def shelf(self, nodeid=None): +# if nodeid is None: +# nodeid = self._maincab._name +# return MercurialShelf(self, self._changectx(nodeid)) +# +# +# # +# # Utils +# # +# +# @staticmethod +# def _sanitize_string(s): +# if isinstance(s, unicode): +# s = s.encode('utf-8') +# return s +# +#class MercurialCabinet(wlrepo.Cabinet): +# +# def __init__(self, library, branchname=None, doc=None, user=None): +# if doc and user: +# super(MercurialCabinet, self).__init__(library, doc=doc, user=user) +# self._branchname = library._bname(user=user, docid=doc) +# elif branchname: +# super(MercurialCabinet, self).__init__(library, name=branchname) +# self._branchname = branchname +# else: +# raise ValueError("Provide either doc/user or branchname") +# +# def shelf(self): +# return self._library.shelf(self._branchname) +# +# def parts(self): +# return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist())) +# +# def retrieve(self, part=None, shelf=None): +# name, fileid = self._filename(part) +# +# print "Retrieving document %s from cab %s" % (name, self._name) +# +# if fileid is None: +# raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.") +# +# def retrieve_action(l,c): +# if l._fileexists(fileid): +# return MercurialDocument(c, name=name, fileid=fileid) +# print "File %s not found " % fileid +# return None +# +# return self._execute_in_branch(retrieve_action) +# +# def create(self, name, initial_data): +# name, fileid = self._filename(name) +# +# if name is None: +# raise ValueError("Can't create main doc for maincabinet.") +# +# def create_action(l, c): +# if l._fileexists(fileid): +# raise wlrepo.LibraryException("Can't create document '%s' in cabinet '%s' - it already exists" % (fileid, c.name)) +# +# fd = l._fileopener()(fileid, "w") +# fd.write(initial_data) +# fd.close() +# l._fileadd(fileid) +# l._commit("File '%s' created." % fileid) +# return MercurialDocument(c, fileid=fileid, name=name) +# +# return self._execute_in_branch(create_action) +# +# def exists(self, part=None, shelf=None): +# name, filepath = self._filename(part) +# +# if filepath is None: return False +# return self._execute_in_branch(lambda l, c: l._fileexists(filepath)) +# +# def _execute_in_branch(self, action, write=False): +# def switch_action(library): +# old = library._switch_to_branch(self._branchname) +# try: +# return action(library, self) +# finally: +# library._switch_to_branch(old) +# +# return self._library._transaction(write_mode=write, action=switch_action) +# +# +# def _filename(self, docid): +# return self._partname(docid, 'xml') +# +# def _partname(self, docid, part): +# docid = self._library._sanitize_string(part) +# part = self._library._sanitize_string(part) +# +# if part is None: +# part = 'xml' +# +# if self._maindoc == '' and docid is None: +# return None +# +# return 'pub_' + docid + '.' + part +# +# def _fileopener(self): +# return self._library._fileopener() +# +# def _hgtip(self): +# return self._library._branch_tip(self._branchname) +# +# def _filectx(self, fileid): +# return self._library._filectx(fileid, self._branchname) +# +# def ismain(self): +# return (self._library.main_cabinet == self) +# +#class MercurialDocument(wlrepo.Document): +# +# def __init__(self, cabinet, docid): +# super(MercurialDocument, self).__init__(cabinet, name=docid) +# self._opener = self._cabinet._fileopener() +# self._docid = docid +# self._ctxs = {} +# +# def _ctx(self, part): +# if not self._ctxs.has_key(part): +# self._ctxs[part] = self._cabinet._filectx(self._fileid()) +# return self._ctxs[part] +# +# def _fileid(self, part='xml'): +# return self._cabinet._partname(self._docid, part) +# +# def read(self, part='xml'): +# return self._opener(self._ctx(part).path(), "r").read() +# +# def write(self, data, part='xml'): +# return self._opener(self._ctx(part).path(), "w").write(data) +# +# def commit(self, message, user): +# """Commit all parts of the document.""" +# self.library._fileadd(self._fileid) +# self.library._commit(self._fileid, message, user) +# +# def update(self): +# """Update parts of the document.""" +# lock = self.library._lock() +# try: +# if self._cabinet.ismain(): +# return True # always up-to-date +# +# user = self._cabinet.username or 'library' +# mdoc = self.library.document(self._fileid) +# +# mshelf = mdoc.shelf() +# shelf = self.shelf() +# +# if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf): +# shelf.merge_with(mshelf, user=user) +# +# return True +# finally: +# lock.release() +# +# def share(self, message): +# lock = self.library._lock() +# try: +# print "sharing from", self._cabinet, self._cabinet.username +# +# if self._cabinet.ismain(): +# return True # always shared +# +# if self._cabinet.username is None: +# raise ValueError("Can only share documents from personal cabinets.") +# +# user = self._cabinet.username +# +# main = self.library.shelf() +# local = self.shelf() +# +# no_changes = True +# +# # Case 1: +# # * local +# # | +# # * <- can also be here! +# # /| +# # / | +# # main * * +# # | | +# # The local branch has been recently updated, +# # so we don't need to update yet again, but we need to +# # merge down to default branch, even if there was +# # no commit's since last update +# +# if main.ancestorof(local): +# print "case 1" +# main.merge_with(local, user=user, message=message) +# no_changes = False +# # Case 2: +# # +# # main * * local +# # |\ | +# # | \| +# # | * +# # | | +# # +# # Default has no changes, to update from this branch +# # since the last merge of local to default. +# elif local.has_common_ancestor(main): +# print "case 2" +# if not local.parentof(main): +# main.merge_with(local, user=user, message=message) +# no_changes = False +# +# # Case 3: +# # main * +# # | +# # * <- this case overlaps with previos one +# # |\ +# # | \ +# # | * local +# # | | +# # +# # There was a recent merge to the defaul branch and +# # no changes to local branch recently. +# # +# # Use the fact, that user is prepared to see changes, to +# # update his branch if there are any +# elif local.ancestorof(main): +# print "case 3" +# if not local.parentof(main): +# local.merge_with(main, user=user, message='Local branch update.') +# no_changes = False +# else: +# print "case 4" +# local.merge_with(main, user=user, message='Local branch update.') +# local = self.shelf() +# main.merge_with(local, user=user, message=message) +# +# print "no_changes: ", no_changes +# return no_changes +# finally: +# lock.release() +# +# def shared(self): +# return self.library.main_cabinet.retrieve(self._name) +# +# def exists(self, part='xml'): +# return self._cabinet.exists(self._fileid(part)) +# +# @property +# def size(self): +# return self._filectx.size() +# +# def shelf(self): +# return self._cabinet.shelf() +# +# @property +# def last_modified(self): +# return self._filectx.date() +# +# def __str__(self): +# return u"Document(%s->%s)" % (self._cabinet.name, self._name) +# +# def __eq__(self, other): +# return self._filectx == other._filectx +# +# +# +#class MercurialShelf(wlrepo.Shelf): +# +# def __init__(self, lib, changectx): +# super(MercurialShelf, self).__init__(lib) +# +# if isinstance(changectx, str): +# self._changectx = lib._changectx(changectx) +# else: +# self._changectx = changectx +# +# @property +# def _rev(self): +# return self._changectx.node() +# +# def __str__(self): +# return self._changectx.hex() +# +# def __repr__(self): +# return "MercurialShelf(%s)" % self._changectx.hex() +# +# def ancestorof(self, other): +# nodes = list(other._changectx._parents) +# while nodes[0].node() != nullid: +# v = nodes.pop(0) +# if v == self._changectx: +# return True +# nodes.extend( v._parents ) +# return False +# +# def parentof(self, other): +# return self._changectx in other._changectx._parents +# +# def has_common_ancestor(self, other): +# a = self._changectx.ancestor(other._changectx) +# # print a, self._changectx.branch(), a.branch() +# +# return (a.branch() == self._changectx.branch()) +# +# def merge_with(self, other, user, message): +# lock = self._library._lock(True) +# try: +# self._library._checkout(self._changectx.node()) +# self._library._merge(other._changectx.node()) +# self._library._commit(user=user, message=message) +# finally: +# lock.release() +# +# def __eq__(self, other): +# return self._changectx.node() == other._changectx.node() +# +# +#class MergeStatus(object): +# def __init__(self, mstatus): +# self.updated = mstatus[0] +# self.merged = mstatus[1] +# self.removed = mstatus[2] +# self.unresolved = mstatus[3] +# +# def isclean(self): +# return self.unresolved == 0 +# +#class UpdateStatus(object): +# +# def __init__(self, mstatus): +# self.modified = mstatus[0] +# self.added = mstatus[1] +# self.removed = mstatus[2] +# self.deleted = mstatus[3] +# self.untracked = mstatus[4] +# self.ignored = mstatus[5] +# self.clean = mstatus[6] +# +# def has_changes(self): +# return bool(len(self.modified) + len(self.added) + \ +# len(self.removed) + len(self.deleted)) +# +#__all__ = ["MercurialLibrary"] \ No newline at end of file