97
_default_initializer = None
98
"""The default initializer for making new branches."""
99
"""Construct the current default format branch in a_bzrdir.
101
This creates the current default BzrDir format, and if that
102
supports multiple Branch formats, then the default Branch format
105
print "not usable until we have repositories"
106
raise NotImplementedError("not usable right now")
107
return bzrdir.BzrDir.create(base)
100
109
def __init__(self, *ignored, **ignored_too):
101
110
raise NotImplementedError('The Branch class is abstract')
113
@deprecated_method(zero_eight)
104
114
def open_downlevel(base):
105
115
"""Open a branch which may be of an old format."""
106
116
return Branch.open(base, _unsupported=True)
109
119
def open(base, _unsupported=False):
110
"""Open an existing branch, rooted at 'base' (url)
112
_unsupported is a private parameter to the Branch class.
120
"""Open the repository rooted at base.
122
For instance, if the repository is at URL/.bzr/repository,
123
Repository.open(URL) -> a Repository instance.
114
t = get_transport(base)
115
mutter("trying to open %r with transport %r", base, t)
116
format = BzrBranchFormat.find_format(t)
117
if not _unsupported and not format.is_supported():
118
# see open_downlevel to open legacy branches.
119
raise errors.UnsupportedFormatError(
120
'sorry, branch format %s not supported' % format,
121
['use a different bzr version',
122
'or remove the .bzr directory'
123
' and "bzr init" again'])
124
return format.open(t)
125
control = bzrdir.BzrDir.open(base, _unsupported)
126
return control.open_branch(_unsupported)
127
129
def open_containing(url):
403
369
if revno < 1 or revno > self.revno():
404
370
raise InvalidRevisionNumber(revno)
406
def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
407
"""Copy this branch into the existing directory to_location.
409
Returns the newly created branch object.
412
If not None, only revisions up to this point will be copied.
413
The head of the new branch will be that revision. Must be a
416
to_location -- The destination directory; must either exist and be
417
empty, or not exist, in which case it is created.
420
A local branch to copy revisions from, related to this branch.
421
This is used when branching from a remote (slow) branch, and we have
422
a local branch that might contain some relevant revisions.
425
Branch type of destination branch
427
from bzrlib.workingtree import WorkingTree
428
assert isinstance(to_location, basestring)
429
if not bzrlib.osutils.lexists(to_location):
430
os.mkdir(to_location)
431
if to_branch_type is None:
432
to_branch_type = BzrBranch
433
print "FIXME use a branch format here"
434
br_to = to_branch_type.initialize(to_location)
435
mutter("copy branch from %s to %s", self, br_to)
436
if basis_branch is not None:
437
basis_branch.push_stores(br_to)
439
revision = self.last_revision()
440
br_to.update_revisions(self, stop_revision=revision)
441
br_to.set_parent(self.base)
442
WorkingTree.create(br_to, to_location).set_root_id(self.get_root_id())
446
def fileid_involved_between_revs(self, from_revid, to_revid):
447
""" This function returns the file_id(s) involved in the
448
changes between the from_revid revision and the to_revid
451
raise NotImplementedError('fileid_involved_between_revs is abstract')
453
def fileid_involved(self, last_revid=None):
454
""" This function returns the file_id(s) involved in the
455
changes up to the revision last_revid
456
If no parametr is passed, then all file_id[s] present in the
457
repository are returned
459
raise NotImplementedError('fileid_involved is abstract')
461
def fileid_involved_by_set(self, changes):
462
""" This function returns the file_id(s) involved in the
463
changes present in the set 'changes'
465
raise NotImplementedError('fileid_involved_by_set is abstract')
467
def fileid_involved_between_revs(self, from_revid, to_revid):
468
""" This function returns the file_id(s) involved in the
469
changes between the from_revid revision and the to_revid
472
raise NotImplementedError('fileid_involved_between_revs is abstract')
474
def fileid_involved(self, last_revid=None):
475
""" This function returns the file_id(s) involved in the
476
changes up to the revision last_revid
477
If no parametr is passed, then all file_id[s] present in the
478
repository are returned
480
raise NotImplementedError('fileid_involved is abstract')
482
def fileid_involved_by_set(self, changes):
483
""" This function returns the file_id(s) involved in the
484
changes present in the set 'changes'
486
raise NotImplementedError('fileid_involved_by_set is abstract')
488
class BzrBranchFormat(object):
373
def clone(self, *args, **kwargs):
374
"""Clone this branch into to_bzrdir preserving all semantic values.
376
revision_id: if not None, the revision history in the new branch will
377
be truncated to end with revision_id.
379
# for API compatability, until 0.8 releases we provide the old api:
380
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
381
# after 0.8 releases, the *args and **kwargs should be changed:
382
# def clone(self, to_bzrdir, revision_id=None):
383
if (kwargs.get('to_location', None) or
384
kwargs.get('revision', None) or
385
kwargs.get('basis_branch', None) or
386
(len(args) and isinstance(args[0], basestring))):
387
# backwards compatability api:
388
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
389
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
392
basis_branch = args[2]
394
basis_branch = kwargs.get('basis_branch', None)
396
basis = basis_branch.bzrdir
401
revision_id = args[1]
403
revision_id = kwargs.get('revision', None)
408
# no default to raise if not provided.
409
url = kwargs.get('to_location')
410
return self.bzrdir.clone(url,
411
revision_id=revision_id,
412
basis=basis).open_branch()
414
# generate args by hand
416
revision_id = args[1]
418
revision_id = kwargs.get('revision_id', None)
422
# no default to raise if not provided.
423
to_bzrdir = kwargs.get('to_bzrdir')
424
result = self._format.initialize(to_bzrdir)
425
self.copy_content_into(result, revision_id=revision_id)
429
def sprout(self, to_bzrdir, revision_id=None):
430
"""Create a new line of development from the branch, into to_bzrdir.
432
revision_id: if not None, the revision history in the new branch will
433
be truncated to end with revision_id.
435
result = self._format.initialize(to_bzrdir)
436
self.copy_content_into(result, revision_id=revision_id)
437
result.set_parent(self.bzrdir.root_transport.base)
441
def copy_content_into(self, destination, revision_id=None):
442
"""Copy the content of self into destination.
444
revision_id: if not None, the revision history in the new branch will
445
be truncated to end with revision_id.
447
new_history = self.revision_history()
448
if revision_id is not None:
450
new_history = new_history[:new_history.index(revision_id) + 1]
452
rev = self.repository.get_revision(revision_id)
453
new_history = rev.get_history(self.repository)[1:]
454
destination.set_revision_history(new_history)
455
parent = self.get_parent()
457
destination.set_parent(parent)
460
class BranchFormat(object):
489
461
"""An encapsulation of the initialization and open routines for a format.
491
463
Formats provide three things:
503
475
object will be created every time regardless.
478
_default_format = None
479
"""The default format used for new branches."""
507
482
"""The known formats."""
510
def find_format(klass, transport):
511
"""Return the format registered for URL."""
485
def find_format(klass, a_bzrdir):
486
"""Return the format for the branch object in a_bzrdir."""
513
format_string = transport.get(".bzr/branch-format").read()
488
transport = a_bzrdir.get_branch_transport(None)
489
format_string = transport.get("format").read()
514
490
return klass._formats[format_string]
515
491
except NoSuchFile:
516
492
raise NotBranchError(path=transport.base)
518
494
raise errors.UnknownFormatError(format_string)
497
def get_default_format(klass):
498
"""Return the current default format."""
499
return klass._default_format
520
501
def get_format_string(self):
521
502
"""Return the ASCII format string that identifies this format."""
522
503
raise NotImplementedError(self.get_format_string)
524
def _find_modes(self, t):
525
"""Determine the appropriate modes for files and directories.
527
FIXME: When this merges into, or from storage,
528
this code becomes delgatable to a LockableFiles instance.
530
For now its cribbed and returns (dir_mode, file_mode)
534
except errors.TransportNotPossible:
538
dir_mode = st.st_mode & 07777
539
# Remove the sticky and execute bits for files
540
file_mode = dir_mode & ~07111
541
if not BzrBranch._set_dir_mode:
543
if not BzrBranch._set_file_mode:
545
return dir_mode, file_mode
547
def initialize(self, url):
548
"""Create a branch of this format at url and return an open branch."""
549
t = get_transport(url)
550
from bzrlib.weavefile import write_weave_v5
551
from bzrlib.weave import Weave
553
# Create an empty weave
555
bzrlib.weavefile.write_weave_v5(Weave(), sio)
556
empty_weave = sio.getvalue()
558
# Since we don't have a .bzr directory, inherit the
559
# mode from the root directory
560
temp_control = LockableFiles(t, '')
561
temp_control._transport.mkdir('.bzr',
562
mode=temp_control._dir_mode)
563
file_mode = temp_control._file_mode
565
mutter('created control directory in ' + t.base)
566
control = t.clone('.bzr')
567
dirs = ['revision-store', 'weaves']
568
lock_file = 'branch-lock'
569
utf8_files = [('README',
570
"This is a Bazaar-NG control directory.\n"
571
"Do not change any files in this directory.\n"),
572
('branch-format', self.get_format_string()),
573
('revision-history', ''),
576
files = [('inventory.weave', StringIO(empty_weave)),
579
# FIXME: RBC 20060125 dont peek under the covers
580
# NB: no need to escape relative paths that are url safe.
581
control.put(lock_file, StringIO(), mode=file_mode)
582
control_files = LockableFiles(control, lock_file)
583
control_files.lock_write()
584
control_files._transport.mkdir_multi(dirs,
585
mode=control_files._dir_mode)
587
for file, content in utf8_files:
588
control_files.put_utf8(file, content)
589
for file, content in files:
590
control_files.put(file, content)
592
control_files.unlock()
593
return BzrBranch(t, _format=self, _control_files=control_files)
505
def initialize(self, a_bzrdir):
506
"""Create a branch of this format in a_bzrdir."""
507
raise NotImplementedError(self.initialized)
595
509
def is_supported(self):
596
510
"""Is this format supported?
604
def open(self, transport):
605
"""Fill out the data in branch for the branch at url."""
606
return BzrBranch(transport, _format=self)
518
def open(self, a_bzrdir, _found=False):
519
"""Return the branch object for a_bzrdir
521
_found is a private parameter, do not use it. It is used to indicate
522
if format probing has already be done.
524
raise NotImplementedError(self.open)
609
527
def register_format(klass, format):
610
528
klass._formats[format.get_format_string()] = format
531
def set_default_format(klass, format):
532
klass._default_format = format
613
535
def unregister_format(klass, format):
614
536
assert klass._formats[format.get_format_string()] is format
615
537
del klass._formats[format.get_format_string()]
618
class BzrBranchFormat4(BzrBranchFormat):
540
class BzrBranchFormat4(BranchFormat):
619
541
"""Bzr branch format 4.
623
- TextStores for texts, inventories,revisions.
625
This format is deprecated: it indexes texts using a text it which is
626
removed in format 5; write support for this format has been removed.
544
- a revision-history file.
545
- a branch-lock lock file [ to be shared with the bzrdir ]
629
def get_format_string(self):
630
"""See BzrBranchFormat.get_format_string()."""
631
return BZR_BRANCH_FORMAT_4
633
def initialize(self, url):
634
"""Format 4 branches cannot be created."""
635
raise UninitializableFormat(self)
637
def is_supported(self):
638
"""Format 4 is not supported.
640
It is not supported because the model changed from 4 to 5 and the
641
conversion logic is expensive - so doing it on the fly was not
548
def initialize(self, a_bzrdir):
549
"""Create a branch of this format in a_bzrdir."""
550
mutter('creating branch in %s', a_bzrdir.transport.base)
551
branch_transport = a_bzrdir.get_branch_transport(self)
552
utf8_files = [('revision-history', ''),
555
control_files = LockableFiles(branch_transport, 'branch-lock')
556
control_files.lock_write()
558
for file, content in utf8_files:
559
control_files.put_utf8(file, content)
561
control_files.unlock()
562
return self.open(a_bzrdir, _found=True)
565
super(BzrBranchFormat4, self).__init__()
566
self._matchingbzrdir = bzrdir.BzrDirFormat6()
568
def open(self, a_bzrdir, _found=False):
569
"""Return the branch object for a_bzrdir
571
_found is a private parameter, do not use it. It is used to indicate
572
if format probing has already be done.
647
class BzrBranchFormat5(BzrBranchFormat):
575
# we are being called directly and must probe.
576
raise NotImplementedError
577
return BzrBranch(_format=self,
578
_control_files=a_bzrdir._control_files,
580
_repository=a_bzrdir.open_repository())
583
class BzrBranchFormat5(BranchFormat):
648
584
"""Bzr branch format 5.
651
- weaves for file texts and inventory
653
- TextStores for revisions and signatures.
587
- a revision-history file.
590
- works with shared repositories.
656
593
def get_format_string(self):
657
"""See BzrBranchFormat.get_format_string()."""
658
return BZR_BRANCH_FORMAT_5
661
class BzrBranchFormat6(BzrBranchFormat):
662
"""Bzr branch format 6.
594
"""See BranchFormat.get_format_string()."""
595
return "Bazaar-NG branch format 5\n"
597
def initialize(self, a_bzrdir):
598
"""Create a branch of this format in a_bzrdir."""
599
mutter('creating branch in %s', a_bzrdir.transport.base)
600
branch_transport = a_bzrdir.get_branch_transport(self)
602
utf8_files = [('revision-history', ''),
606
branch_transport.put(lock_file, StringIO()) # TODO get the file mode from the bzrdir lock files., mode=file_mode)
607
control_files = LockableFiles(branch_transport, 'lock')
608
control_files.lock_write()
609
control_files.put_utf8('format', self.get_format_string())
611
for file, content in utf8_files:
612
control_files.put_utf8(file, content)
614
control_files.unlock()
615
return self.open(a_bzrdir, _found=True, )
618
super(BzrBranchFormat5, self).__init__()
619
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
621
def open(self, a_bzrdir, _found=False):
622
"""Return the branch object for a_bzrdir
624
_found is a private parameter, do not use it. It is used to indicate
625
if format probing has already be done.
628
format = BranchFormat.find_format(a_bzrdir)
629
assert format.__class__ == self.__class__
630
transport = a_bzrdir.get_branch_transport(None)
631
control_files = LockableFiles(transport, 'lock')
632
return BzrBranch(_format=self,
633
_control_files=control_files,
635
_repository=a_bzrdir.find_repository())
638
class BranchReferenceFormat(BranchFormat):
639
"""Bzr branch reference format.
641
Branch references are used in implementing checkouts, they
642
act as an alias to the real branch which is at some other url.
665
- weaves for file texts and inventory
666
- hash subdirectory based stores.
667
- TextStores for revisions and signatures.
670
649
def get_format_string(self):
671
"""See BzrBranchFormat.get_format_string()."""
672
return BZR_BRANCH_FORMAT_6
675
BzrBranchFormat.register_format(BzrBranchFormat4())
676
BzrBranchFormat.register_format(BzrBranchFormat5())
677
BzrBranchFormat.register_format(BzrBranchFormat6())
679
# TODO: jam 20060108 Create a new branch format, and as part of upgrade
680
# make sure that ancestry.weave is deleted (it is never used, but
681
# used to be created)
650
"""See BranchFormat.get_format_string()."""
651
return "Bazaar-NG Branch Reference Format 1\n"
653
def initialize(self, a_bzrdir, target_branch=None):
654
"""Create a branch of this format in a_bzrdir."""
655
if target_branch is None:
656
# this format does not implement branch itself, thus the implicit
657
# creation contract must see it as uninitializable
658
raise errors.UninitializableFormat(self)
659
mutter('creating branch reference in %s', a_bzrdir.transport.base)
660
branch_transport = a_bzrdir.get_branch_transport(self)
661
# FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
662
branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
663
branch_transport.put('format', StringIO(self.get_format_string()))
664
return self.open(a_bzrdir, _found=True)
667
super(BranchReferenceFormat, self).__init__()
668
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
670
def _make_reference_clone_function(format, a_branch):
671
"""Create a clone() routine for a branch dynamically."""
672
def clone(to_bzrdir, revision_id=None):
673
"""See Branch.clone()."""
674
return format.initialize(to_bzrdir, a_branch)
675
# cannot obey revision_id limits when cloning a reference ...
676
# FIXME RBC 20060210 either nuke revision_id for clone, or
677
# emit some sort of warning/error to the caller ?!
680
def open(self, a_bzrdir, _found=False):
681
"""Return the branch that the branch reference in a_bzrdir points at.
683
_found is a private parameter, do not use it. It is used to indicate
684
if format probing has already be done.
687
format = BranchFormat.find_format(a_bzrdir)
688
assert format.__class__ == self.__class__
689
transport = a_bzrdir.get_branch_transport(None)
690
real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
691
result = real_bzrdir.open_branch()
692
# this changes the behaviour of result.clone to create a new reference
693
# rather than a copy of the content of the branch.
694
# I did not use a proxy object because that needs much more extensive
695
# testing, and we are only changing one behaviour at the moment.
696
# If we decide to alter more behaviours - i.e. the implicit nickname
697
# then this should be refactored to introduce a tested proxy branch
698
# and a subclass of that for use in overriding clone() and ....
700
result.clone = self._make_reference_clone_function(result)
704
# formats which have no format string are not discoverable
705
# and not independently creatable, so are not registered.
706
__default_format = BzrBranchFormat5()
707
BranchFormat.register_format(__default_format)
708
BranchFormat.register_format(BranchReferenceFormat())
709
BranchFormat.set_default_format(__default_format)
710
_legacy_formats = [BzrBranchFormat4(),
684
713
class BzrBranch(Branch):
685
714
"""A branch stored in the actual filesystem.
700
728
# This should match a prefix with a function which accepts
701
729
REVISION_NAMESPACES = {}
703
def push_stores(self, branch_to):
704
"""See Branch.push_stores."""
705
if (not isinstance(self._branch_format, BzrBranchFormat4) or
706
self._branch_format != branch_to._branch_format):
707
from bzrlib.fetch import greedy_fetch
708
mutter("Using fetch logic to push between %s(%s) and %s(%s)",
709
self, self._branch_format, branch_to, branch_to._branch_format)
710
greedy_fetch(to_branch=branch_to, from_branch=self,
711
revision=self.last_revision())
714
# format 4 to format 4 logic only.
715
store_pairs = ((self.text_store, branch_to.text_store),
716
(self.inventory_store, branch_to.inventory_store),
717
(self.revision_store, branch_to.revision_store))
719
for from_store, to_store in store_pairs:
720
copy_all(from_store, to_store)
721
except UnlistableStore:
722
raise UnlistableBranch(from_store)
724
def __init__(self, transport, init=DEPRECATED_PARAMETER,
731
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
725
732
relax_version_check=DEPRECATED_PARAMETER, _format=None,
726
_control_files=None):
733
_control_files=None, a_bzrdir=None, _repository=None):
727
734
"""Create new branch object at a particular location.
729
736
transport -- A Transport object, defining how to access files.
966
975
def basis_tree(self):
967
976
"""See Branch.basis_tree."""
969
revision_id = self.revision_history()[-1]
970
# FIXME: This is an abstraction violation, the basis tree
971
# here as defined is on the working tree, the method should
972
# be too. The basis tree for a branch can be different than
973
# that for a working tree. RBC 20051207
974
xml = self.working_tree().read_basis_inventory(revision_id)
975
inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
976
return RevisionTree(self.repository, inv, revision_id)
977
except (IndexError, NoSuchFile, NoWorkingTree), e:
978
return self.repository.revision_tree(self.last_revision())
977
return self.repository.revision_tree(self.last_revision())
979
@deprecated_method(zero_eight)
980
980
def working_tree(self):
981
"""See Branch.working_tree."""
981
"""Create a Working tree object for this branch."""
982
982
from bzrlib.workingtree import WorkingTree
983
983
from bzrlib.transport.local import LocalTransport
984
984
if (self.base.find('://') != -1 or
985
985
not isinstance(self._transport, LocalTransport)):
986
986
raise NoWorkingTree(self.base)
987
return WorkingTree(self.base, branch=self)
987
return self.bzrdir.open_workingtree()
989
989
@needs_write_lock
990
def pull(self, source, overwrite=False):
990
def pull(self, source, overwrite=False, stop_revision=None):
991
991
"""See Branch.pull."""
992
992
source.lock_read()
994
994
old_count = len(self.revision_history())
996
self.update_revisions(source)
996
self.update_revisions(source,stop_revision)
997
997
except DivergedBranches:
998
998
if not overwrite:
1060
1060
history = self._get_truncated_history(revision)
1061
1061
if not bzrlib.osutils.lexists(to_location):
1062
1062
os.mkdir(to_location)
1063
branch_to = Branch.initialize(to_location)
1063
bzrdir_to = self.bzrdir._format.initialize(to_location)
1064
self.repository.clone(bzrdir_to)
1065
branch_to = bzrdir_to.create_branch()
1064
1066
mutter("copy branch from %s to %s", self, branch_to)
1066
self.repository.copy(branch_to.repository)
1068
# must be done *after* history is copied across
1069
1068
# FIXME duplicate code with base .clone().
1070
1069
# .. would template method be useful here? RBC 20051207
1071
1070
branch_to.set_parent(self.base)
1072
1071
branch_to.append_revision(*history)
1073
# FIXME: this should be in workingtree.clone
1074
WorkingTree.create(branch_to, to_location).set_root_id(self.get_root_id())
1072
WorkingTree.create(branch_to, branch_to.base)
1075
1073
mutter("copied")
1076
1074
return branch_to
1078
def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
1079
print "FIXME: clone via create and fetch is probably faster when versioned file comes in."
1080
if to_branch_type is None:
1081
to_branch_type = BzrBranch
1083
if to_branch_type == BzrBranch \
1084
and self.repository.weave_store.listable() \
1085
and self.repository.revision_store.listable():
1086
return self._clone_weave(to_location, revision, basis_branch)
1088
return Branch.clone(self, to_location, revision, basis_branch, to_branch_type)
1090
def fileid_involved_between_revs(self, from_revid, to_revid):
1091
"""Find file_id(s) which are involved in the changes between revisions.
1093
This determines the set of revisions which are involved, and then
1094
finds all file ids affected by those revisions.
1096
# TODO: jam 20060119 This code assumes that w.inclusions will
1097
# always be correct. But because of the presence of ghosts
1098
# it is possible to be wrong.
1099
# One specific example from Robert Collins:
1100
# Two branches, with revisions ABC, and AD
1101
# C is a ghost merge of D.
1102
# Inclusions doesn't recognize D as an ancestor.
1103
# If D is ever merged in the future, the weave
1104
# won't be fixed, because AD never saw revision C
1105
# to cause a conflict which would force a reweave.
1106
w = self.repository.get_inventory_weave()
1107
from_set = set(w.inclusions([w.lookup(from_revid)]))
1108
to_set = set(w.inclusions([w.lookup(to_revid)]))
1109
included = to_set.difference(from_set)
1110
changed = map(w.idx_to_name, included)
1111
return self._fileid_involved_by_set(changed)
1113
def fileid_involved(self, last_revid=None):
1114
"""Find all file_ids modified in the ancestry of last_revid.
1116
:param last_revid: If None, last_revision() will be used.
1118
w = self.repository.get_inventory_weave()
1120
changed = set(w._names)
1122
included = w.inclusions([w.lookup(last_revid)])
1123
changed = map(w.idx_to_name, included)
1124
return self._fileid_involved_by_set(changed)
1126
def fileid_involved_by_set(self, changes):
1127
"""Find all file_ids modified by the set of revisions passed in.
1129
:param changes: A set() of revision ids
1131
# TODO: jam 20060119 This line does *nothing*, remove it.
1132
# or better yet, change _fileid_involved_by_set so
1133
# that it takes the inventory weave, rather than
1134
# pulling it out by itself.
1135
w = self.repository.get_inventory_weave()
1136
return self._fileid_involved_by_set(changes)
1138
def _fileid_involved_by_set(self, changes):
1139
"""Find the set of file-ids affected by the set of revisions.
1141
:param changes: A set() of revision ids.
1142
:return: A set() of file ids.
1144
This peaks at the Weave, interpreting each line, looking to
1145
see if it mentions one of the revisions. And if so, includes
1146
the file id mentioned.
1147
This expects both the Weave format, and the serialization
1148
to have a single line per file/directory, and to have
1149
fileid="" and revision="" on that line.
1151
assert (isinstance(self._branch_format, BzrBranchFormat5) or
1152
isinstance(self._branch_format, BzrBranchFormat6)), \
1153
"fileid_involved only supported for branches which store inventory as xml"
1155
w = self.repository.get_inventory_weave()
1157
for line in w._weave:
1159
# it is ugly, but it is due to the weave structure
1160
if not isinstance(line, basestring): continue
1162
start = line.find('file_id="')+9
1163
if start < 9: continue
1164
end = line.find('"', start)
1166
file_id = xml.sax.saxutils.unescape(line[start:end])
1168
# check if file_id is already present
1169
if file_id in file_ids: continue
1171
start = line.find('revision="')+10
1172
if start < 10: continue
1173
end = line.find('"', start)
1175
revision_id = xml.sax.saxutils.unescape(line[start:end])
1177
if revision_id in changes:
1178
file_ids.add(file_id)
1183
Branch.set_default_initializer(BzrBranch._initialize)
1186
1077
class BranchTestProviderAdapter(object):
1187
1078
"""A tool to generate a suite testing multiple branch formats at once.
1200
1091
def adapt(self, test):
1201
1092
result = TestSuite()
1202
for format in self._formats:
1093
for branch_format, bzrdir_format in self._formats:
1203
1094
new_test = deepcopy(test)
1204
1095
new_test.transport_server = self._transport_server
1205
1096
new_test.transport_readonly_server = self._transport_readonly_server
1206
new_test.branch_format = format
1097
new_test.bzrdir_format = bzrdir_format
1098
new_test.branch_format = branch_format
1207
1099
def make_new_test_id():
1208
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1100
new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
1209
1101
return lambda: new_id
1210
1102
new_test.id = make_new_test_id()
1211
1103
result.addTest(new_test)
1215
class ScratchBranch(BzrBranch):
1216
"""Special test class: a branch that cleans up after itself.
1218
>>> b = ScratchBranch()
1222
>>> b._transport.__del__()
1227
def __init__(self, files=[], dirs=[], transport=None):
1228
"""Make a test branch.
1230
This creates a temporary directory and runs init-tree in it.
1232
If any files are listed, they are created in the working copy.
1234
if transport is None:
1235
transport = bzrlib.transport.local.ScratchTransport()
1236
# local import for scope restriction
1237
from bzrlib.workingtree import WorkingTree
1238
WorkingTree.create_standalone(transport.base)
1239
super(ScratchBranch, self).__init__(transport)
1241
super(ScratchBranch, self).__init__(transport)
1243
# BzrBranch creates a clone to .bzr and then forgets about the
1244
# original transport. A ScratchTransport() deletes itself and
1245
# everything underneath it when it goes away, so we need to
1246
# grab a local copy to prevent that from happening
1247
self._transport = transport
1250
self._transport.mkdir(d)
1253
self._transport.put(f, 'content of %s' % f)
1257
>>> orig = ScratchBranch(files=["file1", "file2"])
1258
>>> os.listdir(orig.base)
1259
[u'.bzr', u'file1', u'file2']
1260
>>> clone = orig.clone()
1261
>>> if os.name != 'nt':
1262
... os.path.samefile(orig.base, clone.base)
1264
... orig.base == clone.base
1267
>>> os.listdir(clone.base)
1268
[u'.bzr', u'file1', u'file2']
1270
from shutil import copytree
1271
from bzrlib.osutils import mkdtemp
1274
copytree(self.base, base, symlinks=True)
1275
return ScratchBranch(
1276
transport=bzrlib.transport.local.ScratchTransport(base))
1279
1107
######################################################################
1283
def is_control_file(filename):
1284
## FIXME: better check
1285
filename = normpath(filename)
1286
while filename != '':
1287
head, tail = os.path.split(filename)
1288
## mutter('check %r for control file' % ((head, tail),))
1289
if tail == bzrlib.BZRDIR:
1291
if filename == head:
1111
@deprecated_function(zero_eight)
1112
def ScratchBranch(*args, **kwargs):
1113
"""See bzrlib.bzrdir.ScratchDir."""
1114
d = ScratchDir(*args, **kwargs)
1115
return d.open_branch()
1118
@deprecated_function(zero_eight)
1119
def is_control_file(*args, **kwargs):
1120
"""See bzrlib.workingtree.is_control_file."""
1121
return bzrlib.workingtree.is_control_file(*args, **kwargs)