launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #01144
[Merge] lp:~abentley/launchpad/incremental-diff-driveby into lp:launchpad/devel
Aaron Bentley has proposed merging lp:~abentley/launchpad/incremental-diff-driveby into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
= Summary =
A set of drivebys to support incremental diffs. Although incremental diffs
will be merged into db-devel, the drivebys will land in devel to reduce skew.
== Proposed fix ==
N/A
== Pre-implementation notes ==
Moving view code into the model was discussed with thumper.
== Implementation details ==
revision_end_date and getRevisionsSinceReviewStart are moved from the view to
the model. getRevisionsSinceReviewStart uses itertools.groupby instead of
reimplementing it. _getNewerRevisions is extracted from
getRevisionsSinceReviewStart.
bzrutils.read_locked allows branches to be locked using with statement.
DirectBranchCommit allows merge parents to be specified.
DiffTestCase.commitFile allows merge parents to be specified.
add_revision_to_branch produces realistic parent references.
Revision.getLefthandParent provides convenient access to the lefthand parent of
the revision.
Literal tab characters are replaced with \t to please lint.
== Tests ==
bin/test -v -t test_commit_uses_merge_parents -t TestGetRevisionsSinceReviewStart -t TestRevisionEndDate
== Demo and Q/A ==
None
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/code/configure.zcml
lib/lp/code/interfaces/branchmergeproposal.py
lib/lp/code/model/branchmergeproposal.py
lib/lp/code/model/tests/test_branchmergeproposal.py
lib/lp/code/tests/helpers.py
lib/lp/code/browser/tests/test_branchmergeproposal.py
lib/lp/code/model/directbranchcommit.py
lib/lp/codehosting/bzrutils.py
lib/lp/code/interfaces/revision.py
lib/lp/code/model/tests/test_diff.py
lib/lp/code/browser/branchmergeproposal.py
lib/lp/code/tests/test_directbranchcommit.py
lib/lp/code/model/revision.py
./lib/lp/code/interfaces/branchmergeproposal.py
715: E302 expected 2 blank lines, found 1
./lib/lp/code/model/tests/test_branchmergeproposal.py
192: E231 missing whitespace after ','
194: E231 missing whitespace after ','
./lib/lp/codehosting/bzrutils.py
165: E301 expected 1 blank line, found 0
343: E301 expected 1 blank line, found 0
./lib/lp/code/browser/branchmergeproposal.py
168: E301 expected 1 blank line, found 0
178: E301 expected 1 blank line, found 0
201: E202 whitespace before '}'
1441: E301 expected 1 blank line, found 0
--
https://code.launchpad.net/~abentley/launchpad/incremental-diff-driveby/+merge/36156
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/incremental-diff-driveby into lp:launchpad/devel.
=== modified file 'lib/lp/code/browser/branchmergeproposal.py'
--- lib/lp/code/browser/branchmergeproposal.py 2010-08-24 10:45:57 +0000
+++ lib/lp/code/browser/branchmergeproposal.py 2010-09-21 15:06:00 +0000
@@ -32,7 +32,6 @@
'latest_proposals_for_each_branch',
]
-from collections import defaultdict
import operator
from lazr.delegates import delegates
@@ -200,7 +199,7 @@
'Approved [Merge Failed]',
BranchMergeProposalStatus.QUEUED : 'Queued',
BranchMergeProposalStatus.SUPERSEDED : 'Superseded'
- }
+ }
return friendly_texts[self.context.queue_status]
@property
@@ -212,8 +211,7 @@
result = ''
if self.context.queue_status in (
BranchMergeProposalStatus.CODE_APPROVED,
- BranchMergeProposalStatus.REJECTED
- ):
+ BranchMergeProposalStatus.REJECTED):
formatter = DateTimeFormatterAPI(self.context.date_reviewed)
result = '%s %s' % (
self.context.reviewer.displayname,
@@ -601,46 +599,15 @@
"""Location of page for commenting on this proposal."""
return canonical_url(self.context, view_name='+comment')
- @property
- def revision_end_date(self):
- """The cutoff date for showing revisions.
-
- If the proposal has been merged, then we stop at the merged date. If
- it is rejected, we stop at the reviewed date. For superseded
- proposals, it should ideally use the non-existant date_last_modified,
- but could use the last comment date.
- """
- status = self.context.queue_status
- if status == BranchMergeProposalStatus.MERGED:
- return self.context.date_merged
- if status == BranchMergeProposalStatus.REJECTED:
- return self.context.date_reviewed
- # Otherwise return None representing an open end date.
- return None
-
- def _getRevisionsSinceReviewStart(self):
- """Get the grouped revisions since the review started."""
- # Work out the start of the review.
- start_date = self.context.date_review_requested
- if start_date is None:
- start_date = self.context.date_created
- source = DecoratedBranch(self.context.source_branch)
- resultset = source.getMainlineBranchRevisions(
- start_date, self.revision_end_date, oldest_first=True)
- # Now group by date created.
- groups = defaultdict(list)
- for branch_revision, revision, revision_author in resultset:
- groups[revision.date_created].append(branch_revision)
- return [
- CodeReviewNewRevisions(revisions, date, source)
- for date, revisions in groups.iteritems()]
-
@cachedproperty
def conversation(self):
"""Return a conversation that is to be rendered."""
# Sort the comments by date order.
- comments = self._getRevisionsSinceReviewStart()
merge_proposal = self.context
+ groups = merge_proposal.getRevisionsSinceReviewStart()
+ source = DecoratedBranch(merge_proposal.source_branch)
+ comments = [CodeReviewNewRevisions(list(revisions), date, source)
+ for date, revisions in groups]
while merge_proposal is not None:
from_superseded = merge_proposal != self.context
comments.extend(
@@ -946,7 +913,6 @@
self.cancel_url = self.next_url
super(MergeProposalEditView, self).initialize()
-
def _getRevisionId(self, data):
"""Translate the revision number that was entered into a revision id.
@@ -1473,8 +1439,8 @@
"""Render an `IText` as XHTML using the webservice."""
formatter = FormattersAPI
def renderer(value):
- nomail = formatter(value).obfuscate_email()
- html = formatter(nomail).text_to_html()
+ nomail = formatter(value).obfuscate_email()
+ html = formatter(nomail).text_to_html()
return html.encode('utf-8')
return renderer
=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
--- lib/lp/code/browser/tests/test_branchmergeproposal.py 2010-08-22 21:34:16 +0000
+++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2010-09-21 15:06:00 +0000
@@ -12,7 +12,6 @@
timedelta,
)
from difflib import unified_diff
-import operator
import unittest
import pytz
@@ -46,7 +45,6 @@
PreviewDiff,
StaticDiff,
)
-from lp.code.tests.helpers import add_revision_to_branch
from lp.testing import (
login_person,
TestCaseWithFactory,
@@ -577,63 +575,6 @@
view = create_initialized_view(self.bmp, '+index')
self.assertEqual([], view.linked_bugs)
- def test_revision_end_date_active(self):
- # An active merge proposal will have None as an end date.
- bmp = self.factory.makeBranchMergeProposal()
- view = create_initialized_view(bmp, '+index')
- self.assertIs(None, view.revision_end_date)
-
- def test_revision_end_date_merged(self):
- # An merged proposal will have the date merged as an end date.
- bmp = self.factory.makeBranchMergeProposal(
- set_state=BranchMergeProposalStatus.MERGED)
- view = create_initialized_view(bmp, '+index')
- self.assertEqual(bmp.date_merged, view.revision_end_date)
-
- def test_revision_end_date_rejected(self):
- # An rejected proposal will have the date reviewed as an end date.
- bmp = self.factory.makeBranchMergeProposal(
- set_state=BranchMergeProposalStatus.REJECTED)
- view = create_initialized_view(bmp, '+index')
- self.assertEqual(bmp.date_reviewed, view.revision_end_date)
-
- def assertRevisionGroups(self, bmp, expected_groups):
- """Get the groups for the merge proposal and check them."""
- view = create_initialized_view(bmp, '+index')
- groups = view._getRevisionsSinceReviewStart()
- view_groups = [
- obj.revisions for obj in sorted(
- groups, key=operator.attrgetter('date'))]
- self.assertEqual(expected_groups, view_groups)
-
- def test_getRevisionsSinceReviewStart_no_revisions(self):
- # If there have been no revisions pushed since the start of the
- # review, the method returns an empty list.
- self.assertRevisionGroups(self.bmp, [])
-
- def test_getRevisionsSinceReviewStart_groups(self):
- # Revisions that were scanned at the same time have the same
- # date_created. These revisions are grouped together.
- review_date = datetime(2009, 9, 10, tzinfo=pytz.UTC)
- bmp = self.factory.makeBranchMergeProposal(
- date_created=review_date)
- login_person(bmp.registrant)
- bmp.requestReview(review_date)
- revision_date = review_date + timedelta(days=1)
- revisions = []
- for date in range(2):
- revisions.append(
- add_revision_to_branch(
- self.factory, bmp.source_branch, revision_date))
- revisions.append(
- add_revision_to_branch(
- self.factory, bmp.source_branch, revision_date))
- revision_date += timedelta(days=1)
- expected_groups = [
- [revisions[0], revisions[1]],
- [revisions[2], revisions[3]]]
- self.assertRevisionGroups(bmp, expected_groups)
-
def test_include_superseded_comments(self):
for x, time in zip(range(3), time_counter()):
if x != 0:
@@ -757,7 +698,6 @@
layer = LaunchpadFunctionalLayer
-
def _makeCommentFromEmailWithAttachment(self, attachment_body):
# Make an email message with an attachment, and create a code
# review comment from it.
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2010-09-20 22:16:32 +0000
+++ lib/lp/code/configure.zcml 2010-09-21 15:06:00 +0000
@@ -236,8 +236,10 @@
votes
all_comments
related_bugs
+ revision_end_date
isMergable
getComment
+ getRevisionsSinceReviewStart
getNotificationRecipients
getVoteReference
isValidTransition
=== modified file 'lib/lp/code/interfaces/branchmergeproposal.py'
--- lib/lp/code/interfaces/branchmergeproposal.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/interfaces/branchmergeproposal.py 2010-09-21 15:06:00 +0000
@@ -290,6 +290,13 @@
def getComment(id):
"""Return the CodeReviewComment with the specified ID."""
+ def getRevisionsSinceReviewStart():
+ """Return all the revisions added since the review began.
+
+ Revisions are grouped by creation (i.e. push) time.
+ :return: An iterator of (date, iterator of revision data)
+ """
+
def getVoteReference(id):
"""Return the CodeReviewVoteReference with the specified ID."""
@@ -518,8 +525,8 @@
source branch.
:param target_revision_id: The revision id that was used from the
target branch.
- :param prerequisite_revision_id: The revision id that was used from the
- prerequisite branch.
+ :param prerequisite_revision_id: The revision id that was used from
+ the prerequisite branch.
:param conflicts: Text describing the conflicts if any.
"""
=== modified file 'lib/lp/code/interfaces/revision.py'
--- lib/lp/code/interfaces/revision.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/interfaces/revision.py 2010-09-21 15:06:00 +0000
@@ -73,6 +73,9 @@
:return: A `Branch` or None if an appropriate branch cannot be found.
"""
+ def getLefthandParent():
+ """Return lefthand parent of revision, or None if not in database."""
+
class IRevisionAuthor(Interface):
"""Committer of a Bazaar revision."""
=== modified file 'lib/lp/code/model/branchmergeproposal.py'
--- lib/lp/code/model/branchmergeproposal.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/model/branchmergeproposal.py 2010-09-21 15:06:00 +0000
@@ -13,7 +13,7 @@
]
from email.Utils import make_msgid
-
+from itertools import groupby
from sqlobject import (
ForeignKey,
IntCol,
@@ -777,6 +777,45 @@
Store.of(self).flush()
return self.preview_diff
+ @property
+ def revision_end_date(self):
+ """The cutoff date for showing revisions.
+
+ If the proposal has been merged, then we stop at the merged date. If
+ it is rejected, we stop at the reviewed date. For superseded
+ proposals, it should ideally use the non-existant date_last_modified,
+ but could use the last comment date.
+ """
+ status = self.queue_status
+ if status == BranchMergeProposalStatus.MERGED:
+ return self.date_merged
+ if status == BranchMergeProposalStatus.REJECTED:
+ return self.date_reviewed
+ # Otherwise return None representing an open end date.
+ return None
+
+ def _getNewerRevisions(self):
+ start_date = self.date_review_requested
+ if start_date is None:
+ start_date = self.date_created
+ return self.source_branch.getMainlineBranchRevisions(
+ start_date, self.revision_end_date, oldest_first=True)
+
+ def getRevisionsSinceReviewStart(self):
+ """Get the grouped revisions since the review started."""
+ resultset = self._getNewerRevisions()
+ # Work out the start of the review.
+ branch_revisions = (
+ branch_revision for branch_revision, revision, revision_author
+ in resultset)
+ # Now group by date created.
+ gby = groupby(branch_revisions, lambda r: r.revision.date_created)
+ # Use a generator expression to wrap the custom iterator so it doesn't
+ # get security-proxied.
+ return (
+ (date, (revision for revision in revisions))
+ for date, revisions in gby)
+
class BranchMergeProposalGetter:
"""See `IBranchMergeProposalGetter`."""
=== modified file 'lib/lp/code/model/directbranchcommit.py'
--- lib/lp/code/model/directbranchcommit.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/model/directbranchcommit.py 2010-09-21 15:06:00 +0000
@@ -55,7 +55,8 @@
is_locked = False
commit_builder = None
- def __init__(self, db_branch, committer=None, no_race_check=False):
+ def __init__(self, db_branch, committer=None, no_race_check=False,
+ merge_parents=None):
"""Create context for direct commit to branch.
Before constructing a `DirectBranchCommit`, set up a server that
@@ -107,6 +108,7 @@
raise
self.files = set()
+ self.merge_parents = merge_parents
def _getDir(self, path):
"""Get trans_id for directory "path." Create if necessary."""
@@ -200,7 +202,8 @@
# required to generate the revision-id.
with override_environ(BZR_EMAIL=committer_id):
new_rev_id = self.transform_preview.commit(
- self.bzrbranch, commit_message, committer=committer_id)
+ self.bzrbranch, commit_message, self.merge_parents,
+ committer=committer_id)
IMasterObject(self.db_branch).branchChanged(
get_stacked_on_url(self.bzrbranch), new_rev_id,
self.db_branch.control_format, self.db_branch.branch_format,
=== modified file 'lib/lp/code/model/revision.py'
--- lib/lp/code/model/revision.py 2010-08-27 02:11:36 +0000
+++ lib/lp/code/model/revision.py 2010-09-21 15:06:00 +0000
@@ -18,6 +18,7 @@
)
import email
+from bzrlib.revision import NULL_REVISION
import pytz
from sqlobject import (
BoolCol,
@@ -39,7 +40,6 @@
)
from storm.locals import (
Bool,
- DateTime,
Int,
Min,
Reference,
@@ -118,6 +118,13 @@
"""
return [parent.parent_id for parent in self.parents]
+ def getLefthandParent(self):
+ if len(self.parent_ids) == 0:
+ parent_id = NULL_REVISION
+ else:
+ parent_id = self.parent_ids[0]
+ return RevisionSet().getByRevisionId(parent_id)
+
def getProperties(self):
"""See `IRevision`."""
return dict((prop.name, prop.value) for prop in self.properties)
=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2010-09-21 15:06:00 +0000
@@ -7,7 +7,7 @@
__metaclass__ = type
-from datetime import datetime
+from datetime import datetime, timedelta
from difflib import unified_diff
from unittest import (
TestCase,
@@ -72,6 +72,7 @@
MergeProposalCreatedJob,
UpdatePreviewDiffJob,
)
+from lp.code.tests.helpers import add_revision_to_branch
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.product import IProductSet
from lp.testing import (
@@ -108,7 +109,8 @@
self.assertTrue(url.startswith(source_branch_url))
def test_BranchMergeProposal_canonical_url_rest(self):
- # The rest of the URL for a merge proposal is +merge followed by the db id.
+ # The rest of the URL for a merge proposal is +merge followed by the
+ # db id.
bmp = self.factory.makeBranchMergeProposal()
url = canonical_url(bmp)
source_branch_url = canonical_url(bmp.source_branch)
@@ -238,7 +240,6 @@
self._attemptTransition,
proposal, to_state)
-
def assertGoodDupeTransition(self, from_state, to_state):
"""Trying to go from `from_state` to `to_state` succeeds."""
proposal = self.prepareDupeTransition(from_state)
@@ -1049,6 +1050,7 @@
else:
self.assertEqual([mp], list(active))
+
class TestBranchMergeProposalGetterGetProposals(TestCaseWithFactory):
"""Test the getProposalsForContext method."""
@@ -1118,7 +1120,6 @@
beaver, [BranchMergeProposalStatus.REJECTED], beaver)
self.assertEqual(beave_proposals.count(), 1)
-
def test_created_proposal_default_status(self):
# When we create a merge proposal using the helper method, the default
# status of the proposal is work in progress.
@@ -1799,5 +1800,67 @@
self.assertIs(None, bmp.next_preview_diff_job)
+class TestRevisionEndDate(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_revision_end_date_active(self):
+ # An active merge proposal will have None as an end date.
+ bmp = self.factory.makeBranchMergeProposal()
+ self.assertIs(None, bmp.revision_end_date)
+
+ def test_revision_end_date_merged(self):
+ # An merged proposal will have the date merged as an end date.
+ bmp = self.factory.makeBranchMergeProposal(
+ set_state=BranchMergeProposalStatus.MERGED)
+ self.assertEqual(bmp.date_merged, bmp.revision_end_date)
+
+ def test_revision_end_date_rejected(self):
+ # An rejected proposal will have the date reviewed as an end date.
+ bmp = self.factory.makeBranchMergeProposal(
+ set_state=BranchMergeProposalStatus.REJECTED)
+ self.assertEqual(bmp.date_reviewed, bmp.revision_end_date)
+
+
+class TestGetRevisionsSinceReviewStart(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def assertRevisionGroups(self, bmp, expected_groups):
+ """Get the groups for the merge proposal and check them."""
+ groups = bmp.getRevisionsSinceReviewStart()
+ revision_groups = [list(revisions) for date, revisions in groups]
+ self.assertEqual(expected_groups, revision_groups)
+
+ def test_getRevisionsSinceReviewStart_no_revisions(self):
+ # If there have been no revisions pushed since the start of the
+ # review, the method returns an empty list.
+ bmp = self.factory.makeBranchMergeProposal()
+ self.assertRevisionGroups(bmp, [])
+
+ def test_getRevisionsSinceReviewStart_groups(self):
+ # Revisions that were scanned at the same time have the same
+ # date_created. These revisions are grouped together.
+ review_date = datetime(2009, 9, 10, tzinfo=UTC)
+ bmp = self.factory.makeBranchMergeProposal(
+ date_created=review_date)
+ login_person(bmp.registrant)
+ bmp.requestReview(review_date)
+ revision_date = review_date + timedelta(days=1)
+ revisions = []
+ for date in range(2):
+ revisions.append(
+ add_revision_to_branch(
+ self.factory, bmp.source_branch, revision_date))
+ revisions.append(
+ add_revision_to_branch(
+ self.factory, bmp.source_branch, revision_date))
+ revision_date += timedelta(days=1)
+ expected_groups = [
+ [revisions[0], revisions[1]],
+ [revisions[2], revisions[3]]]
+ self.assertRevisionGroups(bmp, expected_groups)
+
+
def test_suite():
return TestLoader().loadTestsFromName(__name__)
=== modified file 'lib/lp/code/model/tests/test_diff.py'
--- lib/lp/code/model/tests/test_diff.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/model/tests/test_diff.py 2010-09-21 15:06:00 +0000
@@ -57,13 +57,14 @@
class DiffTestCase(TestCaseWithFactory):
@staticmethod
- def commitFile(branch, path, contents):
+ def commitFile(branch, path, contents, merge_parents=None):
"""Create a commit that updates a file to specified contents.
This will create or modify the file, as needed.
"""
committer = DirectBranchCommit(
- removeSecurityProxy(branch), no_race_check=True)
+ removeSecurityProxy(branch), no_race_check=True,
+ merge_parents=merge_parents)
committer.writeFile(path, contents)
try:
return committer.commit('committing')
@@ -122,7 +123,6 @@
prerequisite)
-
class TestDiff(DiffTestCase):
layer = LaunchpadFunctionalLayer
@@ -186,19 +186,19 @@
self.checkExampleMerge(diff.text)
diff_bytes = (
- "--- bar 2009-08-26 15:53:34.000000000 -0400\n"
- "+++ bar 1969-12-31 19:00:00.000000000 -0500\n"
+ "--- bar\t2009-08-26 15:53:34.000000000 -0400\n"
+ "+++ bar\t1969-12-31 19:00:00.000000000 -0500\n"
"@@ -1,3 +0,0 @@\n"
"-a\n"
"-b\n"
"-c\n"
- "--- baz 1969-12-31 19:00:00.000000000 -0500\n"
- "+++ baz 2009-08-26 15:53:57.000000000 -0400\n"
+ "--- baz\t1969-12-31 19:00:00.000000000 -0500\n"
+ "+++ baz\t2009-08-26 15:53:57.000000000 -0400\n"
"@@ -0,0 +1,2 @@\n"
"+a\n"
"+b\n"
- "--- foo 2009-08-26 15:53:23.000000000 -0400\n"
- "+++ foo 2009-08-26 15:56:43.000000000 -0400\n"
+ "--- foo\t2009-08-26 15:53:23.000000000 -0400\n"
+ "+++ foo\t2009-08-26 15:56:43.000000000 -0400\n"
"@@ -1,3 +1,4 @@\n"
" a\n"
"-b\n"
@@ -207,19 +207,19 @@
"+e\n")
diff_bytes_2 = (
- "--- bar 2009-08-26 15:53:34.000000000 -0400\n"
- "+++ bar 1969-12-31 19:00:00.000000000 -0500\n"
+ "--- bar\t2009-08-26 15:53:34.000000000 -0400\n"
+ "+++ bar\t1969-12-31 19:00:00.000000000 -0500\n"
"@@ -1,3 +0,0 @@\n"
"-a\n"
"-b\n"
"-c\n"
- "--- baz 1969-12-31 19:00:00.000000000 -0500\n"
- "+++ baz 2009-08-26 15:53:57.000000000 -0400\n"
+ "--- baz\t1969-12-31 19:00:00.000000000 -0500\n"
+ "+++ baz\t2009-08-26 15:53:57.000000000 -0400\n"
"@@ -0,0 +1,2 @@\n"
"+a\n"
"+b\n"
- "--- foo 2009-08-26 15:53:23.000000000 -0400\n"
- "+++ foo 2009-08-26 15:56:43.000000000 -0400\n"
+ "--- foo\t2009-08-26 15:53:23.000000000 -0400\n"
+ "+++ foo\t2009-08-26 15:56:43.000000000 -0400\n"
"@@ -1,3 +1,5 @@\n"
" a\n"
"-b\n"
@@ -467,7 +467,6 @@
self.assertEqual('', diff.conflicts)
self.assertFalse(diff.has_conflicts)
-
def test_fromBranchMergeProposal(self):
# Correctly generates a PreviewDiff from a BranchMergeProposal.
bmp, source_rev_id, target_rev_id = self.createExampleMerge()
=== modified file 'lib/lp/code/tests/helpers.py'
--- lib/lp/code/tests/helpers.py 2010-08-31 00:16:41 +0000
+++ lib/lp/code/tests/helpers.py 2010-09-21 15:06:00 +0000
@@ -64,9 +64,14 @@
"""
if date_created is None:
date_created = revision_date
+ parent = branch.revision_history.last()
+ if parent is None:
+ parent_ids = []
+ else:
+ parent_ids = [parent.revision.revision_id]
revision = factory.makeRevision(
revision_date=revision_date, date_created=date_created,
- log_body=commit_msg)
+ log_body=commit_msg, parent_ids=parent_ids)
if mainline:
sequence = branch.revision_count + 1
branch_revision = branch.createBranchRevision(sequence, revision)
@@ -112,7 +117,7 @@
preview.remvoed_lines_count = 13
preview.diffstat = {'file1': (3, 8), 'file2': (4, 5)}
return {
- 'eric': eric, 'fooix': fooix, 'trunk':trunk, 'feature': feature,
+ 'eric': eric, 'fooix': fooix, 'trunk': trunk, 'feature': feature,
'proposed': proposed, 'fred': fred}
=== modified file 'lib/lp/code/tests/test_directbranchcommit.py'
--- lib/lp/code/tests/test_directbranchcommit.py 2010-08-20 20:31:18 +0000
+++ lib/lp/code/tests/test_directbranchcommit.py 2010-09-21 15:06:00 +0000
@@ -99,6 +99,24 @@
branch_revision_id = self.committer.bzrbranch.last_revision()
self.assertEqual(branch_revision_id, revision_id)
+ def test_commit_uses_merge_parents(self):
+ # DirectBranchCommit.commit returns uses merge parents
+ self._tearDownCommitter()
+ # Merge parents cannot be specified for initial commit, so do an
+ # empty commit.
+ self.tree.commit('foo', committer='foo@bar', rev_id='foo')
+ committer = DirectBranchCommit(
+ self.db_branch, merge_parents=['parent-1', 'parent-2'])
+ committer.last_scanned_id = (
+ committer.bzrbranch.last_revision())
+ committer.writeFile('file.txt', 'contents')
+ revision_id = committer.commit('')
+ branch_revision_id = committer.bzrbranch.last_revision()
+ branch_revision = committer.bzrbranch.repository.get_revision(
+ branch_revision_id)
+ self.assertEqual(
+ ['parent-1', 'parent-2'], branch_revision.parent_ids[1:])
+
def test_DirectBranchCommit_aborts_cleanly(self):
# If a DirectBranchCommit is not committed, its changes do not
# go into the branch.
=== modified file 'lib/lp/codehosting/bzrutils.py'
--- lib/lp/codehosting/bzrutils.py 2010-08-20 20:31:18 +0000
+++ lib/lp/codehosting/bzrutils.py 2010-09-21 15:06:00 +0000
@@ -18,11 +18,13 @@
'identical_formats',
'install_oops_handler',
'is_branch_stackable',
+ 'read_locked',
'remove_exception_logging_hook',
'safe_open',
'UnsafeUrlSeen',
]
+from contextlib import contextmanager
import os
import sys
import threading
@@ -363,3 +365,12 @@
return branch.get_stacked_on_url()
except (NotStacked, UnstackableBranchFormat):
return None
+
+
+@contextmanager
+def read_locked(branch):
+ branch.lock_read()
+ try:
+ yield
+ finally:
+ branch.unlock()