26
26
from cStringIO import StringIO
30
29
from bzrlib.lazy_import import lazy_import
31
30
lazy_import(globals(), """
32
31
from stat import S_ISDIR
33
from warnings import warn
35
36
from bzrlib import (
209
215
t = get_transport(url)
212
# TODO: Should take a Transport
214
def create(cls, base, format=None):
219
def create(cls, base, format=None, possible_transports=None):
215
220
"""Create a new BzrDir at the url 'base'.
217
222
This will call the current default formats initialize with base
220
225
:param format: If supplied, the format of branch to create. If not
221
226
supplied, the default is used.
227
:param possible_transports: If supplied, a list of transports that
228
can be reused to share a remote connection.
223
230
if cls is not BzrDir:
224
231
raise AssertionError("BzrDir.create always creates the default"
225
232
" format, not one of %r" % cls)
226
t = get_transport(base)
233
t = get_transport(base, possible_transports)
228
235
if format is None:
229
236
format = BzrDirFormat.get_default_format()
230
return format.initialize(safe_unicode(base))
237
return format.initialize(base, possible_transports)
232
239
def create_branch(self):
233
240
"""Create a branch in this BzrDir.
268
275
def create_branch_convenience(base, force_new_repo=False,
269
force_new_tree=None, format=None):
276
force_new_tree=None, format=None,
277
possible_transports=None):
270
278
"""Create a new BzrDir, Branch and Repository at the url 'base'.
272
280
This is a convenience function - it will use an existing repository
288
296
:param force_new_repo: If True a new repository is always created.
289
297
:param force_new_tree: If True or False force creation of a tree or
290
298
prevent such creation respectively.
291
:param format: Override for the for the bzrdir format to create
299
:param format: Override for the for the bzrdir format to create.
300
:param possible_transports: An optional reusable transports list.
293
302
if force_new_tree:
294
303
# check for non local urls
295
t = get_transport(safe_unicode(base))
304
t = get_transport(base, possible_transports)
296
305
if not isinstance(t, LocalTransport):
297
306
raise errors.NotLocalUrl(base)
298
bzrdir = BzrDir.create(base, format)
307
bzrdir = BzrDir.create(base, format, possible_transports)
299
308
repo = bzrdir._find_or_create_repository(force_new_repo)
300
309
result = bzrdir.create_branch()
301
if force_new_tree or (repo.make_working_trees() and
310
if force_new_tree or (repo.make_working_trees() and
302
311
force_new_tree is None):
304
313
bzrdir.create_workingtree()
305
314
except errors.NotLocalUrl:
319
@deprecated_function(zero_ninetyone)
310
320
def create_repository(base, shared=False, format=None):
311
321
"""Create a new BzrDir and Repository at the url 'base'.
321
331
This must be overridden as an instance method in child classes, where
322
332
it should take no parameters and construct whatever repository format
323
333
that child class desires.
335
This method is deprecated, please call create_repository on a bzrdir
325
338
bzrdir = BzrDir.create(base, format)
326
339
return bzrdir.create_repository(shared)
338
351
:return: The WorkingTree object.
340
t = get_transport(safe_unicode(base))
353
t = get_transport(base)
341
354
if not isinstance(t, LocalTransport):
342
355
raise errors.NotLocalUrl(base)
343
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
356
bzrdir = BzrDir.create_branch_and_repo(base,
344
357
force_new_repo=True,
345
358
format=format).bzrdir
346
359
return bzrdir.create_workingtree()
520
533
return BzrDir.open(base, _unsupported=True)
523
def open(base, _unsupported=False):
536
def open(base, _unsupported=False, possible_transports=None):
524
537
"""Open an existing bzrdir, rooted at 'base' (url)
526
539
_unsupported is a private parameter to the BzrDir class.
528
t = get_transport(base)
541
t = get_transport(base, possible_transports=possible_transports)
529
542
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
581
594
raise NotImplementedError(self.open_branch)
584
def open_containing(url):
597
def open_containing(url, possible_transports=None):
585
598
"""Open an existing branch which contains url.
587
600
:param url: url to search from.
588
601
See open_containing_from_transport for more detail.
590
return BzrDir.open_containing_from_transport(get_transport(url))
603
transport = get_transport(url, possible_transports)
604
return BzrDir.open_containing_from_transport(transport)
593
607
def open_containing_from_transport(a_transport):
743
757
return self.cloning_metadir()
745
759
def sprout(self, url, revision_id=None, force_new_repo=False,
760
recurse='down', possible_transports=None):
747
761
"""Create a copy of this bzrdir prepared for use as a new line of
757
771
if revision_id is not None, then the clone operation may tune
758
772
itself to download less data.
760
target_transport = get_transport(url)
774
target_transport = get_transport(url, possible_transports)
761
775
target_transport.ensure_base()
762
776
cloning_format = self.cloning_metadir()
763
777
result = cloning_format.initialize_on_transport(target_transport)
784
798
result.create_repository()
785
799
elif source_repository is not None and result_repo is None:
786
800
# have source, and want to make a new target repo
787
result_repo = source_repository.sprout(result, revision_id=revision_id)
801
result_repo = source_repository.sprout(result,
802
revision_id=revision_id)
789
804
# fetch needed content into target.
790
805
if source_repository is not None:
791
806
# would rather do
792
# source_repository.copy_content_into(result_repo, revision_id=revision_id)
807
# source_repository.copy_content_into(result_repo,
808
# revision_id=revision_id)
793
809
# so we can override the copy method
794
810
result_repo.fetch(source_repository, revision_id=revision_id)
795
811
if source_branch is not None:
796
812
source_branch.sprout(result, revision_id=revision_id)
798
814
result.create_branch()
799
# TODO: jam 20060426 we probably need a test in here in the
800
# case that the newly sprouted branch is a remote one
801
if result_repo is None or result_repo.make_working_trees():
815
if isinstance(target_transport, LocalTransport) and (
816
result_repo is None or result_repo.make_working_trees()):
802
817
wt = result.create_workingtree()
958
973
self._check_supported(format, unsupported)
959
974
return format.open(self, _found=True)
961
def sprout(self, url, revision_id=None, force_new_repo=False):
976
def sprout(self, url, revision_id=None, force_new_repo=False,
977
possible_transports=None):
962
978
"""See BzrDir.sprout()."""
963
979
from bzrlib.workingtree import WorkingTreeFormat2
964
980
self._make_tail(url)
1068
1084
wt = self.open_workingtree(recommend_upgrade=False)
1069
1085
repository = wt.branch.repository
1070
1086
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1071
wt.revert([], old_tree=empty)
1087
wt.revert(old_tree=empty)
1072
1088
self.destroy_workingtree_metadata()
1074
1090
def destroy_workingtree_metadata(self):
1288
1304
raise NotImplementedError(self.get_converter)
1290
def initialize(self, url):
1306
def initialize(self, url, possible_transports=None):
1291
1307
"""Create a bzr control dir at this url and return an opened copy.
1293
1309
Subclasses should typically override initialize_on_transport
1294
1310
instead of this method.
1296
return self.initialize_on_transport(get_transport(url))
1312
return self.initialize_on_transport(get_transport(url,
1313
possible_transports))
1298
1315
def initialize_on_transport(self, transport):
1299
1316
"""Initialize a new bzrdir in the base directory of a Transport."""
1788
1805
def _convert_working_inv(self):
1789
1806
inv = xml4.serializer_v4.read_inventory(
1790
1807
self.branch.control_files.get('inventory'))
1791
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1808
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1792
1809
# FIXME inventory is a working tree change.
1793
1810
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1873
1890
present_parents = [p for p in rev.parent_ids
1874
1891
if p not in self.absent_revisions]
1875
1892
self._convert_revision_contents(rev, inv, present_parents)
1876
self._store_new_weave(rev, inv, present_parents)
1893
self._store_new_inv(rev, inv, present_parents)
1877
1894
self.converted_revs.add(rev_id)
1879
def _store_new_weave(self, rev, inv, present_parents):
1896
def _store_new_inv(self, rev, inv, present_parents):
1880
1897
# the XML is now updated with text versions
1882
1899
entries = inv.iter_entries()
1887
1904
(file_id, rev.revision_id)
1888
1905
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1889
1906
new_inv_sha1 = sha_string(new_inv_xml)
1890
self.inv_weave.add_lines(rev.revision_id,
1907
self.inv_weave.add_lines(rev.revision_id,
1891
1908
present_parents,
1892
1909
new_inv_xml.splitlines(True))
1893
1910
rev.inventory_sha1 = new_inv_sha1
1918
1935
w = Weave(file_id)
1919
1936
self.text_weaves[file_id] = w
1920
1937
text_changed = False
1921
previous_entries = ie.find_previous_heads(parent_invs,
1925
for old_revision in previous_entries:
1926
# if this fails, its a ghost ?
1927
assert old_revision in self.converted_revs, \
1928
"Revision {%s} not in converted_revs" % old_revision
1938
parent_candiate_entries = ie.parent_candidates(parent_invs)
1939
for old_revision in parent_candiate_entries.keys():
1940
# if this fails, its a ghost ?
1941
assert old_revision in self.converted_revs, \
1942
"Revision {%s} not in converted_revs" % old_revision
1943
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1944
# XXX: Note that this is unordered - and this is tolerable because
1945
# the previous code was also unordered.
1946
previous_entries = dict((head, parent_candiate_entries[head]) for head
1929
1948
self.snapshot_ie(previous_entries, ie, w, rev_id)
1931
1950
assert getattr(ie, 'revision', None) is not None
1952
def get_parents(self, revision_ids):
1953
for revision_id in revision_ids:
1954
yield self.revisions[revision_id].parent_ids
1933
1956
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1934
1957
# TODO: convert this logic, which is ~= snapshot to
1935
1958
# a call to:. This needs the path figured out. rather than a work_tree
1944
1967
ie.revision = previous_ie.revision
1946
1969
if ie.has_text():
1947
text = self.branch.repository.text_store.get(ie.text_id)
1970
text = self.branch.repository.weave_store.get(ie.text_id)
1948
1971
file_lines = text.readlines()
1949
1972
assert sha_strings(file_lines) == ie.text_sha1
1950
1973
assert sum(map(len, file_lines)) == ie.text_size
2217
2240
def initialize_on_transport(self, transport):
2219
2242
# hand off the request to the smart server
2220
medium = transport.get_smart_medium()
2243
shared_medium = transport.get_shared_medium()
2221
2244
except errors.NoSmartMedium:
2222
2245
# TODO: lookup the local format from a server hint.
2223
2246
local_dir_format = BzrDirMetaFormat1()
2224
2247
return local_dir_format.initialize_on_transport(transport)
2225
client = _SmartClient(medium)
2248
client = _SmartClient(shared_medium)
2226
2249
path = client.remote_path_from_transport(transport)
2227
response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
2250
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2228
2252
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2229
2253
return remote.RemoteBzrDir(transport)
2343
2367
def help_topic(self, topic):
2344
2368
output = textwrap.dedent("""\
2345
Bazaar directory formats
2346
------------------------
2348
2369
These formats can be used for creating branches, working trees, and
2373
default_realkey = None
2352
2374
default_help = self.get_help('default')
2353
2375
help_pairs = []
2354
2376
for key in self.keys():
2363
2385
def wrapped(key, help, info):
2364
2386
if info.native:
2365
2387
help = '(native) ' + help
2366
return ' %s:\n%s\n\n' % (key,
2388
return ':%s:\n%s\n\n' % (key,
2367
2389
textwrap.fill(help, initial_indent=' ',
2368
2390
subsequent_indent=' '))
2369
output += wrapped('%s/default' % default_realkey, default_help,
2370
self.get_info('default'))
2391
if default_realkey is not None:
2392
output += wrapped(default_realkey, '(default) %s' % default_help,
2393
self.get_info('default'))
2371
2394
deprecated_pairs = []
2372
2395
for key, help in help_pairs:
2373
2396
info = self.get_info(key)
2379
2402
output += wrapped(key, help, info)
2380
2403
if len(deprecated_pairs) > 0:
2381
output += "Deprecated formats\n------------------\n\n"
2404
output += "Deprecated formats are shown below.\n\n"
2382
2405
for key, help in deprecated_pairs:
2383
2406
info = self.get_info(key)
2384
2407
output += wrapped(key, help, info)