/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/bzr/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2020-03-22 01:35:14 UTC
  • mfrom: (7490.7.6 work)
  • mto: This revision was merged to the branch mainline in revision 7499.
  • Revision ID: jelmer@jelmer.uk-20200322013514-7vw1ntwho04rcuj3
merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
 
from __future__ import absolute_import
 
18
from io import BytesIO
 
19
import sys
19
20
 
20
21
from ..lazy_import import lazy_import
21
22
lazy_import(globals(), """
26
27
    lockdir,
27
28
    rio,
28
29
    shelf,
 
30
    )
 
31
from breezy.bzr import (
29
32
    tag as _mod_tag,
30
33
    )
31
34
""")
48
51
    only_raises,
49
52
    )
50
53
from ..lock import _RelockDebugMixin, LogicalLockResult
51
 
from ..sixish import (
52
 
    BytesIO,
53
 
    viewitems,
54
 
    )
55
54
from ..trace import (
56
55
    mutter,
57
56
    )
84
83
            raise ValueError('name must be supplied')
85
84
        self.controldir = a_controldir
86
85
        self._user_transport = self.controldir.transport.clone('..')
87
 
        if name != "":
 
86
        if name != u"":
88
87
            self._user_transport.set_segment_parameter(
89
88
                "branch", urlutils.escape(name))
90
89
        self._base = self._user_transport.base
97
96
        self.repository = _repository
98
97
        self.conf_store = None
99
98
        Branch.__init__(self, possible_transports)
 
99
        self._tags_bytes = None
100
100
 
101
101
    def __str__(self):
102
102
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
127
127
 
128
128
    def _get_config_store(self):
129
129
        if self.conf_store is None:
130
 
            self.conf_store =  _mod_config.BranchStore(self)
 
130
            self.conf_store = _mod_config.BranchStore(self)
131
131
        return self.conf_store
132
132
 
133
133
    def _uncommitted_branch(self):
190
190
            return BranchWriteLockResult(
191
191
                self.unlock,
192
192
                self.control_files.lock_write(token=token))
193
 
        except:
 
193
        except BaseException:
194
194
            if took_lock:
195
195
                self.repository.unlock()
196
196
            raise
210
210
        try:
211
211
            self.control_files.lock_read()
212
212
            return LogicalLockResult(self.unlock)
213
 
        except:
 
213
        except BaseException:
214
214
            if took_lock:
215
215
                self.repository.unlock()
216
216
            raise
239
239
    def set_last_revision_info(self, revno, revision_id):
240
240
        if not revision_id or not isinstance(revision_id, bytes):
241
241
            raise errors.InvalidRevisionId(
242
 
                    revision_id=revision_id, branch=self)
 
242
                revision_id=revision_id, branch=self)
243
243
        revision_id = _mod_revision.ensure_null(revision_id)
244
244
        with self.lock_write():
245
245
            old_revno, old_revid = self.last_revision_info()
259
259
        _locs = ['parent', 'pull', 'x-pull']
260
260
        for l in _locs:
261
261
            try:
262
 
                return self._transport.get_bytes(l).strip('\n')
 
262
                contents = self._transport.get_bytes(l)
263
263
            except errors.NoSuchFile:
264
264
                pass
 
265
            else:
 
266
                return contents.strip(b'\n').decode('utf-8')
265
267
        return None
266
268
 
267
269
    def get_stacked_on_url(self):
277
279
        if url is None:
278
280
            self._transport.delete('parent')
279
281
        else:
280
 
            self._transport.put_bytes('parent', url + '\n',
281
 
                mode=self.controldir._get_file_mode())
 
282
            if isinstance(url, str):
 
283
                url = url.encode('utf-8')
 
284
            self._transport.put_bytes('parent', url + b'\n',
 
285
                                      mode=self.controldir._get_file_mode())
282
286
 
283
287
    def unbind(self):
284
288
        """If bound, unbind"""
313
317
 
314
318
    def get_bound_location(self):
315
319
        try:
316
 
            return self._transport.get_bytes('bound')[:-1]
 
