X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/3c5fe5b298287f92a9c6a8e485c3860db36931fd..e459e4567cc578abd5d7e02e70a01ec9464f474d:/lib/wlrepo/mercurial_backend/document.py?ds=sidebyside diff --git a/lib/wlrepo/mercurial_backend/document.py b/lib/wlrepo/mercurial_backend/document.py old mode 100644 new mode 100755 index 657fa6e3..77b8ead7 --- a/lib/wlrepo/mercurial_backend/document.py +++ b/lib/wlrepo/mercurial_backend/document.py @@ -1,20 +1,40 @@ # -*- encoding: utf-8 -*- +import logging +log = logging.getLogger('ral.mercurial') + __author__ = "Łukasz Rekucki" __date__ = "$2009-09-25 09:35:06$" __doc__ = "Module documentation." import wlrepo +import mercurial.error +import re + +import logging +log = logging.getLogger('wlrepo.document') class MercurialDocument(wlrepo.Document): def data(self, entry): - path = self._revision._docname + '.' + entry - return self._library._filectx(path, \ - self._revision.hgrev()).data() + path = self._library._sanitize_string(self.id + u'.' + entry) + try: + return self._library._filectx(path, \ + self._revision.hgrev()).data().decode('utf-8') + except mercurial.error.LookupError, e: + fl = [x.decode('utf-8') for x in self._revision._changectx] + raise wlrepo.EntryNotFound(self._revision, path.decode('utf-8'), fl) def quickwrite(self, entry, data, msg, user=None): user = user or self.owner + + if isinstance(data, unicode): + data = data.encode('utf-8') + + user = self._library._sanitize_string(user) + msg = self._library._sanitize_string(msg) + entry = self._library._sanitize_string(entry) + if user is None: raise ValueError("Can't determine user.") @@ -22,151 +42,204 @@ class MercurialDocument(wlrepo.Document): f = l._fileopen(r(entry), "w+") f.write(data) f.close() - l._fileadd(r(entry)) + l._fileadd(r(entry)) - return self.invoke_and_commit(write, lambda d: (msg, user)) + return self.invoke_and_commit(write, lambda d: (msg, \ + self._library._sanitize_string(self.owner)) ) - def invoke_and_commit(self, ops, before_commit): - lock = self._library._lock() + def invoke_and_commit(self, ops, commit_info): + lock = self._library.lock() try: self._library._checkout(self._revision.hgrev()) def entry_path(entry): - return self.id + '.' + entry + return self._library._sanitize_string(self.id + u'.' + entry) ops(self._library, entry_path) - message, user = before_commit(self) - self._library._commit(message, user) + message, user = commit_info(self) - return self._library.document(docid=self.id, user=self.owner) - finally: - lock.release() + message = self._library._sanitize_string(message) + user = self._library._sanitize_string(user) + self._library._commit(message, user) + try: + return self._library.document(docid=self.id, user=user) + except Exception, e: + # rollback the last commit + self._library._rollback() + raise e + finally: + lock.release() + # def commit(self, message, user): # """Make a new commit.""" # self.invoke_and_commit(message, user, lambda *a: True) def ismain(self): - return self._revision.user_name() is None + return self._revision.user_name is None + + def islatest(self): + return (self == self.latest()) def shared(self): if self.ismain(): return self - return self._library.document(docid=self._revision.document_name()) + + return self._library.document(docid=self.id) + + def latest(self): + return self._library.document(docid=self.id, user=self.owner) def take(self, user): fullid = self._library.fulldocid(self.id, user) def take_action(library, resolve): # branch from latest - library._create_branch(fullid, parent=self._revision) + library._set_branchname(fullid) if not self._library.has_revision(fullid): + log.info("Checking out document %s" % fullid) + self.invoke_and_commit(take_action, \ lambda d: ("$AUTO$ File checkout.", user) ) - return self._library.document_for_rev(fullid) - + return self._library.document_for_revision(fullid) + + def up_to_date(self): + if self.ismain(): + return True + + shared = self.shared() + + if shared.ancestorof(self): + return True + + if shared.has_parent_from(self): + return True + + return False + def update(self, user): """Update parts of the document.""" - lock = self.library._lock() + lock = self.library.lock() try: if self.ismain(): # main revision of the document - return True - - if self._revision.has_children(): - # can't update non-latest revision - return False + return self - sv = self.shared() - - if not sv.ancestorof(self) and not self.parentof(sv): - self._revision.merge_with(sv._revision, user=user) + # check for children in this branch + if self._revision.has_children(limit_branch=True): + raise wlrepo.UpdateException("Revision %s has children." % self.revision) - return True - finally: - lock.release() + shared = self.shared() + # * + # /| + # * | + # | | + # + # we carry the latest version + if shared.ancestorof(self): + return self + + # * + # |\ + # | * + # | | + # + # We just shared + if self.parentof(shared): + return self + + # s s S + # | | | + # *<-S *<-* + # | | | . + # + # This is ok (s - shared, S - self) - def share(self, message): - lock = self.library._lock() - try: - if self.ismain(): - return True # always shared + if self._revision.merge_with(shared._revision, user=user,\ + message="$AUTO$ Personal branch update."): + return self.latest() + else: + raise wlrepo.UpdateException("Merge failed.") + finally: + lock.release() - user = self._revision.user_name() - main = self.shared()._revision - local = self._revision + def would_share(self): + if self.ismain(): + return False, "Main version is always shared" + + shared = self.shared() + + # we just did this - move on + if self.parentof(shared): + return False, "Document has been recetly shared - no changes" + + # * + # /| + # * * + # |\| + # | * + # | | + # Situation above is ok - what we don't want, is: + # * + # /| + # * | + # |\| + # | * + # | | + # We want to prevent stuff like this. + if self.parent().parentof(shared) and shared.parentof(self): + return False, "Preventing zig-zag" + + return True, "All ok" - no_changes = True + def share(self, message): + lock = self.library.lock() + try: + # check if the document is in "updated" state + if not self.up_to_date(): + raise wlrepo.OutdatedException("You must update your document before share.") - # Case 1: + # now check if there is anything to do + need_work, info = self.would_share() + + if not need_work: + return self.shared() + + # The good situation + # # * 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 + shared = self.shared() + + try: + success = shared._revision.merge_with(self._revision, user=self.owner, message=message) + if not success: + raise wlrepo.LibraryException("Merge failed.") + + return shared.latest() + except Abort, e: + raise wlrepo.LibraryException( repr(e) ) finally: - lock.release() + lock.release() - def __str__(self): - return u"Document(%s:%s)" % (self.name, self.owner) + def has_conflict_marks(self): + return re.search("^(?:<<<<<<< .*|=======|>>>>>>> .*)$", self.data('xml'), re.MULTILINE) + + def __unicode__(self): + return u"Document(%s:%s)" % (self.id, self.owner) + + def __str__(self): + return self.__unicode__().encode('utf-8') + def __eq__(self, other): - return (self._revision == other._revision) and (self.name == other.name) + return (self._revision == other._revision)