--- a/ForgeHg/forgehg/model/hg.py
+++ b/ForgeHg/forgehg/model/hg.py
@@ -7,26 +7,34 @@
import cPickle as pickle
import email as EM
from datetime import datetime
+from cStringIO import StringIO
+from itertools import islice
import pymongo
from pylons import c
from mercurial import ui, hg
+from webob import exc
from ming.orm.mapped_class import MappedClass
from ming.orm.property import FieldProperty
from ming.utils import LazyProperty
-from pyforge.model import Repository, Commit, ArtifactReference
+from pyforge import model as M
from pyforge.lib import helpers as h
log = logging.getLogger(__name__)
-class HgRepository(Repository):
+def on_import():
+ HgRepository.CommitClass = HgCommit
+ HgCommit.TreeClass = HgTree
+ HgTree.BlobClass = HgBlob
+
+class HgRepository(M.Repository):
class __mongometa__:
name='hg-repository'
def index(self):
- result = Repository.index(self)
+ result = super(HgRepository, self).index()
result.update(
type_s='HgRepository')
return result
@@ -71,38 +79,54 @@
def __iter__(self):
return self.log()
- def revision(self, hash):
- return HgCommit.from_hg(self._impl[hash], self)
-
- def log(self, changeset=None, branch=None, tag=None):
- if branch and branch in self._impl.branchmap():
- cs = self._impl.branchmap()[branch][0]
- elif tag and tag in self._impl.tags():
- cs = self._impl.tags()[tag]
- elif changeset:
- cs = changeset
- else:
- cs = self._impl.heads()[0]
- cs = self._impl[cs]
- yield HgCommit.from_hg(cs, self)
- for x in cs.ancestors():
- yield HgCommit.from_hg(x, self)
+ def commit(self, hash):
+ return self.CommitClass.from_repo_object(self._impl[hash], self)
+
+ def _log(self, ci, **kwargs):
+ def _iter(root):
+ frontier = [root]
+ while frontier:
+ ci = frontier.pop(0)
+ yield ci
+ frontier += ci.parents()
+ frontier.sort(key=lambda ci: sum(ci.date()))
+ commits = _iter(ci)
+ offset = kwargs.pop('offset', 0)
+ limit = kwargs.pop('limit', None)
+ if limit is None:
+ commits = islice(commits, offset, None)
+ else:
+ commits = islice(commits, offset, offset+limit)
+ return [ self.CommitClass.from_repo_object(entry, self)
+ for entry in commits ]
+
+ def log(self, branch=None, tag='tip', offset=0, limit=10):
+ if branch is not None:
+ ci = self._impl.branchmap()[branch][0]
+ elif tag is not None:
+ ci = self._impl.tags()[tag]
+ else:
+ ci = self._impl.changelog.tip()
+ ci = self._impl[ci]
+ return self._log(ci, offset=offset, limit=limit)
def __getattr__(self, name):
+ assert type(self._impl) != type(self)
return getattr(self._impl, name)
def __getitem__(self, name):
- return HgCommit.from_hg(self._impl[name], self)
+ return HgCommit.from_repo_object(self._impl[name], self)
def repo_tags(self):
'''Override Artifact.tags'''
return self._impl.tags()
-class HgCommit(Commit):
+class HgCommit(M.Commit):
type_s='HgCommit'
+ _impl = None
@classmethod
- def from_hg(cls, ctx ,repo):
+ def from_repo_object(cls, ctx ,repo):
result = cls(id=ctx.hex(), repo=repo)
result.__dict__['_impl'] = ctx
result.user = dict(
@@ -113,15 +137,28 @@
result.datetime=datetime.fromtimestamp(sum(ctx.date()))
return result
+ def url(self):
+ return self._repo.url() + 'ci/' + self._id + '/'
+
def __getattr__(self, name):
+ assert type(self._impl) != type(self)
return getattr(self._impl, name)
def shorthand_id(self):
return '[%s]' % self._id[:6]
+ @LazyProperty
def parents(self):
- return tuple(HgCommit.from_hg(p, self._repo)
+ return tuple(HgCommit.from_repo_object(p, self._repo)
for p in self._impl.parents())
+
+ @LazyProperty
+ def children(self):
+ return tuple(HgCommit.from_repo_object(p, self._repo)
+ for p in self._impl.children())
+
+ def tree(self):
+ return self.TreeClass(self._repo, self)
def diffs(self):
differ = h.diff_text_genshi
@@ -138,4 +175,116 @@
else:
pass
+ def diff_summarize(self):
+ if len(self.parents) == 1:
+ parent = self.parents[0]
+ for filename in self._impl.files():
+ if filename in parent._impl:
+ if filename in self._impl:
+ yield 'change', filename
+ else:
+ yield 'remove', filename
+ else:
+ yield 'add', filename
+ elif len(self.parents) == 0:
+ for blob in self.tree:
+ yield 'add', blob.path
+
+ def context(self):
+ return dict(
+ prev=self.parents,
+ next=self.children)
+
+class HgTree(M.Tree):
+
+ def __init__(self, repo, commit, parent=None, name=None):
+ super(HgTree, self).__init__(repo, commit, parent, name)
+ if self._parent:
+ self._tree = self._parent._tree[name]
+ self._manifest = self._parent._manifest
+ else:
+ self._tree = {}
+ self._manifest = commit._manifest
+ for k,v in self._manifest.iteritems():
+ dirname, filename = os.path.split(k)
+ tree = self._tree
+ for dirpart in dirname.split('/'):
+ tree = tree.setdefault(dirpart, {})
+ tree[filename] = v
+
+ def ls(self):
+ for name, dirent in sorted(self._tree.iteritems()):
+ name = name
+ date = None
+ href = name
+ last_author = '-'
+ commit = None
+ if isinstance(dirent, dict):
+ href = href + '/'
+ kind='dir'
+ else:
+ kind='file'
+ fc = self._repo._impl.filectx(
+ self.path() + name,
+ fileid=dirent)
+ date = datetime.fromtimestamp(sum(fc.date()))
+ last_author = fc.user()
+ commit = HgCommit.from_repo_object(fc.changectx(), self._repo)
+ yield dict(
+ dirent=dirent,
+ name=name,
+ date=date,
+ href=href,
+ kind=kind,
+ last_author=last_author,
+ commit=commit,
+ )
+
+ def is_blob(self, name):
+ try:
+ dirent = self._tree[name]
+ return not isinstance(dirent, dict)
+ except:
+ log.exception('Error checking blob-ness of %s', name)
+ return False
+
+class HgBlob(M.Blob):
+
+ def __init__(self, repo, commit, tree, filename):
+ super(HgBlob, self).__init__(
+ repo, commit, tree, filename)
+ self._blob = self._repo._impl.filectx(
+ self.path(), fileid=tree._tree[filename])
+
+ def __iter__(self):
+ fp = StringIO(self.text)
+ return iter(fp)
+
+ @LazyProperty
+ def text(self):
+ return self._blob.data()
+
+ @classmethod
+ def from_repo_object(cls, fc, repo):
+ ci = HgCommit.from_repo_object(fc.changectx(), repo)
+ dirname, filename = os.path.split(fc.path())
+ tree = ci.tree()
+ try:
+ return tree.get_blob(filename, dirname[1:].split('/'))
+ except KeyError:
+ return None
+
+ def context(self):
+ prev=[ HgBlob.from_repo_object(fc, self._repo)
+ for fc in self._blob.parents() ]
+ next=[ HgBlob.from_repo_object(fc, self._repo)
+ for fc in self._blob.children() ]
+ prev = [ b for b in prev if b is not None ]
+ next = [ b for b in next if b is not None ]
+ return dict(prev=prev, next=next)
+
+ def __getattr__(self, name):
+ return getattr(self._blob, name)
+
+on_import()
MappedClass.compile_all()