320
            return self._transport.get_bytes('bound')[:-1].decode('utf-8')
317
321
        except errors.NoSuchFile:
318
322
            return None
319
323
 
337
341
                               possible_transports=possible_transports)
338
342
        except (errors.NotBranchError, errors.ConnectionError) as e:
339
343
            raise errors.BoundBranchConnectionFailure(
340
 
                    self, bound_loc, e)
 
344
                self, bound_loc, e)
341
345
 
342
346
    def set_bound_location(self, location):
343
347
        """Set the target where this branch is bound to.
347
351
        with self.lock_write():
348
352
            self._master_branch_cache = None
349
353
            if location:
350
 
                self._transport.put_bytes('bound', location+'\n',
 
354
                self._transport.put_bytes(
 
355
                    'bound', location.encode('utf-8') + b'\n',
351
356
                    mode=self.controldir._get_file_mode())
352
357
            else:
353
358
                try:
367
372
            if master is not None:
368
373
                old_tip = _mod_revision.ensure_null(self.last_revision())
369
374
                self.pull(master, overwrite=True)
370
 
                if self.repository.get_graph().is_ancestor(old_tip,
371
 
                    _mod_revision.ensure_null(self.last_revision())):
 
375
                if self.repository.get_graph().is_ancestor(
 
376
                        old_tip, _mod_revision.ensure_null(
 
377
                            self.last_revision())):
372
378
                    return None
373
379
                return old_tip
374
380
            return None
390
396
        revision_id = _mod_revision.ensure_null(revision_id)
391
397
        out_string = b'%d %s\n' % (revno, revision_id)
392
398
        self._transport.put_bytes('last-revision', out_string,
393
 
            mode=self.controldir._get_file_mode())
 
399
                                  mode=self.controldir._get_file_mode())
394
400
 
395
401
    def update_feature_flags(self, updated_flags):
396
402
        """Update the feature flags for this branch.
400
406
        """
401
407
        with self.lock_write():
402
408
            self._format._update_feature_flags(updated_flags)
403
 
            self.control_transport.put_bytes('format', self._format.as_string())
 
409
            self.control_transport.put_bytes(
 
410
                'format', self._format.as_string())
 
411
 
 
412
    def _get_tags_bytes(self):
 
413
        """Get the bytes of a serialised tags dict.
 
414
 
 
415
        Note that not all branches support tags, nor do all use the same tags
 
416
        logic: this method is specific to BasicTags. Other tag implementations
 
417
        may use the same method name and behave differently, safely, because
 
418
        of the double-dispatch via
 
419
        format.make_tags->tags_instance->get_tags_dict.
 
420
 
 
421
        :return: The bytes of the tags file.
 
422
        :seealso: Branch._set_tags_bytes.
 
423
        """
 
424
        with self.lock_read():
 
425
            if self._tags_bytes is None:
 
426
                self._tags_bytes = self._transport.get_bytes('tags')
 
427
            return self._tags_bytes
 
428
 
 
429
    def _set_tags_bytes(self, bytes):
 
430
        """Mirror method for _get_tags_bytes.
 
431
 
 
432
        :seealso: Branch._get_tags_bytes.
 
433
        """
 
434
        with self.lock_write():
 
435
            self._tags_bytes = bytes
 
436
            return self._transport.put_bytes('tags', bytes)
 
437
 
 
438
    def _clear_cached_state(self):
 
439
        super(BzrBranch, self)._clear_cached_state()
 
440
        self._tags_bytes = None
 
441
 
 
442
    def reconcile(self, thorough=True):
 
443
        """Make sure the data stored in this branch is consistent."""
 
444
        from .reconcile import BranchReconciler
 
445
        with self.lock_write():
 
446
            reconciler = BranchReconciler(self, thorough=thorough)
 
447
            return reconciler.reconcile()
 
448
 
 
449
    def set_reference_info(self, file_id, branch_location, path=None):
 
450
        """Set the branch location to use for a tree reference."""
 
451
        raise errors.UnsupportedOperation(self.set_reference_info, self)
 
452
 
 
453
    def get_reference_info(self, file_id, path=None):
 
454
        """Get the tree_path and branch_location for a tree reference."""
 
455
        raise errors.UnsupportedOperation(self.get_reference_info, self)
 
456
 
 
457
    def reference_parent(self, file_id, path, possible_transports=None):
 
458
        """Return the parent branch for a tree-reference.
 
