def test_some_commits(self): branch = self.make_branch('test') tree = branch.bzrdir.create_workingtree() tree.commit('acommit', rev_id='A') tree.commit('bcommit', rev_id='B') tree.commit('ccommit', rev_id='C') self.assertEquals( ['A', 'B', 'C'], branch_revision_history(tree.branch))
def test_some_commits(self): branch = self.make_branch('test') tree = branch.bzrdir.create_workingtree() tree.commit('acommit', rev_id='A') tree.commit('bcommit', rev_id='B') tree.commit('ccommit', rev_id='C') self.assertEquals(['A', 'B', 'C'], branch_revision_history(tree.branch))
def test_revisionsToInsert_linear(self): # If the branch has a linear ancestry, revisionsToInsert() should # yield each revision along with a sequence number, starting at 1. self.commitRevision(rev_id="rev-1") bzrsync = self.makeBzrSync(self.db_branch) bzr_history = branch_revision_history(self.bzr_branch) added_ancestry = bzrsync.getAncestryDelta(self.bzr_branch)[0] result = bzrsync.revisionsToInsert(bzr_history, self.bzr_branch.revno(), added_ancestry) self.assertEqual({"rev-1": 1}, dict(result))
def test_revisionsToInsert_linear(self): # If the branch has a linear ancestry, revisionsToInsert() should # yield each revision along with a sequence number, starting at 1. self.commitRevision(rev_id='rev-1') bzrsync = self.makeBzrSync(self.db_branch) bzr_history = branch_revision_history(self.bzr_branch) added_ancestry = bzrsync.getAncestryDelta(self.bzr_branch)[0] result = bzrsync.revisionsToInsert( bzr_history, self.bzr_branch.revno(), added_ancestry) self.assertEqual({'rev-1': 1}, dict(result))
def test_revisionsToInsert_branched(self): # Confirm that these revisions are generated by getRevisions with None # as the sequence 'number'. (db_branch, bzr_tree), ignored = self.makeBranchWithMerge("base", "trunk", "branch", "merge") bzrsync = self.makeBzrSync(db_branch) bzr_history = branch_revision_history(bzr_tree.branch) added_ancestry = bzrsync.getAncestryDelta(bzr_tree.branch)[0] expected = {"base": 1, "trunk": 2, "merge": 3, "branch": None} self.assertEqual( expected, dict(bzrsync.revisionsToInsert(bzr_history, bzr_tree.branch.revno(), added_ancestry)) )
def test_revisionsToInsert_branched(self): # Confirm that these revisions are generated by getRevisions with None # as the sequence 'number'. (db_branch, bzr_tree), ignored = self.makeBranchWithMerge( 'base', 'trunk', 'branch', 'merge') bzrsync = self.makeBzrSync(db_branch) bzr_history = branch_revision_history(bzr_tree.branch) added_ancestry = bzrsync.getAncestryDelta(bzr_tree.branch)[0] expected = {'base': 1, 'trunk': 2, 'merge': 3, 'branch': None} self.assertEqual( expected, dict(bzrsync.revisionsToInsert(bzr_history, bzr_tree.branch.revno(), added_ancestry)))
def iterAddedMainline(self): """Iterate through revisions added to the mainline.""" repository = self.bzr_branch.repository added_revisions = repository.get_graph().find_unique_ancestors( self.last_revision_id, [self.last_scanned_id]) # Avoid hitting the database since bzrlib makes it easy to check. # There are possibly more efficient ways to get the mainline # revisions, but this is simple and it works. history = branch_revision_history(self.bzr_branch) for num, revid in enumerate(history): if revid in added_revisions: yield repository.get_revision(revid), num + 1
def syncBranch(self, bzr_branch): """Synchronize the database view of a branch with Bazaar data. `bzr_branch` must be read locked. Several tables must be updated: * Revision: there must be one Revision row for each revision in the branch ancestry. If the row for a revision that has just been added to the branch is already present, it must be checked for consistency. * BranchRevision: there must be one BrancheRevision row for each revision in the branch ancestry. If history revisions became merged revisions, the corresponding rows must be changed. * Branch: the branch-scanner status information must be updated when the sync is complete. """ self.logger.info("Scanning branch: %s", self.db_branch.unique_name) self.logger.info(" from %s", bzr_branch.base) # Get the history and ancestry from the branch first, to fail early # if something is wrong with the branch. self.logger.info("Retrieving history from bzrlib.") bzr_history = branch_revision_history(bzr_branch) # The BranchRevision, Revision and RevisionParent tables are only # written to by the branch-scanner, so they are not subject to # write-lock contention. Update them all in a single transaction to # improve the performance and allow garbage collection in the future. db_ancestry, db_history = self.retrieveDatabaseAncestry() (new_ancestry, branchrevisions_to_delete, revids_to_insert) = self.planDatabaseChanges(bzr_branch, bzr_history, db_ancestry, db_history) new_db_revs = (new_ancestry - getUtility(IRevisionSet).onlyPresent(new_ancestry)) self.logger.info("Adding %s new revisions.", len(new_db_revs)) for revids in iter_list_chunks(list(new_db_revs), 10000): revisions = self.getBazaarRevisions(bzr_branch, revids) self.syncRevisions(bzr_branch, revisions, revids_to_insert) self.deleteBranchRevisions(branchrevisions_to_delete) self.insertBranchRevisions(bzr_branch, revids_to_insert) transaction.commit() # Synchronize the RevisionCache for this branch. getUtility(IRevisionSet).updateRevisionCacheForBranch(self.db_branch) transaction.commit() # Notify any listeners that the tip of the branch has changed, but # before we've actually updated the database branch. initial_scan = (len(db_history) == 0) notify(events.TipChanged(self.db_branch, bzr_branch, initial_scan)) # The Branch table is modified by other systems, including the web UI, # so we need to update it in a short transaction to avoid causing # timeouts in the webapp. This opens a small race window where the # revision data is updated in the database, but the Branch table has # not been updated. Since this has no ill-effect, and can only err on # the pessimistic side (tell the user the data has not yet been # updated although it has), the race is acceptable. self.updateBranchStatus(bzr_history) notify( events.ScanCompleted(self.db_branch, bzr_branch, self.logger, new_ancestry)) transaction.commit()
def test_empty(self): branch = self.make_branch('test') self.assertEquals([], branch_revision_history(branch))
def syncBranch(self, bzr_branch): """Synchronize the database view of a branch with Bazaar data. `bzr_branch` must be read locked. Several tables must be updated: * Revision: there must be one Revision row for each revision in the branch ancestry. If the row for a revision that has just been added to the branch is already present, it must be checked for consistency. * BranchRevision: there must be one BrancheRevision row for each revision in the branch ancestry. If history revisions became merged revisions, the corresponding rows must be changed. * Branch: the branch-scanner status information must be updated when the sync is complete. """ self.logger.info("Scanning branch: %s", self.db_branch.unique_name) self.logger.info(" from %s", bzr_branch.base) # Get the history and ancestry from the branch first, to fail early # if something is wrong with the branch. self.logger.info("Retrieving history from bzrlib.") bzr_history = branch_revision_history(bzr_branch) # The BranchRevision, Revision and RevisionParent tables are only # written to by the branch-scanner, so they are not subject to # write-lock contention. Update them all in a single transaction to # improve the performance and allow garbage collection in the future. db_ancestry, db_history = self.retrieveDatabaseAncestry() (new_ancestry, branchrevisions_to_delete, revids_to_insert) = self.planDatabaseChanges( bzr_branch, bzr_history, db_ancestry, db_history) new_db_revs = ( new_ancestry - getUtility(IRevisionSet).onlyPresent(new_ancestry)) self.logger.info("Adding %s new revisions.", len(new_db_revs)) for revids in iter_list_chunks(list(new_db_revs), 10000): revisions = self.getBazaarRevisions(bzr_branch, revids) self.syncRevisions(bzr_branch, revisions, revids_to_insert) self.deleteBranchRevisions(branchrevisions_to_delete) self.insertBranchRevisions(bzr_branch, revids_to_insert) transaction.commit() # Synchronize the RevisionCache for this branch. getUtility(IRevisionSet).updateRevisionCacheForBranch(self.db_branch) transaction.commit() # Notify any listeners that the tip of the branch has changed, but # before we've actually updated the database branch. initial_scan = (len(db_history) == 0) notify(events.TipChanged(self.db_branch, bzr_branch, initial_scan)) # The Branch table is modified by other systems, including the web UI, # so we need to update it in a short transaction to avoid causing # timeouts in the webapp. This opens a small race window where the # revision data is updated in the database, but the Branch table has # not been updated. Since this has no ill-effect, and can only err on # the pessimistic side (tell the user the data has not yet been # updated although it has), the race is acceptable. self.updateBranchStatus(bzr_history) notify( events.ScanCompleted( self.db_branch, bzr_branch, self.logger, new_ancestry)) transaction.commit()