1 ## -*- encoding: utf-8 -*-
 
   3 #__author__ = "Ćukasz Rekucki"
 
   4 #__date__ = "$2009-09-18 10:49:24$"
 
   6 #__doc__ = """RAL implementation over Mercurial"""
 
   9 #from mercurial import localrepo as hglrepo
 
  10 #from mercurial import ui as hgui
 
  11 #from mercurial.node import nullid
 
  15 #FILTER = re.compile(r"^pub_(.+)\.xml$", re.UNICODE)
 
  17 #def default_filter(name):
 
  18 #    m = FILTER.match(name)
 
  20 #        return name, m.group(1)
 
  23 #class MercurialLibrary(wlrepo.Library):
 
  25 #    def __init__(self, path, maincabinet="default", ** kwargs):
 
  26 #        super(wlrepo.Library, self).__init__( ** kwargs)
 
  28 #        self._hgui = hgui.ui()
 
  29 #        self._hgui.config('ui', 'quiet', 'true')
 
  30 #        self._hgui.config('ui', 'interactive', 'false')
 
  33 #        self._ospath = self._sanitize_string(os.path.realpath(path))
 
  35 #        maincabinet = self._sanitize_string(maincabinet)
 
  37 #        if os.path.isdir(path):
 
  39 #                self._hgrepo = hglrepo.localrepository(self._hgui, path)
 
  40 #            except mercurial.error.RepoError:
 
  41 #                raise wlrepo.LibraryException("[HGLibrary] Not a valid repository at path '%s'." % path)
 
  42 #        elif kwargs.get('create', False):
 
  45 #                self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1)
 
  46 #            except mercurial.error.RepoError:
 
  47 #                raise wlrepo.LibraryException("[HGLibrary] Can't create a repository on path '%s'." % path)
 
  49 #            raise wlrepo.LibraryException("[HGLibrary] Can't open a library on path '%s'." % path)
 
  51 #        # fetch the main cabinet
 
  52 #        lock = self._hgrepo.lock()
 
  54 #            btags = self._hgrepo.branchtags()
 
  56 #            if not self._has_branch(maincabinet):
 
  57 #                raise wlrepo.LibraryException("[HGLibrary] No branch named '%s' to init main cabinet" % maincabinet)
 
  59 #            self._maincab = MercurialCabinet(self, maincabinet)
 
  68 #    def main_cabinet(self):
 
  69 #        return self._maincab
 
  71 #    def document(self, docid, user, part=None, shelve=None):
 
  72 #        return self.cabinet(docid, user, create=False).retrieve(part=part, shelve=shelve)
 
  74 #    def cabinet(self, docid, user, create=False):
 
  75 #        docid = self._sanitize_string(docid)
 
  76 #        user = self._sanitize_string(user)
 
  78 #        bname = self._bname(user, docid)
 
  80 #        lock = self._lock(True)
 
  82 #            if self._has_branch(bname):
 
  83 #                return MercurialCabinet(self, doc=docid, user=user)
 
  86 #                raise wlrepo.CabinetNotFound(bname)
 
  88 #            # check if the docid exists in the main cabinet
 
  89 #            needs_touch = not self._maincab.exists(docid)
 
  90 #            cab = MercurialCabinet(self, doc=docid, user=user)
 
  92 #            name, fileid = cab._filename(None)
 
  94 #            def cleanup_action(l):
 
  96 #                    l._fileopener()(fileid, "w").write('')
 
  99 #                garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)]
 
 100 #                l._filesrm(garbage)
 
 101 #                print "removed: ", garbage
 
 103 #            # create the branch
 
 104 #            self._create_branch(bname, before_commit=cleanup_action)
 
 105 #            return MercurialCabinet(self, doc=docid, user=user)
 
 117 #    def _lock(self, write_mode=False):
 
 118 #        return self._hgrepo.wlock() # no support for read/write mode yet
 
 120 #    def _transaction(self, write_mode, action):
 
 121 #        lock = self._lock(write_mode)
 
 123 #            return action(self)
 
 128 #    # Basic repo manipulation
 
 131 #    def _checkout(self, rev, force=True):
 
 132 #        return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None))
 
 134 #    def _merge(self, rev):
 
 135 #        """ Merge the revision into current working directory """
 
 136 #        return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None))
 
 138 #    def _common_ancestor(self, revA, revB):
 
 139 #        return self._hgrepo[revA].ancestor(self.repo[revB])
 
 141 #    def _commit(self, message, user=u"library"):
 
 142 #        return self._hgrepo.commit(text=message, user=user)
 
 145 #    def _fileexists(self, fileid):
 
 146 #        return (fileid in self._hgrepo[None])
 
 148 #    def _fileadd(self, fileid):
 
 149 #        return self._hgrepo.add([fileid])
 
 151 #    def _filesadd(self, fileid_list):
 
 152 #        return self._hgrepo.add(fileid_list)
 
 154 #    def _filerm(self, fileid):
 
 155 #        return self._hgrepo.remove([fileid])
 
 157 #    def _filesrm(self, fileid_list):
 
 158 #        return self._hgrepo.remove(fileid_list)
 
 160 #    def _filelist(self, filter=default_filter):
 
 161 #        for name in  self._hgrepo[None]:
 
 162 #            result = filter(name)
 
 163 #            if result is None: continue
 
 167 #    def _fileopener(self):
 
 168 #        return self._hgrepo.wopener
 
 170 #    def _filectx(self, fileid, branchid):
 
 171 #        return self._hgrepo.filectx(fileid, changeid=branchid)
 
 173 #    def _changectx(self, nodeid):
 
 174 #        return self._hgrepo.changectx(nodeid)
 
 177 #    # BASIC BRANCH routines
 
 180 #    def _bname(self, user, docid):
 
 181 #        """Returns a branch name for a given document and user."""
 
 182 #        docid = self._sanitize_string(docid)
 
 183 #        uname = self._sanitize_string(user)
 
 184 #        return "personal_" + uname + "_file_" + docid;
 
 186 #    def _has_branch(self, name):
 
 187 #        return self._hgrepo.branchmap().has_key(self._sanitize_string(name))
 
 189 #    def _branch_tip(self, name):
 
 190 #        name = self._sanitize_string(name)
 
 191 #        return self._hgrepo.branchtags()[name]
 
 193 #    def _create_branch(self, name, parent=None, before_commit=None):
 
 194 #        name = self._sanitize_string(name)
 
 196 #        if self._has_branch(name): return # just exit
 
 199 #            parent = self._maincab
 
 201 #        parentrev = parent._hgtip()
 
 203 #        self._checkout(parentrev)
 
 204 #        self._hgrepo.dirstate.setbranch(name)
 
 206 #        if before_commit: before_commit(self)
 
 208 #        self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library')
 
 210 #        # revert back to main
 
 211 #        self._checkout(self._maincab._hgtip())
 
 212 #        return self._branch_tip(name)
 
 214 #    def _switch_to_branch(self, branchname):
 
 215 #        current = self._hgrepo[None].branch()
 
 217 #        if current == branchname:
 
 218 #            return current # quick exit
 
 220 #        self._checkout(self._branch_tip(branchname))
 
 223 #    def shelf(self, nodeid=None):
 
 225 #            nodeid = self._maincab._name
 
 226 #        return MercurialShelf(self, self._changectx(nodeid))
 
 234 #    def _sanitize_string(s):
 
 235 #        if isinstance(s, unicode):
 
 236 #            s = s.encode('utf-8')
 
 239 #class MercurialCabinet(wlrepo.Cabinet):
 
 241 #    def __init__(self, library, branchname=None, doc=None, user=None):
 
 243 #            super(MercurialCabinet, self).__init__(library, doc=doc, user=user)
 
 244 #            self._branchname = library._bname(user=user, docid=doc)
 
 246 #            super(MercurialCabinet, self).__init__(library, name=branchname)
 
 247 #            self._branchname = branchname
 
 249 #            raise ValueError("Provide either doc/user or branchname")
 
 252 #        return self._library.shelf(self._branchname)
 
 255 #        return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist()))
 
 257 #    def retrieve(self, part=None, shelf=None):
 
 258 #        name, fileid = self._filename(part)
 
 260 #        print "Retrieving document %s from cab %s" % (name, self._name)
 
 263 #            raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.")
 
 265 #        def retrieve_action(l,c):
 
 266 #            if l._fileexists(fileid):
 
 267 #                return MercurialDocument(c, name=name, fileid=fileid)
 
 268 #            print "File %s not found " % fileid
 
 271 #        return self._execute_in_branch(retrieve_action)
 
 273 #    def create(self, name, initial_data):
 
 274 #        name, fileid = self._filename(name)
 
 277 #            raise ValueError("Can't create main doc for maincabinet.")
 
 279 #        def create_action(l, c):
 
 280 #            if l._fileexists(fileid):
 
 281 #                raise wlrepo.LibraryException("Can't create document '%s' in cabinet '%s' - it already exists" % (fileid, c.name))
 
 283 #            fd = l._fileopener()(fileid, "w")
 
 284 #            fd.write(initial_data)
 
 287 #            l._commit("File '%s' created." % fileid)
 
 288 #            return MercurialDocument(c, fileid=fileid, name=name)
 
 290 #        return self._execute_in_branch(create_action)
 
 292 #    def exists(self, part=None, shelf=None):
 
 293 #        name, filepath = self._filename(part)
 
 295 #        if filepath is None: return False
 
 296 #        return self._execute_in_branch(lambda l, c: l._fileexists(filepath))
 
 298 #    def _execute_in_branch(self, action, write=False):
 
 299 #        def switch_action(library):
 
 300 #            old = library._switch_to_branch(self._branchname)
 
 302 #                return action(library, self)
 
 304 #                library._switch_to_branch(old)
 
 306 #        return self._library._transaction(write_mode=write, action=switch_action)
 
 309 #    def _filename(self, docid):
 
 310 #        return self._partname(docid, 'xml')
 
 312 #    def _partname(self, docid, part):
 
 313 #        docid = self._library._sanitize_string(part)
 
 314 #        part = self._library._sanitize_string(part)
 
 319 #        if self._maindoc == '' and docid is None:
 
 322 #        return 'pub_' + docid + '.' + part
 
 324 #    def _fileopener(self):
 
 325 #        return self._library._fileopener()
 
 328 #        return self._library._branch_tip(self._branchname)
 
 330 #    def _filectx(self, fileid):
 
 331 #        return self._library._filectx(fileid, self._branchname)
 
 334 #        return (self._library.main_cabinet == self)
 
 336 #class MercurialDocument(wlrepo.Document):
 
 338 #    def __init__(self, cabinet, docid):
 
 339 #        super(MercurialDocument, self).__init__(cabinet, name=docid)
 
 340 #        self._opener = self._cabinet._fileopener()
 
 341 #        self._docid = docid
 
 344 #    def _ctx(self, part):
 
 345 #        if not self._ctxs.has_key(part):
 
 346 #            self._ctxs[part] = self._cabinet._filectx(self._fileid())
 
 347 #        return self._ctxs[part]
 
 349 #    def _fileid(self, part='xml'):
 
 350 #        return self._cabinet._partname(self._docid, part)
 
 352 #    def read(self, part='xml'):
 
 353 #        return self._opener(self._ctx(part).path(), "r").read()
 
 355 #    def write(self, data, part='xml'):
 
 356 #        return self._opener(self._ctx(part).path(), "w").write(data)
 
 358 #    def commit(self, message, user):
 
 359 #        """Commit all parts of the document."""
 
 360 #        self.library._fileadd(self._fileid)
 
 361 #        self.library._commit(self._fileid, message, user)
 
 364 #        """Update parts of the document."""
 
 365 #        lock = self.library._lock()
 
 367 #            if self._cabinet.ismain():
 
 368 #                return True # always up-to-date
 
 370 #            user = self._cabinet.username or 'library'
 
 371 #            mdoc = self.library.document(self._fileid)
 
 373 #            mshelf = mdoc.shelf()
 
 374 #            shelf = self.shelf()
 
 376 #            if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf):
 
 377 #                shelf.merge_with(mshelf, user=user)
 
 383 #    def share(self, message):
 
 384 #        lock = self.library._lock()
 
 386 #            print "sharing from", self._cabinet, self._cabinet.username
 
 388 #            if self._cabinet.ismain():
 
 389 #                return True # always shared
 
 391 #            if self._cabinet.username is None:
 
 392 #                raise ValueError("Can only share documents from personal cabinets.")
 
 394 #            user = self._cabinet.username
 
 396 #            main = self.library.shelf()
 
 397 #            local = self.shelf()
 
 404 #            #         * <- can also be here!
 
 409 #            # The local branch has been recently updated,
 
 410 #            # so we don't need to update yet again, but we need to
 
 411 #            # merge down to default branch, even if there was
 
 412 #            # no commit's since last update
 
 414 #            if main.ancestorof(local):
 
 416 #                main.merge_with(local, user=user, message=message)
 
 426 #            # Default has no changes, to update from this branch
 
 427 #            # since the last merge of local to default.
 
 428 #            elif local.has_common_ancestor(main):
 
 430 #                if not local.parentof(main):
 
 431 #                    main.merge_with(local, user=user, message=message)
 
 437 #            #      * <- this case overlaps with previos one
 
 443 #            # There was a recent merge to the defaul branch and
 
 444 #            # no changes to local branch recently.
 
 446 #            # Use the fact, that user is prepared to see changes, to
 
 447 #            # update his branch if there are any
 
 448 #            elif local.ancestorof(main):
 
 450 #                if not local.parentof(main):
 
 451 #                    local.merge_with(main, user=user, message='Local branch update.')
 
 455 #                local.merge_with(main, user=user, message='Local branch update.')
 
 456 #                local = self.shelf()
 
 457 #                main.merge_with(local, user=user, message=message)
 
 459 #            print "no_changes: ", no_changes
 
 465 #        return self.library.main_cabinet.retrieve(self._name)
 
 467 #    def exists(self, part='xml'):
 
 468 #        return self._cabinet.exists(self._fileid(part))
 
 472 #        return self._filectx.size()
 
 475 #        return self._cabinet.shelf()
 
 478 #    def last_modified(self):
 
 479 #        return self._filectx.date()
 
 482 #        return u"Document(%s->%s)" % (self._cabinet.name, self._name)
 
 484 #    def __eq__(self, other):
 
 485 #        return self._filectx == other._filectx
 
 489 #class MercurialShelf(wlrepo.Shelf):
 
 491 #    def __init__(self, lib, changectx):
 
 492 #        super(MercurialShelf, self).__init__(lib)
 
 494 #        if isinstance(changectx, str):
 
 495 #            self._changectx = lib._changectx(changectx)
 
 497 #            self._changectx = changectx
 
 501 #        return self._changectx.node()
 
 504 #        return self._changectx.hex()
 
 506 #    def __repr__(self):
 
 507 #        return "MercurialShelf(%s)" % self._changectx.hex()
 
 509 #    def ancestorof(self, other):
 
 510 #        nodes = list(other._changectx._parents)
 
 511 #        while nodes[0].node() != nullid:
 
 513 #            if v == self._changectx:
 
 515 #            nodes.extend( v._parents )
 
 518 #    def parentof(self, other):
 
 519 #        return self._changectx in other._changectx._parents
 
 521 #    def has_common_ancestor(self, other):
 
 522 #        a = self._changectx.ancestor(other._changectx)
 
 523 #        # print a, self._changectx.branch(), a.branch()
 
 525 #        return (a.branch() == self._changectx.branch())
 
 527 #    def merge_with(self, other, user, message):
 
 528 #        lock = self._library._lock(True)
 
 530 #            self._library._checkout(self._changectx.node())
 
 531 #            self._library._merge(other._changectx.node())
 
 532 #            self._library._commit(user=user, message=message)
 
 536 #    def __eq__(self, other):
 
 537 #        return self._changectx.node() == other._changectx.node()
 
 540 #class MergeStatus(object):
 
 541 #    def __init__(self, mstatus):
 
 542 #        self.updated = mstatus[0]
 
 543 #        self.merged = mstatus[1]
 
 544 #        self.removed = mstatus[2]
 
 545 #        self.unresolved = mstatus[3]
 
 548 #        return self.unresolved == 0
 
 550 #class UpdateStatus(object):
 
 552 #    def __init__(self, mstatus):
 
 553 #        self.modified = mstatus[0]
 
 554 #        self.added = mstatus[1]
 
 555 #        self.removed = mstatus[2]
 
 556 #        self.deleted = mstatus[3]
 
 557 #        self.untracked = mstatus[4]
 
 558 #        self.ignored = mstatus[5]
 
 559 #        self.clean = mstatus[6]
 
 561 #    def has_changes(self):
 
 562 #        return bool(len(self.modified) + len(self.added) + \
 
 563 #                    len(self.removed) + len(self.deleted))
 
 565 #__all__ = ["MercurialLibrary"]