459
 
 
460
        :param path: The path of the nested tree in the tree
 
461
        :return: A branch associated with the nested tree
 
462
        """
 
463
        try:
 
464
            branch_location = self.get_reference_info(file_id)[0]
 
465
        except errors.UnsupportedOperation:
 
466
            branch_location = None
 
467
        if branch_location is None:
 
468
            try:
 
469
                return Branch.open_from_transport(
 
470
                    self.controldir.root_transport.clone(path),
 
471
                    possible_transports=possible_transports)
 
472
            except errors.NotBranchError:
 
473
                return None
 
474
        return Branch.open(
 
475
            urlutils.join(
 
476
                urlutils.strip_segment_parameters(self.user_url), branch_location),
 
477
            possible_transports=possible_transports)
404
478
 
405
479
 
406
480
class BzrBranch8(BzrBranch):
414
488
        try:
415
489
            url = self.get_stacked_on_url()
416
490
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
417
 
            UnstackableBranchFormat):
 
491
                UnstackableBranchFormat):
418
492
            pass
419
493
        else:
420
494
            for hook in Branch.hooks['transform_fallback_location']:
424
498
                    raise AssertionError(
425
499
                        "'transform_fallback_location' hook %s returned "
426
500
                        "None, not a URL." % hook_name)
427
 
            self._activate_fallback_location(url,
428
 
                possible_transports=possible_transports)
 
501
            self._activate_fallback_location(
 
502
                url, possible_transports=possible_transports)
429
503
 
430
504
    def __init__(self, *args, **kwargs):
431
505
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
453
527
        """Generate the revision history from last revision
454
528
        """
455
529
        last_revno, last_revision = self.last_revision_info()
456
 
        self._extend_partial_history(stop_index=last_revno-1)
 
530
        self._extend_partial_history(stop_index=last_revno - 1)
457
531
        return list(reversed(self._partial_revision_history_cache))
458
532
 
459
533
    def _set_parent_location(self, url):
460
534
        """Set the parent branch"""
461
535
        with self.lock_write():
462
 
            self._set_config_location('parent_location', url, make_relative=True)
 
536
            self._set_config_location(
 
537
                'parent_location', url, make_relative=True)
463
538
 
464
539
    def _get_parent_location(self):
465
540
        """Set the parent branch"""
469
544
    def _set_all_reference_info(self, info_dict):
470
545
        """Replace all reference info stored in a branch.
471
546
 
472
 
        :param info_dict: A dict of {file_id: (tree_path, branch_location)}
 
547
        :param info_dict: A dict of {file_id: (branch_location, tree_path)}
473
548
        """
474
549
        s = BytesIO()
475
550
        writer = rio.RioWriter(s)
476
 
        for key, (tree_path, branch_location) in viewitems(info_dict):
477
 
            stanza = rio.Stanza(file_id=key, tree_path=tree_path,
 
551
        for file_id, (branch_location, tree_path) in info_dict.items():
 
552
            stanza = rio.Stanza(file_id=file_id,
478
553
                                branch_location=branch_location)
 
554
            if tree_path is not None:
 
555
                stanza.add('tree_path', tree_path)
479
556
            writer.write_stanza(stanza)
480
557
        with self.lock_write():
481
558
            self._transport.put_bytes('references', s.getvalue())
484
561
    def _get_all_reference_info(self):
485
562
        """Return all the reference info stored in a branch.
486
563
 
487
 
        :return: A dict of {file_id: (tree_path, branch_location)}
 
564
        :return: A dict of {tree_path: (branch_location, file_id)}
