Copying extra revisions in sprout
James Westby
jw+debian at jameswestby.net
Mon Oct 1 21:46:29 BST 2007
Hi all,
For a while now I have been trying to come up with a way to accomplish a
certain task with bzr. I have considered various implementations, but
none of them fit the bzr model and still appear clean from the outside.
Two of the four leading solutions at the moment both avoid the ugliness
of the other two, but also both require (or at least massively benefit from
in one case) extra revisions to be copied when branching. By extra revisions
I mean revisions that are in the repository, but not referenced by the branch,
and probably not by any branch.
I have been looking at how complicated this would be to implement, and
it doesn't look too hard (see my diff at the end for what I have now).
However I now have to copy a file named .bzr/branch/extra-heads when
branching, which I think means a format bump doesn't it?
So, is there any opposition to this approach, or a better way to achieve
it?
For reference the four solutions look something like
1. Have a git-like many branches in one location.
2. Carry patches around with base revisions to apply to for 3 way
merging.
3. Use normal bzr branches and have commands to work on multiple
branches.
4. Carry patches around, with bundles to reconstruct the bases for 3 way
merging.
Thanks,
James
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2007-09-25 19:04:31 +0000
+++ bzrlib/builtins.py 2007-10-01 20:25:24 +0000
@@ -890,9 +890,22 @@
raise errors.BzrCommandError('Parent of "%s" does not exist.'
% to_location)
try:
+ extra_revs = []
+ try:
+ extra_heads_file = br_from.control_files.get('extra-heads')
+ try:
+ for line in extra_heads_file.readlines():
+ if line.endswith('\n'):
+ line = line[:-1]
+ extra_revs.append(line)
+ finally:
+ extra_heads_file.close()
+ except errors.NoSuchFile:
+ pass
Yes, its ugly and unportable.
# preserve whatever source format we have.
dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
- possible_transports=[to_transport])
+ possible_transports=[to_transport],
+ extra_revisions=extra_revs)
branch = dir.open_branch()
except errors.NoSuchRevision:
to_transport.delete_tree('.')
=== modified file 'bzrlib/bzrdir.py'
--- bzrlib/bzrdir.py 2007-09-21 07:29:37 +0000
+++ bzrlib/bzrdir.py 2007-10-01 20:13:13 +0000
@@ -775,7 +775,8 @@
return self.cloning_metadir()
def sprout(self, url, revision_id=None, force_new_repo=False,
- recurse='down', possible_transports=None):
+ recurse='down', possible_transports=None,
+ extra_revisions=None):
"""Create a copy of this bzrdir prepared for use as a new line of
development.
@@ -788,11 +789,17 @@
if revision_id is not None, then the clone operation may tune
itself to download less data.
+
+ :param extra_revisions: if not None, a list of revision ids that,
+ along with their ancestors should be in the new bzrdir.
+ They must be present in the source bzrdir.
"""
target_transport = get_transport(url, possible_transports)
target_transport.ensure_base()
cloning_format = self.cloning_metadir()
result = cloning_format.initialize_on_transport(target_transport)
+ if extra_revisions is None:
+ extra_revisions = []
try:
source_branch = self.open_branch()
source_repository = source_branch.repository
@@ -818,6 +825,8 @@
# have source, and want to make a new target repo
result_repo = source_repository.sprout(result,
revision_id=revision_id)
+ for extra_rev in extra_revisions:
+ result_repo.fetch(source_repository, revision_id=extra_rev)
else:
# fetch needed content into target.
if source_repository is not None:
@@ -826,6 +835,9 @@
# revision_id=revision_id)
# so we can override the copy method
result_repo.fetch(source_repository, revision_id=revision_id)
+ for extra_rev in extra_revisions:
+ result_repo.fetch(source_repository,
+ revision_id=extra_rev)
if source_branch is not None:
source_branch.sprout(result, revision_id=revision_id)
else:
=== modified file 'bzrlib/tests/blackbox/test_branch.py'
--- bzrlib/tests/blackbox/test_branch.py 2007-08-03 12:59:14 +0000
+++ bzrlib/tests/blackbox/test_branch.py 2007-10-01 20:23:03 +0000
@@ -23,6 +23,7 @@
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
from bzrlib.tests.blackbox import ExternalBase
from bzrlib.tests.test_sftp_transport import TestCaseWithSFTPServer
+from bzrlib.uncommit import uncommit
from bzrlib.workingtree import WorkingTree
@@ -84,6 +85,31 @@
self.assertFalse(pushed_repo.has_revision('a-2'))
self.assertTrue(pushed_repo.has_revision('b-1'))
+ def test_branch_extra_heads(self):
+ tree = self.make_branch_and_tree('branch')
+ tree.commit('unused1', rev_id='unused1')
+ tree.commit('unused2', rev_id='unused2')
+ uncommit(tree.branch, tree=tree, revno=1)
+ tree.commit('unused3', rev_id='unused3')
+ tree.commit('unused4', rev_id='unused4')
+ uncommit(tree.branch, tree=tree, revno=1)
+ tree.commit('used', rev_id='used')
+ f = open('branch/.bzr/branch/extra-heads', 'wb')
+ try:
+ f.write('unused2\n')
+ f.write('unused4\n')
+ finally:
+ f.close()
+ self.run_bzr('branch branch new_branch')
+ new_branch = branch.Branch.open('new_branch')
+ # This is informally deprecated and issues an evil warning,
+ # but there is no other way to do this.
+ all_revs = new_branch.repository.all_revision_ids()
+ for rev_id in ['unused1', 'unused2', 'unused3', 'unused4']:
+ self.assertTrue(rev_id in all_revs,
+ "revisions from .bzr/branch/extra-heads are "
+ "not copied correctly")
+
class TestRemoteBranch(TestCaseWithSFTPServer):
=== modified file 'bzrlib/tests/test_bzrdir.py'
--- bzrlib/tests/test_bzrdir.py 2007-09-18 07:14:35 +0000
+++ bzrlib/tests/test_bzrdir.py 2007-10-01 20:01:36 +0000
@@ -28,6 +28,7 @@
help_topics,
repository,
symbol_versioning,
+ uncommit,
urlutils,
workingtree,
)
@@ -560,6 +561,30 @@
self.failUnlessExists('repo/tree2/subtree')
self.failIfExists('repo/tree2/subtree/file')
+ def test_sprout_extra_revs(self):
+ tree = self.make_branch_and_tree('branch')
+ tree.commit('unused1', rev_id='unused1')
+ tree.commit('unused2', rev_id='unused2')
+ uncommit.uncommit(tree.branch, tree=tree, revno=1)
+ tree.revert()
+ tree.commit('used', rev_id='used')
+ transport = self.get_transport('new_branch')
+ tree.branch.bzrdir.sprout(transport.base, 'used',
+ extra_revisions=['unused2'])
+ new_tree = workingtree.WorkingTree.open('new_branch')
+ rh = new_tree.branch.revision_history()
+ self.assertFalse('unused2' in rh,
+ "Sprout copied too much of the repository")
+ self.assertFalse('unused1' in rh,
+ "Sprout copied too much of the repository")
+ # This is informally deprecated and issues an evil warning,
+ # but there is no other way to do this.
+ all_revs = new_tree.branch.repository.all_revision_ids()
+ self.assertTrue('unused2' in all_revs,
+ "Sprout didn't copy extra_revisions")
+ self.assertTrue('unused1' in all_revs,
+ "Sprout didn't copy ancestors of extra_revisions")
+
class TestMeta1DirFormat(TestCaseWithTransport):
"""Tests specific to the meta1 dir format."""
--
James Westby -- GPG Key ID: B577FE13 -- http://jameswestby.net/
seccure key - (3+)k7|M*edCX/.A:n*N!>|&7U.L#9E)Tu)T0>AM - secp256r1/nistp256
More information about the bazaar
mailing list