# -*- encoding: utf-8 -*-
+
__author__ = "Łukasz Rekucki"
__date__ = "$2009-09-18 10:49:24$"
import mercurial
from mercurial import localrepo as hglrepo
from mercurial import ui as hgui
+from mercurial.node import hex as to_hex
import re
import wlrepo
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']:
+ 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)
def main_cabinet(self):
return self._maincab
+ def document(self, docid, user):
+ return self.cabinet(docid, user, create=False).retrieve()
+
def cabinet(self, docid, user, create=False):
bname = self._bname(user, docid)
return MercurialCabinet(self, bname, doc=docid, user=user)
if not create:
- raise wlrepo.CabinetNotFound(docid, user)
+ raise wlrepo.CabinetNotFound(bname)
# check if the docid exists in the main cabinet
- needs_touch = not self._maincab.exists(docid)
- print "Creating branch: ", needs_touch
+ needs_touch = not self._maincab.exists(docid)
cab = MercurialCabinet(self, bname, doc=docid, user=user)
- fileid = cab._fileid(None)
+ name, fileid = cab._filename(None)
def cleanup_action(l):
- if needs_touch:
- print "Touch for file", docid
+ if needs_touch:
l._fileopener()(fileid, "w").write('')
l._fileadd(fileid)
- garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)]
- print "Garbage: ", garbage
+ garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)]
l._filesrm(garbage)
# create the branch
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 _commit(self, message, user=u"library"):
+ return self._hgrepo.commit(text=message, user=user)
def _fileexists(self, fileid):
def _fileopener(self):
return self._hgrepo.wopener
+
+ def _filectx(self, fileid, branchid):
+ return self._hgrepo.filectx(fileid, changeid=branchid)
#
# BASIC BRANCH routines
if before_commit: before_commit(self)
- print "commiting"
self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library')
# revert back to main
#
@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
+ def _sanitize_string(s):
+ if isinstance(s, unicode):
+ s = s.encode('utf-8')
+ return s
class MercurialCabinet(wlrepo.Cabinet):
self._branchname = branchname
+ def shelf(self, selector=None):
+ if selector is not None:
+ raise NotImplementedException()
+
+ return MercurialShelf(self, self._hgtip())
+
def documents(self):
- return self._execute_in_branch(action=lambda l, c: ( e[1] for e in l._filelist()) )
+ 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)
+ def retrieve(self, part=None, shelf=None):
+ name, fileid = self._filename(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))
+ return self._execute_in_branch(lambda l, c: MercurialDocument(c, name, fileid))
- def create(self, name, initial_data=''):
- fileid = self._fileid(name)
+ def create(self, name, initial_data):
+ name, fileid = self._filename(name)
if name is None:
raise ValueError("Can't create main doc for maincabinet.")
fd = l._fileopener()(fileid, "w")
fd.write(initial_data)
- l._fileadd(fileid)
- l._commit("File '%d' created.")
-
- return MercurialDocument(c, fileid)
+ 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, shelve=None):
- fileid = self._fileid(part)
+ def exists(self, part=None, shelf=None):
+ name, filepath = self._filename(part)
- if fileid is None: return false
- return self._execute_in_branch(lambda l, c: l._fileexists(fileid))
+ 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):
return self._library._transaction(write_mode=write, action=switch_action)
- def _fileid(self, part):
+ def _filename(self, part):
+ part = self._library._sanitize_string(part)
fileid = None
if self._maindoc == '':
- if part is None: return None
+ if part is None: return [None, None]
fileid = part
else:
fileid = self._maindoc + (('$' + part) if part else '')
- return 'pub_' + fileid + '.xml'
+ return fileid, 'pub_' + fileid + '.xml'
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)
+
class MercurialDocument(wlrepo.Document):
- def __init__(self, cabinet, fileid):
- super(MercurialDocument, self).__init__(cabinet, fileid)
- self._opener = self._cabinet._fileopener()
+ def __init__(self, cabinet, name, fileid):
+ super(MercurialDocument, self).__init__(cabinet, name=name)
+ self._opener = self._cabinet._fileopener()
+ self._fileid = fileid
+ self._refresh()
+
+ def _refresh(self):
+ self._filectx = self._cabinet._filectx(self._fileid)
+ self._shelf = MercurialShelf(self, self._filectx.node())
def read(self):
- return self._opener(self._name, "r").read()
+ return self._opener(self._filectx.path(), "r").read()
def write(self, data):
- return self._opener(self._name, "w").write(data)
+ return self._opener(self._filectx.path(), "w").write(data)
+
+ def commit(self, message, user):
+ self.library._fileadd(self._fileid)
+ self.library._commit(self._fileid, message, user)
+
+ def update(self):
+ if self._cabinet.is_main():
+ return True # always up-to-date
+
+ 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)
+
+ return rc.ALL_OK
+
+ def share(self, message, user):
+ if self._cabinet.is_main():
+ return True # always shared
+
+ main = self.shared_version()
+ 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):
+ 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 main.has_common_ancestor(local):
+ 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):
+ if not local.parentof(main):
+ local.merge_with(main, user=user, message='Local branch update.')
+ no_changes = False
+ else:
+ local.merge_with(main, user=user, message='Local branch update.')
+
+ self._refresh()
+ local = self.shelf()
+
+ main.merge_with(local, user=user, message=message)
+
+ def shared(self):
+ return self.library.document(self._fileid)
+
+ @property
+ def size(self):
+ return self._filectx.size()
+
+
+ def shelf(self):
+ return self._shelf
+
+ @property
+ def last_modified(self):
+ return self._filectx.date()
+
+ def __str__(self):
+ return u"Document(%s->%s)" % (self._cabinet.name, self._name)
+
+
+class MercurialShelf(wlrepo.Shelf):
+
+ def __init__(self, cabinet, revision):
+ super(MercurialShelf, self).__init__(cabinet)
+ self._revision = revision
+
+ @property
+ def _rev(self):
+ return _revision
+
+ def __str__(self):
+ return to_hex(self._revision)
class MergeStatus(object):
return bool(len(self.modified) + len(self.added) + \
len(self.removed) + len(self.deleted))
-
-__all__ = ["MercurialLibrary", "MercurialCabinet", "MercurialDocument"]
\ No newline at end of file
+__all__ = ["MercurialLibrary"]
\ No newline at end of file