488
565
        """
489
566
        with self.lock_read():
490
567
            if self._reference_info is not None:
491
568
                return self._reference_info
492
 
            rio_file = self._transport.get('references')
493
569
            try:
494
 
                stanzas = rio.read_stanzas(rio_file)
495
 
                info_dict = dict((s['file_id'], (s['tree_path'],
496
 
                                 s['branch_location'])) for s in stanzas)
497
 
            finally:
498
 
                rio_file.close()
 
570
                with self._transport.get('references') as rio_file:
 
571
                    stanzas = rio.read_stanzas(rio_file)
 
572
                    info_dict = {
 
573
                        s['file_id'].encode('utf-8'): (
 
574
                            s['branch_location'],
 
575
                            s['tree_path'] if 'tree_path' in s else None)
 
576
                        for s in stanzas}
 
577
            except errors.NoSuchFile:
 
578
                info_dict = {}
499
579
            self._reference_info = info_dict
500
580
            return info_dict
501
581
 
502
 
    def set_reference_info(self, file_id, tree_path, branch_location):
 
582
    def set_reference_info(self, file_id, branch_location, tree_path=None):
503
583
        """Set the branch location to use for a tree reference.
504
584
 
 
585
        :param branch_location: The location of the branch to retrieve tree
 
586
            references from.
505
587
        :param file_id: The file-id of the tree reference.
506
588
        :param tree_path: The path of the tree reference in the tree.
507
 
        :param branch_location: The location of the branch to retrieve tree
508
 
            references from.
509
589
        """
510
590
        info_dict = self._get_all_reference_info()
511
 
        info_dict[file_id] = (tree_path, branch_location)
512
 
        if None in (tree_path, branch_location):
513
 
            if tree_path is not None:
514
 
                raise ValueError('tree_path must be None when branch_location'
515
 
                                 ' is None.')
516
 
            if branch_location is not None:
517
 
                raise ValueError('branch_location must be None when tree_path'
518
 
                                 ' is None.')
 
591
        info_dict[file_id] = (branch_location, tree_path)
 
592
        if branch_location is None:
519
593
            del info_dict[file_id]
520
594
        self._set_all_reference_info(info_dict)
521
595
 
522
596
    def get_reference_info(self, file_id):
523
597
        """Get the tree_path and branch_location for a tree reference.
524
598
 
525
 
        :return: a tuple of (tree_path, branch_location)
 
599
        :return: a tuple of (branch_location, tree_path)
526
600
        """
527
601
        return self._get_all_reference_info().get(file_id, (None, None))
528
602
 
529
 
    def reference_parent(self, file_id, path, possible_transports=None):
530
 
        """Return the parent branch for a tree-reference file_id.
531
 
 
532
 
        :param file_id: The file_id of the tree reference
533
 
        :param path: The path of the file_id in the tree
534
 
        :return: A branch associated with the file_id
