You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by jo...@apache.org on 2012/11/27 17:18:54 UTC
[2/2] git commit: Partial commit, WIP
Partial commit, WIP
Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/e4e9fe84
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/e4e9fe84
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/e4e9fe84
Branch: refs/heads/cj/4691
Commit: e4e9fe84ead0035a39b57a3b3c2a7912d18637d0
Parents: 7eb7956
Author: Cory Johns <jo...@geek.net>
Authored: Wed Oct 10 02:28:07 2012 +0000
Committer: Cory Johns <jo...@geek.net>
Committed: Tue Nov 27 14:53:45 2012 +0000
----------------------------------------------------------------------
Allura/allura/model/repo.py | 191 ++++++-
Allura/allura/model/repo_refresh.py | 101 ++++-
Allura/allura/model/repository.py | 38 ++-
.../allura/templates/widgets/repo/tree_widget.html | 20 +-
Allura/allura/tests/model/test_repo.py | 451 +++++++++++++++
ForgeGit/forgegit/model/git_repo.py | 4 +
ForgeHg/forgehg/model/hg.py | 4 +
ForgeSVN/forgesvn/model/svn.py | 5 +-
8 files changed, 790 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/Allura/allura/model/repo.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo.py b/Allura/allura/model/repo.py
index b46f3fa..bb9784f 100644
--- a/Allura/allura/model/repo.py
+++ b/Allura/allura/model/repo.py
@@ -11,7 +11,7 @@ from difflib import SequenceMatcher, unified_diff
from pylons import c
import pymongo.errors
-from ming import Field, collection
+from ming import Field, collection, Index
from ming import schema as S
from ming.base import Object
from ming.utils import LazyProperty
@@ -61,9 +61,7 @@ TreeDoc = collection(
Field('blob_ids', [dict(name=str, id=str)]),
Field('other_ids', [dict(name=str, id=str, type=SObjType)]))
-# Information about the last commit to touch a tree/blob
-# LastCommitDoc.object_id = TreeDoc._id
-LastCommitDoc = collection(
+LastCommitDoc_old = collection(
'repo_last_commit', project_doc_session,
Field('_id', str),
Field('object_id', str, index=True),
@@ -77,6 +75,26 @@ LastCommitDoc = collection(
shortlink=str,
summary=str)))
+# Information about the last commit to touch a tree
+LastCommitDoc = collection(
+ 'repo_last_commit', main_doc_session,
+ Field('_id', S.ObjectId()),
+ Field('commit_ids', [str]),
+ Field('path', str),
+ Index('commit_ids', 'path'),
+ Field('entries', {
+ str:dict(
+ type=str,
+ name=str,
+ commit_info=dict(
+ id=str,
+ date=datetime,
+ author=str,
+ author_email=str,
+ author_url=str,
+ shortlink=str,
+ summary=str))}))
+
# List of all trees contained within a commit
# TreesDoc._id = CommitDoc._id
# TreesDoc.tree_ids = [ TreeDoc._id, ... ]
@@ -182,13 +200,28 @@ class Commit(RepoObject):
def symbolic_ids(self):
return self.repo.symbolics_for_commit(self)
- def parent(self, index=0):
- ci = None
- if self.parent_ids:
+ def get_parent(self, index=0):
+ '''Get the parent of this commit.
+
+ If there is no parent commit, or if an invalid index is given,
+ returns None.
+ '''
+ try:
ci = self.query.get(_id=self.parent_ids[index])
- if ci:
ci.set_context(self.repo)
- return ci
+ return ci
+ except IndexError as e:
+ return None
+
+ def climb_commit_tree(self):
+ '''
+ Returns a generator that walks up the commit tree along
+ the first-parent ancestory, starting with this commit.'''
+ yield self
+ ancestor = self.get_parent()
+ while ancestor:
+ yield ancestor
+ ancestor = ancestor.get_parent()
def url(self):
if self.repo is None: self.repo = self.guess_repo()
@@ -293,7 +326,7 @@ class Commit(RepoObject):
if not removed:
return []
copied = []
- prev_commit = self.parent()
+ prev_commit = self.get_parent()
for removed_name in removed[:]:
removed_blob = prev_commit.tree.get_obj_by_path(removed_name)
rename_info = None
@@ -316,6 +349,26 @@ class Commit(RepoObject):
cur = cur[part]
return cur
+ def changed(self, path):
+ '''Test whether a given path was changed in this commit.'''
+ di = DiffInfoDoc.m.get(_id=self._id)
+ for change in di.differences:
+ if change.name.strip('/') == path.strip('/'):
+ return True
+ return False
+
+ @LazyProperty
+ def info(self):
+ return dict(
+ id=self._id,
+ author=self.authored.name,
+ author_email=self.authored.email,
+ date=self.authored.date,
+ author_url=self.author_url,
+ shortlink=self.shorthand_id(),
+ summary=self.summary
+ )
+
class Tree(RepoObject):
# Ephemeral attrs
repo=None
@@ -386,6 +439,15 @@ class Tree(RepoObject):
return None, None
def ls(self):
+ last_commit = LastCommitDoc.m.get(
+ commit_ids=self.commit._id,
+ path=self.path(),
+ )
+ if not last_commit:
+ from .repo_refresh import build_last_commit_doc
+ last_commit = build_last_commit_doc(self)
+ return sorted(last_commit.entries.values(), cmp=lambda a,b: cmp(b.type,a.type) or cmp(a.name,b.name))
+
# Load last commit info
id_re = re.compile("^{0}:{1}:".format(
self.repo._id,
@@ -569,5 +631,114 @@ class Blob(object):
differ = SequenceMatcher(v0, v1)
return differ.get_opcodes()
+class LastCommit(RepoObject):
+ @classmethod
+ def build(cls, tree):
+ '''
+ We need a LCD for this tree, for which there are two possibilities:
+
+ 1) This tree was modified in an ancestor commit but the commit ID chain
+ was not filled in. In this case, as we walk back up the tree, we'll
+ find a LCD record when or before we find the commit in which this tree
+ was changed. If we find this, we save the new commit IDs in the LCD
+ record for faster access in the future.
+
+ 2) The LCD record for the commit in which this tree was changed does not
+ exist. We'll find the commit and still not have a LCD record, which
+ means we have to construct it. The LCD record will only contain the
+ commit IDs up to the commit where the tree was most recently changed.
+
+ Constructing it differs for SVN and Git / Hg. SVN can pull all the info
+ from a single SVN call. Git / Hg have to walk up the tree. (SVN could
+ walk up the tree as well, except that the TreesDoc and DiffInfoDoc
+ records are not correctly populated, making it hard to tell when a tree
+ was changed in a given commit, plus it's unnecessary.)
+
+ To walk up the tree, we have to keep track of which entries we still
+ need info about. At each step of the walk, we check the following:
+
+ 1) If the current tree has a LCD record, we can pull all the remaining
+ info we need from it, and we're done.
+
+ 2) If the tree was modified in this commit, then we pull the info for
+ all changed entries, then making the parent tree the new active
+ tree and continuing the walk. Once we have data for all entries,
+ we're done.
+ '''
+ unfilled = set([n.name for n in chain(tree.tree_ids, tree.blob_ids, tree.other_ids)])
+ tree_nodes = set([n.name for n in tree.tree_ids])
+ entires = []
+ commit_ids = []
+ commit = tree.commit
+ path = tree.path().strip('/')
+ lcd = None
+ has_changes = False
+ for commit in commit.climb_commit_tree():
+ last_commit = LastCommitDoc.m.get(
+ commit_ids=commit._id,
+ path=path,
+ )
+ if not has_changes:
+ # no changes found yet, so look to see if we have a matching LCD
+ # that just doesn't have all the commit_ids filled in
+ if last_commit:
+ # found a complete LCD for our tree
+ last_commit.commit_ids.extend(commit_ids)
+ last_commit.m.save()
+ return last_commit
+ # LCD is only valid for the most recent commit
+ # that changed the tree, so once we have changes,
+ # stop recording commit_ids
+ commit_ids.append(commit._id)
+
+ # look for changes to the tree in this commit,
+ # meaning our LCD is missing and must be built
+ diff_info = DiffInfoDoc.m.get(_id=commit._id)
+ diffs = set()
+ for d in diff_info.differences:
+ diffs.add(d.name)
+ node_path = os.path.dirname(d.name)
+ while node_path:
+ diffs.add(node_path)
+ node_path = os.path.dirname(node_path)
+ diffs.add('') # include '/' if there are any changes
+ #import ipdb; ipdb.set_trace()
+ if path in diffs:
+ has_changes = True
+ for name in list(unfilled):
+ full_name = os.path.join(path, name)
+ if full_name in diffs:
+ if lcd is None:
+ lcd = LastCommitDoc(dict(
+ commit_ids=commit_ids,
+ path=path,
+ entries=dict(),
+ ))
+ lcd.entries[name] = dict(
+ type=name in tree_nodes and 'DIR' or 'BLOB',
+ name=name,
+ commit_info=commit.info,
+ )
+ unfilled.remove(name)
+
+ # if we have changes but this commit has an LCD for our
+ # path, it should have all the remaining info we need
+ if last_commit:
+ for name in list(unfilled):
+ lcd.entries[name] = last_commit.entries[name]
+ unfilled.remove(name)
+
+ if has_changes and not unfilled:
+ break
+ if lcd is None:
+ lcd = LastCommitDoc(dict(
+ commit_ids=commit_ids,
+ path=path,
+ entries=dict(),
+ ))
+ lcd.m.save()
+ return lcd
+
mapper(Commit, CommitDoc, repository_orm_session)
mapper(Tree, TreeDoc, repository_orm_session)
+mapper(LastCommit, LastCommitDoc, repository_orm_session)
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 6e0db59..3fa41fc 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -2,6 +2,7 @@ import logging
from itertools import chain
from cPickle import dumps
import re
+import os
import bson
@@ -348,7 +349,7 @@ def compute_diffs(repo_id, tree_cache, rhs_ci):
dict(name=name, lhs_id=lhs_id, rhs_id=rhs_id))
# Set last commit data
rhs_tree = tree_index[rhs_ci.tree_id]
- refresh_last_commit(repo_id, '/', rhs_tree, lhs_tree, None, commit_info)
+ #refresh_last_commit(repo_id, '/', rhs_tree, lhs_tree, None, commit_info)
# Build the diffinfo
di = DiffInfoDoc(dict(
_id=rhs_ci._id,
@@ -522,3 +523,101 @@ def last_known_commit_id(all_commit_ids, new_commit_ids):
if not all_commit_ids: return None
if not new_commit_ids: return all_commit_ids[-1]
return all_commit_ids[all_commit_ids.index(new_commit_ids[0]) - 1]
+
+def build_last_commit_doc(tree):
+ '''
+ We need a LCD for this tree, for which there are two possibilities:
+
+ 1) This tree was modified in an ancestor commit but the commit ID chain
+ was not filled in. In this case, as we walk back up the tree, we'll
+ find a LCD record when or before we find the commit in which this tree
+ was changed. If we find this, we save the new commit IDs in the LCD
+ record for faster access in the future.
+
+ 2) The LCD record for the commit in which this tree was changed does not
+ exist. We'll find the commit and still not have a LCD record, which
+ means we have to construct it. The LCD record will only contain the
+ commit IDs up to the commit where the tree was most recently changed.
+
+ Constructing it differs for SVN and Git / Hg. SVN can pull all the info
+ from a single SVN call. Git / Hg have to walk up the tree. (SVN could
+ walk up the tree as well, except that the TreesDoc and DiffInfoDoc
+ records are not correctly populated, making it hard to tell when a tree
+ was changed in a given commit, plus it's unnecessary.)
+
+ To walk up the tree, we have to keep track of which entries we still
+ need info about. At each step of the walk, we check the following:
+
+ 1) If the current tree has a LCD record, we can pull all the remaining
+ info we need from it, and we're done.
+
+ 2) If the tree was modified in this commit, then we pull the info for
+ all changed entries, then making the parent tree the new active
+ tree and continuing the walk. Once we have data for all entries,
+ we're done.
+ '''
+ unfilled = set([n.name for n in chain(tree.tree_ids, tree.blob_ids, tree.other_ids)])
+ tree_nodes = set([n.name for n in tree.tree_ids])
+ entires = []
+ commit_ids = []
+ commit = tree.commit
+ path = tree.path().strip('/')
+ lcd = None
+ has_changes = False
+ while unfilled:
+ last_commit = LastCommitDoc.m.get(
+ commit_ids=commit._id,
+ path=path,
+ )
+ if not has_changes:
+ # no changes found yet, so look to see if we have a matching LCD
+ # that just doesn't have all the commit_ids filled in
+ if last_commit:
+ # found a complete LCD for our tree
+ last_commit.commit_ids.extend(commit_ids)
+ last_commit.m.save()
+ return last_commit
+ # LCD is only valid for the most recent commit
+ # that changed the tree, so once we have changes,
+ # stop recording commit_ids
+ commit_ids.append(commit._id)
+
+ # look for changes to the tree in this commit,
+ # meaning our LCD is missing and must be built
+ diff_info = DiffInfoDoc.m.get(_id=commit._id)
+ diffs = set()
+ for d in diff_info.differences:
+ diffs.add(d.name)
+ node_path = os.path.dirname(d.name)
+ while node_path:
+ diffs.add(node_path)
+ node_path = os.path.dirname(node_path)
+ import ipdb; ipdb.set_trace()
+ for name in list(unfilled):
+ full_name = path + '/' + name
+ if full_name in diffs:
+ has_changes = True
+ if lcd is None:
+ lcd = LastCommitDoc(dict(
+ commit_ids=commit_ids,
+ path=path,
+ entries=dict(),
+ ))
+ lcd.entries[name] = dict(
+ type=name in tree_nodes and 'DIR' or 'BLOB',
+ name=name,
+ commit_info=get_commit_info(commit),
+ )
+ unfilled.remove(name)
+
+ # if we have changes but this commit has an LCD for our
+ # path, it should have all the remaining info we need
+ if last_commit:
+ for name in list(unfilled):
+ lcd.entries[name] = last_commit.entries[name]
+ unfilled.remove(name)
+
+ # walk up the tree
+ commit = commit.get_parent()
+ lcd.m.save()
+ return lcd
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 9867cf0..45125df 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -433,8 +433,42 @@ class Repository(Artifact, ActivityObject):
with self.push_upstream_context():
return MergeRequest.query.find(q).count()
- def get_last_commit(self, obj):
- from .repo import LastCommitDoc
+ def get_last_commit(self, tree):
+ '''Find the LastCommitDoc for the given tree.
+
+ Walks up the commit tree until either:
+
+ 1) A LCD is found for the given tree. (If the LCD was not found for the
+ tree's commit, the commits traversed while searching for it are
+ added to the LCD for faster retrieval in the future.)
+
+ 2) The commit in which the tree was most recently modified is found.
+ In this case, we know that the LCD hasn't been constructed for this
+ (chain of) commit(s), and it will have to be built.
+ '''
+ from .repo import Commit, LastCommitDoc
+ last_commit = None
+ commit = tree.commit
+ path = tree.path()
+ orig_tree = tree
+ other_commit_ids = []
+ while commit is not None:
+ last_commit = LastCommitDoc.m.find(dict(
+ commit_ids=commit._id,
+ path=path,
+ )).first()
+ if last_commit is not None:
+ # found our LCD; add any traversed commits to it
+ last_commit.commit_ids.append(commit_ids)
+ last_commit.m.save()
+ return last_commit
+ elif commit.changed(path):
+ # tree was changed but no LCD found; have to build
+ return self.compute_last_commit(commit, path, other_commit_ids)
+ else:
+ other_commit_ids.append(commit._id)
+ commit = commit.get_parent()
+
lc = LastCommitDoc.m.get(
repo_id=self._id, object_id=obj._id)
if lc is None:
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/Allura/allura/templates/widgets/repo/tree_widget.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/repo/tree_widget.html b/Allura/allura/templates/widgets/repo/tree_widget.html
index 09b0758..a2d8024 100644
--- a/Allura/allura/templates/widgets/repo/tree_widget.html
+++ b/Allura/allura/templates/widgets/repo/tree_widget.html
@@ -25,24 +25,24 @@
{% for dirent in tree.ls() %}
<tr>
<td class="nowrap">
- <a href="{{h.urlquote(dirent.href)}}">
- <b data-icon="{{dirent.kind == 'DIR' and 'o' or 'n'}}" class="ico {{dirent.kind == 'DIR' and 'folder' or 'table'}}"></b>
+ <a href="{{h.urlquote(dirent.name)}}">
+ <b data-icon="{{dirent.type == 'DIR' and 'o' or 'n'}}" class="ico {{dirent.type == 'DIR' and 'folder' or 'table'}}"></b>
<span>{{h.really_unicode(dirent.name)}}</span>
</a>
</td>
- <td class="nowrap">{{lib.abbr_date(dirent.last_commit.date)}}</td>
+ <td class="nowrap">{{lib.abbr_date(dirent.commit_info.date)}}</td>
<td class="nowrap">
- {% if dirent.last_commit.author_url %}
- <a href="{{dirent.last_commit.author_url}}">{{lib.email_gravatar(dirent.last_commit.author_email, title=h.really_unicode(dirent.last_commit.author), size=16)}}</a>
- <a href="{{dirent.last_commit.author_url}}">{{h.really_unicode(dirent.last_commit.author)}}</a>
+ {% if dirent.commit_info.author_url %}
+ <a href="{{dirent.commit_info.author_url}}">{{lib.email_gravatar(dirent.commit_info.author_email, title=h.really_unicode(dirent.commit_info.author), size=16)}}</a>
+ <a href="{{dirent.commit_info.author_url}}">{{h.really_unicode(dirent.commit_info.author)}}</a>
{% else %}
- {{lib.email_gravatar(dirent.last_commit.author_email, title=h.really_unicode(dirent.last_commit.author), size=16)}} {{h.really_unicode(dirent.last_commit.author)}}
+ {{lib.email_gravatar(dirent.commit_info.author_email, title=h.really_unicode(dirent.commit_info.author), size=16)}} {{h.really_unicode(dirent.commit_info.author)}}
{% endif %}
</td>
<td>
- <a href="{{dirent.last_commit.href}}">
- {{dirent.last_commit.shortlink}}
- {{dirent.last_commit.summary}}
+ <a href="{{dirent.commit_info.href}}">
+ {{dirent.commit_info.shortlink}}
+ {{dirent.commit_info.summary}}
</a>
</td>
</tr>
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/Allura/allura/tests/model/test_repo.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_repo.py b/Allura/allura/tests/model/test_repo.py
index 90eaac1..484b7d1 100644
--- a/Allura/allura/tests/model/test_repo.py
+++ b/Allura/allura/tests/model/test_repo.py
@@ -1,5 +1,10 @@
+from datetime import datetime
+import unittest
+import mock
from nose.tools import assert_equal
from pylons import c
+from bson import ObjectId
+from ming.orm import session
from alluratest.controller import setup_basic_test, setup_global_objects
from allura import model as M
@@ -67,3 +72,449 @@ class RepoImplTestBase(object):
self.assertEqual(run.commit_ids, commit_ids)
self.assertEqual(len(run.commit_ids), len(run.commit_times))
self.assertEqual(run.parent_commit_ids, [])
+
+
+class TestLastCommit(unittest.TestCase):
+ def setUp(self):
+ self.repo = mock.Mock('repo', _commits={}, _last_commit=None)
+ self.repo.shorthand_for_commit = lambda _id: _id[:6]
+
+ def _build_tree(self, commit, path, tree_paths):
+ tree_nodes = []
+ blob_nodes = []
+ def n(p):
+ m = mock.Mock()
+ m.name = p
+ return m
+ for p in tree_paths:
+ if '/' in p:
+ tree_nodes.append(n(p.split('/')[0]))
+ else:
+ blob_nodes.append(n(p))
+ tree = mock.Mock(
+ commit=commit,
+ path=mock.Mock(return_value=path),
+ tree_ids=tree_nodes,
+ blob_ids=blob_nodes,
+ other_ids=[],
+ )
+ return tree
+
+ def _add_commit(self, msg, tree_paths, diff_paths=None, parents=[]):
+ suser = dict(
+ name='test',
+ email='test@example.com',
+ date=datetime(2013, 1, 1 + len(self.repo._commits)),
+ )
+ commit = M.repo.Commit(
+ _id=str(ObjectId()),
+ message=msg,
+ parent_ids=[parent._id for parent in parents],
+ commited=suser,
+ authored=suser,
+ repo=self.repo,
+ )
+ commit.tree = self._build_tree(commit, '/', tree_paths)
+ diffinfo = M.repo.DiffInfoDoc(dict(
+ _id=commit._id,
+ differences=[{'name': p} for p in diff_paths or tree_paths],
+ ))
+ diffinfo.m.save()
+ self.repo._commits[commit._id] = commit
+ return commit
+
+ def test_single_commit(self):
+ commit1 = self._add_commit('Commit 1', [
+ 'file1',
+ 'dir1/file2',
+ ])
+ lcd = M.repo.LastCommit.build(commit1.tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit1.message])
+ self.assertEqual(lcd.path, '')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 1',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ 'dir1': dict(
+ type='DIR',
+ name='dir1',
+ commit_info=dict(
+ summary='Commit 1',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ })
+
+ def test_multiple_commits_no_overlap(self):
+ commit1 = self._add_commit('Commit 1', ['file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'dir1/file1'], ['dir1/file1'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'dir1/file1', 'file2'], ['file2'], [commit2])
+ lcd = M.repo.LastCommit.build(commit3.tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit3.message])
+ self.assertEqual(lcd.commit_ids, [commit3._id])
+ self.assertEqual(lcd.path, '')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 1',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ 'dir1': dict(
+ type='DIR',
+ name='dir1',
+ commit_info=dict(
+ summary='Commit 2',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 2),
+ author_url=None,
+ id=commit2._id,
+ shortlink=self.repo.shorthand_for_commit(commit2._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ })
+
+ def test_multiple_commits_with_overlap(self):
+ commit1 = self._add_commit('Commit 1', ['file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'dir1/file1'], ['dir1/file1'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'dir1/file1', 'file2'], ['file1', 'file2'], [commit2])
+ lcd = M.repo.LastCommit.build(commit3.tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit3.message])
+ self.assertEqual(lcd.path, '')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ 'dir1': dict(
+ type='DIR',
+ name='dir1',
+ commit_info=dict(
+ summary='Commit 2',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 2),
+ author_url=None,
+ id=commit2._id,
+ shortlink=self.repo.shorthand_for_commit(commit2._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ })
+
+ def test_multiple_commits_subdir_change(self):
+ commit1 = self._add_commit('Commit 1', ['file1', 'dir1/file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file2'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file1'], [commit2])
+ lcd = M.repo.LastCommit.build(commit3.tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit3.message])
+ self.assertEqual(lcd.path, '')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 1',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ 'dir1': dict(
+ type='DIR',
+ name='dir1',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ })
+
+ def test_subdir_lcd(self):
+ commit1 = self._add_commit('Commit 1', ['file1', 'dir1/file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file2'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file1'], [commit2])
+ tree = self._build_tree(commit3, '/dir1', ['file1', 'file2'])
+ lcd = M.repo.LastCommit.build(tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit3.message])
+ self.assertEqual(lcd.path, 'dir1')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 2',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 2),
+ author_url=None,
+ id=commit2._id,
+ shortlink=self.repo.shorthand_for_commit(commit2._id),
+ )),
+ })
+
+ def test_subdir_lcd_prev_commit(self):
+ commit1 = self._add_commit('Commit 1', ['file1', 'dir1/file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file2'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file1'], [commit2])
+ commit4 = self._add_commit('Commit 1', ['file1', 'dir1/file1', 'dir1/file2', 'file2'], ['file2'], [commit3])
+ tree = self._build_tree(commit3, '/dir1', ['file1', 'file2'])
+ lcd = M.repo.LastCommit.build(tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit3.message])
+ self.assertEqual(lcd.path, 'dir1')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 2',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 2),
+ author_url=None,
+ id=commit2._id,
+ shortlink=self.repo.shorthand_for_commit(commit2._id),
+ )),
+ })
+
+ def test_subdir_lcd_always_empty(self):
+ commit1 = self._add_commit('Commit 1', ['file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'file2'], ['file2'], [commit1])
+ tree = self._build_tree(commit2, '/dir1', [])
+ lcd = M.repo.LastCommit.build(tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit2.message, commit1.message])
+ self.assertEqual(lcd.path, 'dir1')
+ self.assertEqual(lcd.entries, {})
+
+ def test_subdir_lcd_emptied(self):
+ commit1 = self._add_commit('Commit 1', ['file1', 'dir1/file1'])
+ commit2 = self._add_commit('Commit 2', ['file1'], ['dir1/file1'], [commit1])
+ tree = self._build_tree(commit2, '/dir1', [])
+ lcd = M.repo.LastCommit.build(tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit2.message])
+ self.assertEqual(lcd.path, 'dir1')
+ self.assertEqual(lcd.entries, {})
+
+ def test_existing_lcd_unchained(self):
+ commit1 = self._add_commit('Commit 1', ['file1', 'dir1/file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'dir1/file1', 'dir1/file2'], ['dir1/file2'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'dir1/file1', 'dir1/file2'], ['file1'], [commit2])
+ prev_lcd = M.repo.LastCommit(
+ path='dir1',
+ commit_ids=[commit2._id],
+ entries={
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Commit 1',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 2',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 2),
+ author_url=None,
+ id=commit2._id,
+ shortlink=self.repo.shorthand_for_commit(commit2._id),
+ )),
+ },
+ )
+ session(prev_lcd).flush()
+ tree = self._build_tree(commit3, '/dir1', ['file1', 'file2'])
+ lcd = M.repo.LastCommit.build(tree)
+ self.assertEqual(lcd._id, prev_lcd._id)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit2.message, commit3.message])
+ self.assertEqual(lcd.path, 'dir1')
+ self.assertEqual(lcd.entries, prev_lcd.entries)
+
+ def test_existing_lcd_partial(self):
+ commit1 = self._add_commit('Commit 1', ['file1'])
+ commit2 = self._add_commit('Commit 2', ['file1', 'file2'], ['file2'], [commit1])
+ commit3 = self._add_commit('Commit 3', ['file1', 'file2', 'file3'], ['file3'], [commit2])
+ commit4 = self._add_commit('Commit 4', ['file1', 'file2', 'file3', 'file4'], ['file2', 'file4'], [commit3])
+ prev_lcd = M.repo.LastCommit(
+ path='',
+ commit_ids=[commit3._id],
+ entries={
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Existing LCD', # lying here to test that it uses this
+ author='test', # data instead of walking up the tree
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 2',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 2),
+ author_url=None,
+ id=commit2._id,
+ shortlink=self.repo.shorthand_for_commit(commit2._id),
+ )),
+ 'file3': dict(
+ type='BLOB',
+ name='file3',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ },
+ )
+ session(prev_lcd).flush()
+ lcd = M.repo.LastCommit.build(commit4.tree)
+ self.assertEqual([self.repo._commits[c].message for c in lcd.commit_ids], [commit4.message])
+ self.assertEqual(lcd.path, '')
+ self.assertEqual(lcd.entries['file1']['commit_info']['summary'], 'Existing LCD')
+ self.assertEqual(lcd.entries, {
+ 'file1': dict(
+ type='BLOB',
+ name='file1',
+ commit_info=dict(
+ summary='Existing LCD',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 1),
+ author_url=None,
+ id=commit1._id,
+ shortlink=self.repo.shorthand_for_commit(commit1._id),
+ )),
+ 'file2': dict(
+ type='BLOB',
+ name='file2',
+ commit_info=dict(
+ summary='Commit 4',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 4),
+ author_url=None,
+ id=commit4._id,
+ shortlink=self.repo.shorthand_for_commit(commit4._id),
+ )),
+ 'file3': dict(
+ type='BLOB',
+ name='file3',
+ commit_info=dict(
+ summary='Commit 3',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 3),
+ author_url=None,
+ id=commit3._id,
+ shortlink=self.repo.shorthand_for_commit(commit3._id),
+ )),
+ 'file4': dict(
+ type='BLOB',
+ name='file4',
+ commit_info=dict(
+ summary='Commit 4',
+ author='test',
+ author_email='test@example.com',
+ date=datetime(2013, 1, 4),
+ author_url=None,
+ id=commit4._id,
+ shortlink=self.repo.shorthand_for_commit(commit4._id),
+ )),
+ })
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 3501e38..222cc88 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -288,6 +288,10 @@ class GitImplementation(M.RepositoryImplementation):
tree = self.refresh_tree_info(ci.tree, set())
return tree._id
+ def compute_last_commit_info(self, commit, path, other_commit_ids=None):
+ if other_commit_ids is None:
+ other_commit_ids = []
+
class _OpenedGitBlob(object):
CHUNK_SIZE=4096
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/ForgeHg/forgehg/model/hg.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/model/hg.py b/ForgeHg/forgehg/model/hg.py
index 22d9227..bc0330d 100644
--- a/ForgeHg/forgehg/model/hg.py
+++ b/ForgeHg/forgehg/model/hg.py
@@ -291,4 +291,8 @@ class HgImplementation(M.RepositoryImplementation):
tree = self.refresh_tree_info(fake_tree, set())
return tree._id
+ def compute_last_commit_info(self, commit, path, other_commit_ids=None):
+ if other_commit_ids is None:
+ other_commit_ids = []
+
Mapper.compile_all()
http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e4e9fe84/ForgeSVN/forgesvn/model/svn.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/model/svn.py b/ForgeSVN/forgesvn/model/svn.py
index 48d2c9c..6af7aa1 100644
--- a/ForgeSVN/forgesvn/model/svn.py
+++ b/ForgeSVN/forgesvn/model/svn.py
@@ -76,7 +76,7 @@ class Repository(M.Repository):
while ci is not None and limit > 0:
yield ci._id
limit -= 1
- ci = ci.parent()
+ ci = ci.get_parent()
def latest(self, branch=None):
if self._impl is None: return None
@@ -383,6 +383,9 @@ class SVNImplementation(M.RepositoryImplementation):
trees_doc.m.save(safe=False)
return tree_id
+ def compute_last_commit_info(self, commit, path, other_commit_ids=None):
+ pass
+
def _tree_oid(self, commit_id, path):
data = 'tree\n%s\n%s' % (commit_id, h.really_unicode(path))
return sha1(data.encode('utf-8')).hexdigest()