535
 
        """
536
 
        branch_location = self.get_reference_info(file_id)[1]
537
 
        if branch_location is None:
538
 
            return Branch.reference_parent(self, file_id, path,
539
 
                                           possible_transports)
540
 
        branch_location = urlutils.join(self.user_url, branch_location)
541
 
        return Branch.open(branch_location,
542
 
                           possible_transports=possible_transports)
543
 
 
544
603
    def set_push_location(self, location):
545
604
        """See Branch.set_push_location."""
546
605
        self._set_config_location('push_location', location)
548
607
    def set_bound_location(self, location):
549
608
        """See Branch.set_push_location."""
550
609
        self._master_branch_cache = None
551
 
        result = None
552
610
        conf = self.get_config_stack()
553
611
        if location is None:
554
612
            if not conf.get('bound'):
582
640
    def get_stacked_on_url(self):
583
641
        # you can always ask for the URL; but you might not be able to use it
584
642
        # if the repo can't support stacking.
585
 
        ## self._check_stackable_repo()
 
643
        # self._check_stackable_repo()
586
644
        # stacked_on_location is only ever defined in branch.conf, so don't
587
645
        # waste effort reading the whole stack of config files.
588
646
        conf = _mod_config.BranchOnlyStack(self)
590
648
                                                config=conf)
591
649
        if stacked_url is None:
592
650
            raise errors.NotStacked(self)
593
 
        return stacked_url.encode('utf-8')
 
651
        # TODO(jelmer): Clean this up for pad.lv/1696545
 
652
        return stacked_url
594
653
 
595
654
    def get_rev_id(self, revno, history=None):
596
655
        """Find the revision id of the specified revno."""
600
659
        with self.lock_read():
601
660
            last_revno, last_revision_id = self.last_revision_info()
602
661
            if revno <= 0 or revno > last_revno:
603
 
                raise errors.NoSuchRevision(self, revno)
 
662
                raise errors.RevnoOutOfBounds(revno, (0, last_revno))
604
663
 
605
664
            if history is not None:
606
665
                return history[revno - 1]
625
684
                    self._extend_partial_history(stop_revision=revision_id)
626
685
                except errors.RevisionNotPresent as e:
627
686
                    raise errors.GhostRevisionsHaveNoRevno(
628
 
                            revision_id, e.revision_id)
 
687
                        revision_id, e.revision_id)
629
688
                index = len(self._partial_revision_history_cache) - 1
630
689
                if index < 0:
631
690
                    raise errors.NoSuchRevision(self, revision_id)
637
696
class BzrBranch7(BzrBranch8):
638
697
    """A branch with support for a fallback repository."""
639
698
 
640
 
    def set_reference_info(self, file_id, tree_path, branch_location):
641
 
        Branch.set_reference_info(self, file_id, tree_path, branch_location)
642
 
 
643
 
    def get_reference_info(self, file_id):
644
 
        Branch.get_reference_info(self, file_id)
645
 
 
646
 
    def reference_parent(self, file_id, path, possible_transports=None):
647
 
        return Branch.reference_parent(self, file_id, path,
648
 
                                       possible_transports)
 
699
    def set_reference_info(self, file_id, branch_location, tree_path=None):
 
700
        super(BzrBranch7, self).set_reference_info(
 
701
            file_id, branch_location, tree_path)
 
702
        format_string = BzrBranchFormat8.get_format_string()
 
703
        mutter('Upgrading branch to format %r', format_string)
 
704
        self._transport.put_bytes('format', format_string)
649
705
 
650
706
 
651
707
class BzrBranch6(BzrBranch7):
676
732
            raise errors.NotBranchError(path=name, controldir=controldir)
677
733
        try:
678
734
            format_string = transport.get_bytes("format")
679
 
            # GZ 2017-06-09: Where should format strings get decoded...
680
 
            format_text = format_string.decode("ascii")
681
735
        except errors.NoSuchFile:
682
736
            raise errors.NotBranchError(
683
737
                path=transport.base, controldir=controldir)
684
 
        return klass._find_format(format_registry, 'branch', format_text)
 
738
        return klass._find_format(format_registry, 'branch', format_string)
685
739
 
686
740
    def _branch_class(self):
687
741
        """What class to instantiate on open calls."""
710
764
        mutter('creating branch %r in %s', self, a_controldir.user_url)
711
765
        branch_transport = a_controldir.get_branch_transport(self, name=name)
712
766
        control_files = lockable_files.LockableFiles(branch_transport,
713
 
            'lock', lockdir.LockDir)
 
767
                                                     'lock', lockdir.LockDir)
714
768
        control_files.create_lock()
715
769
        control_files.lock_write()
716
770
        try:
722
776
        finally:
723
777
            control_files.unlock()
724
778
        branch = self.open(a_controldir, name, _found=True,
725
 
                found_repository=repository)
 
779
                           found_repository=repository)
726
780
        self._run_post_branch_init_hooks(a_controldir, name, branch)
727
781
        return branch
728
782
 
729
 
    def open(self, a_controldir, name=None, _found=False, ignore_fallbacks=False,
730
 
            found_repository=None, possible_transports=None):
 
783
    def open(self, a_controldir, name=None, _found=False,
 
784
             ignore_fallbacks=False, found_repository=None,
 
785
             possible_transports=None):
731
786
        """See BranchFormat.open()."""
732
787
        if name is None:
733
788
            name = a_controldir._get_selected_branch()
735
790
            format = BranchFormatMetadir.find_format(a_controldir, name=name)
736
791
            if format.__class__ != self.__class__:
737
792
                raise AssertionError("wrong format %r found for %r" %
738
 
                    (format, self))
 
793
                                     (format, self))
739
794
        transport = a_controldir.get_branch_transport(None, name=name)
740
795
        try:
741
796
            control_files = lockable_files.LockableFiles(transport, 'lock',
742
797
                                                         lockdir.LockDir)
743
798
            if found_repository is None:
744
799
                found_repository = a_controldir.find_repository()
745
 
            return self._branch_class()(_format=self,
746
 
                              _control_files=control_files,
747
 
                              name=name,
748
 
                              a_controldir=a_controldir,
749
 
                              _repository=found_repository,
750
 
                              ignore_fallbacks=ignore_fallbacks,
751
 
                              possible_transports=possible_transports)
 
800
            return self._branch_class()(
 
801
                _format=self, _control_files=control_files, name=name,
 
802
                a_controldir=a_controldir, _repository=found_repository,
 
803
                ignore_fallbacks=ignore_fallbacks,
 
804
                possible_transports=possible_transports)
752
805
        except errors.NoSuchFile:
753
 
            raise errors.NotBranchError(path=transport.base, controldir=a_controldir)
 
806
            raise errors.NotBranchError(
 
807
                path=transport.base, controldir=a_controldir)
754
808
 
755
809
    @property
756
810
    def _matchingcontroldir(self):
765
819
        return True
766
820
 
767
821
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
768
 
            basedir=None):
769
 
        BranchFormat.check_support_status(self,
770
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
771
 
            basedir=basedir)
772
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
822
                             basedir=None):
 
823
        BranchFormat.check_support_status(
 
824
            self, allow_unsupported=allow_unsupported,
 
825
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
826
        bzrdir.BzrFormat.check_support_status(
 
827
            self, allow_unsupported=allow_unsupported,
773
828
            recommend_upgrade=recommend_upgrade, basedir=basedir)
774
829
 
775
830
 
790
845
    @classmethod
791
846
    def get_format_string(cls):
792
847
        """See BranchFormat.get_format_string()."""
793
 
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
 
848
        return b"Bazaar Branch Format 6 (bzr 0.15)\n"
794
849
 
795
850
    def get_format_description(self):
796
851
        """See BranchFormat.get_format_description()."""
799
854
    def initialize(self, a_controldir, name=None, repository=None,
800
855
                   append_revisions_only=None):
801
856
        """Create a branch of this format in a_controldir."""
802
 
        utf8_files = [('last-revision', '0 null:\n'),
803
 
                      ('branch.conf',
804
 
                          self._get_initial_config(append_revisions_only)),
805
 
                      ('tags', ''),
806
 
                      ]
807
 
        return self._initialize_helper(a_controldir, utf8_files, name, repository)
 
857
        utf8_files = [
 
858
            ('last-revision', b'0 null:\n'),
 
859
            ('branch.conf', self._get_initial_config(append_revisions_only)),
 
860
            ('tags', b''),
 
861
            ]
 
862
        return self._initialize_helper(
 
863
            a_controldir, utf8_files, name, repository)
808
864
 
809
865
    def make_tags(self, branch):
810
866
        """See breezy.branch.BranchFormat.make_tags()."""
813
869
    def supports_set_append_revisions_only(self):
814
870
        return True
815
871
 
 
872
    supports_reference_locations = True
 
873
 
816
874
 
817
875
class BzrBranchFormat8(BranchFormatMetadir):
818
876
    """Metadir format supporting storing locations of subtree branches."""
823
881
    @classmethod
824
882
    def get_format_string(cls):
825
883
        """See BranchFormat.get_format_string()."""
826
 
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
 
884
        return b"Bazaar Branch Format 8 (needs bzr 1.15)\n"
827
885
 
828
886
    def get_format_description(self):
829
887
        """See BranchFormat.get_format_description()."""
832
890
    def initialize(self, a_controldir, name=None, repository=None,
833
891
                   append_revisions_only=None):
834
892
        """Create a branch of this format in a_controldir."""
835
 
        utf8_files = [('last-revision', '0 null:\n'),
 
893
        utf8_files = [('last-revision', b'0 null:\n'),
836
894
                      ('branch.conf',
837
895
                          self._get_initial_config(append_revisions_only)),
838
 
                      ('tags', ''),
839
 
                      ('references', '')
 
896
                      ('tags', b''),
 
897
                      ('references', b'')
840
898
                      ]
841
 
        return self._initialize_helper(a_controldir, utf8_files, name, repository)
 
899
        return self._initialize_helper(
 
900
            a_controldir, utf8_files, name, repository)
842
901
 
843
902
    def make_tags(self, branch):
844
903
        """See breezy.branch.BranchFormat.make_tags()."""
870
929
                          self._get_initial_config(append_revisions_only)),
871
930
                      ('tags', b''),
872
931
                      ]
873
 
        return self._initialize_helper(a_controldir, utf8_files, name, repository)
 
932
        return self._initialize_helper(
 
933
            a_controldir, utf8_files, name, repository)
874
934
 
875
935
    def _branch_class(self):
876
936
        return BzrBranch7
878
938
    @classmethod
879
939
    def get_format_string(cls):
880
940
        """See BranchFormat.get_format_string()."""
881
 
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
 
941
        return b"Bazaar Branch Format 7 (needs bzr 1.6)\n"
882
942
 
883
943
    def get_format_description(self):
884
944
        """See BranchFormat.get_format_description()."""
894
954
        """See breezy.branch.BranchFormat.make_tags()."""
895
955
        return _mod_tag.BasicTags(branch)
896
956
 
897
 
    supports_reference_locations = False
 
957
    # This is a white lie; as soon as you set a reference location, we upgrade
 
958
    # you to BzrBranchFormat8.
 
959
    supports_reference_locations = True
898
960
 
899
961
 
900
962
class BranchReferenceFormat(BranchFormatMetadir):
911
973
    @classmethod
912
974
    def get_format_string(cls):
913
975
        """See BranchFormat.get_format_string()."""
914
 
        return "Bazaar-NG Branch Reference Format 1\n"
 
976
        return b"Bazaar-NG Branch Reference Format 1\n"
915
977
 
916
978
    def get_format_description(self):
917
979
        """See BranchFormat.get_format_description()."""
920
982
    def get_reference(self, a_controldir, name=None):
921
983
        """See BranchFormat.get_reference()."""
922
984
        transport = a_controldir.get_branch_transport(None, name=name)
923
 
        return transport.get_bytes('location')
 
985
        url = urlutils.strip_segment_parameters(a_controldir.user_url)
 
986
        return urlutils.join(
 
987
            url, transport.get_bytes('location').decode('utf-8'))
 
988
 
 
989
    def _write_reference(self, a_controldir, transport, to_branch):
 
990
        to_url = to_branch.user_url
 
991
        # Ideally, we'd write a relative path here for the benefit of colocated
 
992
        # branches - so that moving a control directory doesn't break
 
993
        # any references to colocated branches. Unfortunately, bzr
 
994
        # does not support relative URLs. See pad.lv/1803845 -- jelmer
 
995
        # to_url = urlutils.relative_url(
 
996
        #    a_controldir.user_url, to_branch.user_url)
 
997
        transport.put_bytes('location', to_url.encode('utf-8'))
924
998
 
925
999
    def set_reference(self, a_controldir, name, to_branch):
926
1000
        """See BranchFormat.set_reference()."""
927
1001
        transport = a_controldir.get_branch_transport(None, name=name)
928
 
        location = transport.put_bytes('location', to_branch.base)
 
1002
        self._write_reference(a_controldir, transport, to_branch)
929
1003
 
930
1004
    def initialize(self, a_controldir, name=None, target_branch=None,
931
 
            repository=None, append_revisions_only=None):
 
1005
                   repository=None, append_revisions_only=None):
932
1006
        """Create a branch of this format in a_controldir."""
933
1007
        if target_branch is None:
934
1008
            # this format does not implement branch itself, thus the implicit
940
1014
        if name is None:
941
1015
            name = a_controldir._get_selected_branch()
942
1016
        branch_transport = a_controldir.get_branch_transport(self, name=name)
943
 
        branch_transport.put_bytes('location',
944
 
            target_branch.user_url)
 
1017
        self._write_reference(a_controldir, branch_transport, target_branch)
945
1018
        branch_transport.put_bytes('format', self.as_string())
946
1019
        branch = self.open(a_controldir, name, _found=True,
947
 
            possible_transports=[target_branch.controldir.root_transport])
 
1020
                           possible_transports=[target_branch.controldir.root_transport])
948
1021
        self._run_post_branch_init_hooks(a_controldir, name, branch)
949
1022
        return branch
950
1023
 
951
1024
    def _make_reference_clone_function(format, a_branch):
952
1025
        """Create a clone() routine for a branch dynamically."""
953
1026
        def clone(to_bzrdir, revision_id=None,
954
 
            repository_policy=None):
 
1027
                  repository_policy=None, tag_selector=None):
955
1028
            """See Branch.clone()."""
956
1029
            return format.initialize(to_bzrdir, target_branch=a_branch)
957
1030
            # cannot obey revision_id limits when cloning a reference ...
981
1054
            format = BranchFormatMetadir.find_format(a_controldir, name=name)
982
1055
            if format.__class__ != self.__class__:
983
1056
                raise AssertionError("wrong format %r found for %r" %
984
 
                    (format, self))
 
1057
                                     (format, self))
985
1058
        if location is None:
986
1059
            location = self.get_reference(a_controldir, name)
987
1060
        real_bzrdir = controldir.ControlDir.open(
988
1061
            location, possible_transports=possible_transports)
989
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
1062
        result = real_bzrdir.open_branch(
 
1063
            ignore_fallbacks=ignore_fallbacks,
990
1064
            possible_transports=possible_transports)
991
1065
        # this changes the behaviour of result.clone to create a new reference
992
1066
        # rather than a copy of the content of the branch.
1010
1084
 
1011
1085
        # Copy source data into target
1012
1086
        new_branch._write_last_revision_info(*branch.last_revision_info())
1013
 
        new_branch.lock_write()
1014
 
        try:
 
1087
        with new_branch.lock_write():
1015
1088
            new_branch.set_parent(branch.get_parent())
1016
1089
            new_branch.set_bound_location(branch.get_bound_location())
1017
1090
            new_branch.set_push_location(branch.get_push_location())
1018
 
        finally:
1019
 
            new_branch.unlock()
1020
1091
 
1021
1092
        # New branch has no tags by default
1022
1093
        new_branch.tags._set_tag_dict({})
1023
1094
 
1024
1095
        # Copying done; now update target format
1025
 
        new_branch._transport.put_bytes('format',
1026
 
            format.as_string(),
 
1096
        new_branch._transport.put_bytes(
 
1097
            'format', format.as_string(),
1027
1098
            mode=new_branch.controldir._get_file_mode())
1028
1099
 
1029
1100
        # Clean up old files
1030
1101
        new_branch._transport.delete('revision-history')
1031
 
        branch.lock_write()
1032
 
        try:
 
1102
        with branch.lock_write():
1033
1103
            try:
1034
1104
                branch.set_parent(None)
1035
1105
            except errors.NoSuchFile:
1036
1106
                pass
1037
1107
            branch.set_bound_location(None)
1038
 
        finally:
1039
 
            branch.unlock()
1040
1108
 
1041
1109
 
1042
1110
class Converter6to7(object):
1054
1122
 
1055
1123
    def convert(self, branch):
1056
1124
        format = BzrBranchFormat8()
1057
 
        branch._transport.put_bytes('references', '')
 
1125
        branch._transport.put_bytes('references', b'')
1058
1126
        # update target format
1059
1127
        branch._transport.put_bytes('format', format.as_string())
1060
 
 
1061
 
 
1062