~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2007-05-07 16:48:14 UTC
  • mto: This revision was merged to the branch mainline in revision 2485.
  • Revision ID: robertc@robertcollins.net-20070507164814-wpagonutf4b5cf8s
Move HACKING to docs/developers/HACKING and adjust Makefile to accomodate this.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
27
27
from bzrlib import (
28
28
        bzrdir,
29
29
        cache_utf8,
 
30
        config as _mod_config,
30
31
        errors,
31
32
        lockdir,
32
33
        lockable_files,
34
35
        revision as _mod_revision,
35
36
        transport,
36
37
        tree,
 
38
        tsort,
37
39
        ui,
38
40
        urlutils,
39
41
        )
40
42
from bzrlib.config import BranchConfig, TreeConfig
41
43
from bzrlib.lockable_files import LockableFiles, TransportLock
 
44
from bzrlib.tag import (
 
45
    BasicTags,
 
46
    DisabledTags,
 
47
    )
42
48
""")
43
49
 
44
50
from bzrlib.decorators import needs_read_lock, needs_write_lock
49
55
                           NotBranchError, UninitializableFormat,
50
56
                           UnlistableStore, UnlistableBranch,
51
57
                           )
 
58
from bzrlib.hooks import Hooks
52
59
from bzrlib.symbol_versioning import (deprecated_function,
53
60
                                      deprecated_method,
54
61
                                      DEPRECATED_PARAMETER,
55
62
                                      deprecated_passed,
56
 
                                      zero_eight, zero_nine,
 
63
                                      zero_eight, zero_nine, zero_sixteen,
57
64
                                      )
58
65
from bzrlib.trace import mutter, note
59
66
 
60
67
 
61
68
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
62
69
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
63
 
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
 
70
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
71
 
65
72
 
66
73
# TODO: Maybe include checks for common corruption of newlines, etc?
80
87
 
81
88
    base
82
89
        Base directory/url of the branch.
 
90
 
 
91
    hooks: An instance of BranchHooks.
83
92
    """
84
93
    # this is really an instance variable - FIXME move it there
85
94
    # - RBC 20060112
86
95
    base = None
87
96
 
 
97
    # override this to set the strategy for storing tags
 
98
    def _make_tags(self):
 
99
        return DisabledTags(self)
 
100
 
88
101
    def __init__(self, *ignored, **ignored_too):
89
 
        raise NotImplementedError('The Branch class is abstract')
 
102
        self.tags = self._make_tags()
 
103
        self._revision_history_cache = None
 
104
        self._revision_id_to_revno_cache = None
90
105
 
91
106
    def break_lock(self):
92
107
        """Break a lock if one is present from another instance.
182
197
    def get_physical_lock_status(self):
183
198
        raise NotImplementedError(self.get_physical_lock_status)
184
199
 
 
200
    @needs_read_lock
 
201
    def get_revision_id_to_revno_map(self):
 
202
        """Return the revision_id => dotted revno map.
 
203
 
 
204
        This will be regenerated on demand, but will be cached.
 
205
 
 
206
        :return: A dictionary mapping revision_id => dotted revno.
 
207
            This dictionary should not be modified by the caller.
 
208
        """
 
209
        if self._revision_id_to_revno_cache is not None:
 
210
            mapping = self._revision_id_to_revno_cache
 
211
        else:
 
212
            mapping = self._gen_revno_map()
 
213
            self._cache_revision_id_to_revno(mapping)
 
214
        # TODO: jam 20070417 Since this is being cached, should we be returning
 
215
        #       a copy?
 
216
        # I would rather not, and instead just declare that users should not
 
217
        # modify the return value.
 
218
        return mapping
 
219
 
 
220
    def _gen_revno_map(self):
 
221
        """Create a new mapping from revision ids to dotted revnos.
 
222
 
 
223
        Dotted revnos are generated based on the current tip in the revision
 
224
        history.
 
225
        This is the worker function for get_revision_id_to_revno_map, which
 
226
        just caches the return value.
 
227
 
 
228
        :return: A dictionary mapping revision_id => dotted revno.
 
229
        """
 
230
        last_revision = self.last_revision()
 
231
        revision_graph = self.repository.get_revision_graph(last_revision)
 
232
        merge_sorted_revisions = tsort.merge_sort(
 
233
            revision_graph,
 
234
            last_revision,
 
235
            None,
 
236
            generate_revno=True)
 
237
        revision_id_to_revno = dict((rev_id, revno)
 
238
                                    for seq_num, rev_id, depth, revno, end_of_merge
 
239
                                     in merge_sorted_revisions)
 
240
        return revision_id_to_revno
 
241
 
 
242
    def leave_lock_in_place(self):
 
243
        """Tell this branch object not to release the physical lock when this
 
244
        object is unlocked.
 
245
        
 
246
        If lock_write doesn't return a token, then this method is not supported.
 
247
        """
 
248
        self.control_files.leave_in_place()
 
249
 
 
250
    def dont_leave_lock_in_place(self):
 
251
        """Tell this branch object to release the physical lock when this
 
252
        object is unlocked, even if it didn't originally acquire it.
 
253
 
 
254
        If lock_write doesn't return a token, then this method is not supported.
 
255
        """
 
256
        self.control_files.dont_leave_in_place()
 
257
 
185
258
    def abspath(self, name):
186
259
        """Return absolute filename for something in the branch
187
260
        
222
295
        try:
223
296
            if last_revision is None:
224
297
                pb.update('get source history')
225
 
                from_history = from_branch.revision_history()
226
 
                if from_history:
227
 
                    last_revision = from_history[-1]
228
 
                else:
229
 
                    # no history in the source branch
 
298
                last_revision = from_branch.last_revision()
 
299
                if last_revision is None:
230
300
                    last_revision = _mod_revision.NULL_REVISION
231
301
            return self.repository.fetch(from_branch.repository,
232
302
                                         revision_id=last_revision,
244
314
        """
245
315
        return None
246
316
    
 
317
    def get_old_bound_location(self):
 
318
        """Return the URL of the branch we used to be bound to
 
319
        """
 
320
        raise errors.UpgradeRequired(self.base)
 
321
 
247
322
    def get_commit_builder(self, parents, config=None, timestamp=None, 
248
323
                           timezone=None, committer=None, revprops=None, 
249
324
                           revision_id=None):
261
336
        if config is None:
262
337
            config = self.get_config()
263
338
        
264
 
        return self.repository.get_commit_builder(self, parents, config, 
 
339
        return self.repository.get_commit_builder(self, parents, config,
265
340
            timestamp, timezone, committer, revprops, revision_id)
266
341
 
267
342
    def get_master_branch(self):
283
358
            raise InvalidRevisionNumber(revno)
284
359
        return self.repository.get_revision_delta(rh[revno-1])
285
360
 
 
361
    @deprecated_method(zero_sixteen)
286
362
    def get_root_id(self):
287
 
        """Return the id of this branches root"""
 
363
        """Return the id of this branches root
 
364
 
 
365
        Deprecated: branches don't have root ids-- trees do.
 
366
        Use basis_tree().get_root_id() instead.
 
367
        """
288
368
        raise NotImplementedError(self.get_root_id)
289
369
 
290
370
    def print_file(self, file, revision_id):
297
377
    def set_revision_history(self, rev_history):
298
378
        raise NotImplementedError(self.set_revision_history)
299
379
 
 
380
    def _cache_revision_history(self, rev_history):
 
381
        """Set the cached revision history to rev_history.
 
382
 
 
383
        The revision_history method will use this cache to avoid regenerating
 
384
        the revision history.
 
385
 
 
386
        This API is semi-public; it only for use by subclasses, all other code
 
387
        should consider it to be private.
 
388
        """
 
389
        self._revision_history_cache = rev_history
 
390
 
 
391
    def _cache_revision_id_to_revno(self, revision_id_to_revno):
 
392
        """Set the cached revision_id => revno map to revision_id_to_revno.
 
393
 
 
394
        This API is semi-public; it only for use by subclasses, all other code
 
395
        should consider it to be private.
 
396
        """
 
397
        self._revision_id_to_revno_cache = revision_id_to_revno
 
398
 
 
399
    def _clear_cached_state(self):
 
400
        """Clear any cached data on this branch, e.g. cached revision history.
 
401
 
 
402
        This means the next call to revision_history will need to call
 
403
        _gen_revision_history.
 
404
 
 
405
        This API is semi-public; it only for use by subclasses, all other code
 
406
        should consider it to be private.
 
407
        """
 
408
        self._revision_history_cache = None
 
409
        self._revision_id_to_revno_cache = None
 
410
 
 
411
    def _gen_revision_history(self):
 
412
        """Return sequence of revision hashes on to this branch.
 
413
        
 
414
        Unlike revision_history, this method always regenerates or rereads the
 
415
        revision history, i.e. it does not cache the result, so repeated calls
 
416
        may be expensive.
 
417
 
 
418
        Concrete subclasses should override this instead of revision_history so
 
419
        that subclasses do not need to deal with caching logic.
 
420
        
 
421
        This API is semi-public; it only for use by subclasses, all other code
 
422
        should consider it to be private.
 
423
        """
 
424
        raise NotImplementedError(self._gen_revision_history)
 
425
 
 
426
    @needs_read_lock
300
427
    def revision_history(self):
301
 
        """Return sequence of revision hashes on to this branch."""
302
 
        raise NotImplementedError(self.revision_history)
 
428
        """Return sequence of revision hashes on to this branch.
 
429
        
 
430
        This method will cache the revision history for as long as it is safe to
 
431
        do so.
 
432
        """
 
433
        if self._revision_history_cache is not None:
 
434
            history = self._revision_history_cache
 
435
        else:
 
436
            history = self._gen_revision_history()
 
437
            self._cache_revision_history(history)
 
438
        return list(history)
303
439
 
304
440
    def revno(self):
305
441
        """Return current revision number for this branch.
313
449
        """Older format branches cannot bind or unbind."""
314
450
        raise errors.UpgradeRequired(self.base)
315
451
 
 
452
    def set_append_revisions_only(self, enabled):
 
453
        """Older format branches are never restricted to append-only"""
 
454
        raise errors.UpgradeRequired(self.base)
 
455
 
316
456
    def last_revision(self):
317
457
        """Return last revision id, or None"""
318
458
        ph = self.revision_history()
321
461
        else:
322
462
            return None
323
463
 
 
464
    def last_revision_info(self):
 
465
        """Return information about the last revision.
 
466
 
 
467
        :return: A tuple (revno, last_revision_id).
 
468
        """
 
469
        rh = self.revision_history()
 
470
        revno = len(rh)
 
471
        if revno:
 
472
            return (revno, rh[-1])
 
473
        else:
 
474
            return (0, _mod_revision.NULL_REVISION)
 
475
 
324
476
    def missing_revisions(self, other, stop_revision=None):
325
477
        """Return a list of new revisions that would perfectly fit.
326
478
        
357
509
        """Given a revision id, return its revno"""
358
510
        if revision_id is None:
359
511
            return 0
 
512
        revision_id = osutils.safe_revision_id(revision_id)
360
513
        history = self.revision_history()
361
514
        try:
362
515
            return history.index(revision_id) + 1
363
516
        except ValueError:
364
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
 
517
            raise errors.NoSuchRevision(self, revision_id)
365
518
 
366
519
    def get_rev_id(self, revno, history=None):
367
520
        """Find the revision id of the specified revno."""
370
523
        if history is None:
371
524
            history = self.revision_history()
372
525
        if revno <= 0 or revno > len(history):
373
 
            raise bzrlib.errors.NoSuchRevision(self, revno)
 
526
            raise errors.NoSuchRevision(self, revno)
374
527
        return history[revno - 1]
375
528
 
376
529
    def pull(self, source, overwrite=False, stop_revision=None):
 
530
        """Mirror source into this branch.
 
531
 
 
532
        This branch is considered to be 'local', having low latency.
 
533
 
 
534
        :returns: PullResult instance
 
535
        """
377
536
        raise NotImplementedError(self.pull)
378
537
 
 
538
    def push(self, target, overwrite=False, stop_revision=None):
 
539
        """Mirror this branch into target.
 
540
 
 
541
        This branch is considered to be 'local', having low latency.
 
542
        """
 
543
        raise NotImplementedError(self.push)
 
544
 
379
545
    def basis_tree(self):
380
546
        """Return `Tree` object for last revision."""
381
547
        return self.repository.revision_tree(self.last_revision())
412
578
        """
413
579
        raise NotImplementedError(self.get_parent)
414
580
 
 
581
    def _set_config_location(self, name, url, config=None,
 
582
                             make_relative=False):
 
583
        if config is None:
 
584
            config = self.get_config()
 
585
        if url is None:
 
586
            url = ''
 
587
        elif make_relative:
 
588
            url = urlutils.relative_url(self.base, url)
 
589
        config.set_user_option(name, url)
 
590
 
 
591
    def _get_config_location(self, name, config=None):
 
592
        if config is None:
 
593
            config = self.get_config()
 
594
        location = config.get_user_option(name)
 
595
        if location == '':
 
596
            location = None
 
597
        return location
 
598
 
415
599
    def get_submit_branch(self):
416
600
        """Return the submit location of the branch.
417
601
 
430
614
        """
431
615
        self.get_config().set_user_option('submit_branch', location)
432
616
 
 
617
    def get_public_branch(self):
 
618
        """Return the public location of the branch.
 
619
 
 
620
        This is is used by merge directives.
 
621
        """
 
622
        return self._get_config_location('public_branch')
 
623
 
 
624
    def set_public_branch(self, location):
 
625
        """Return the submit location of the branch.
 
626
 
 
627
        This is the default location for bundle.  The usual
 
628
        pattern is that the user can override it by specifying a
 
629
        location.
 
630
        """
 
631
        self._set_config_location('public_branch', location)
 
632
 
433
633
    def get_push_location(self):
434
634
        """Return the None or the location to push this branch to."""
435
 
        raise NotImplementedError(self.get_push_location)
 
635
        push_loc = self.get_config().get_user_option('push_location')
 
636
        return push_loc
436
637
 
437
638
    def set_push_location(self, location):
438
639
        """Set a new push location for this branch."""
466
667
            raise InvalidRevisionNumber(revno)
467
668
 
468
669
    @needs_read_lock
469
 
    def clone(self, *args, **kwargs):
 
670
    def clone(self, to_bzrdir, revision_id=None):
470
671
        """Clone this branch into to_bzrdir preserving all semantic values.
471
672
        
472
673
        revision_id: if not None, the revision history in the new branch will
473
674
                     be truncated to end with revision_id.
474
675
        """
475
 
        # for API compatibility, until 0.8 releases we provide the old api:
476
 
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
477
 
        # after 0.8 releases, the *args and **kwargs should be changed:
478
 
        # def clone(self, to_bzrdir, revision_id=None):
479
 
        if (kwargs.get('to_location', None) or
480
 
            kwargs.get('revision', None) or
481
 
            kwargs.get('basis_branch', None) or
482
 
            (len(args) and isinstance(args[0], basestring))):
483
 
            # backwards compatibility api:
484
 
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
485
 
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
486
 
            # get basis_branch
487
 
            if len(args) > 2:
488
 
                basis_branch = args[2]
489
 
            else:
490
 
                basis_branch = kwargs.get('basis_branch', None)
491
 
            if basis_branch:
492
 
                basis = basis_branch.bzrdir
493
 
            else:
494
 
                basis = None
495
 
            # get revision
496
 
            if len(args) > 1:
497
 
                revision_id = args[1]
498
 
            else:
499
 
                revision_id = kwargs.get('revision', None)
500
 
            # get location
501
 
            if len(args):
502
 
                url = args[0]
503
 
            else:
504
 
                # no default to raise if not provided.
505
 
                url = kwargs.get('to_location')
506
 
            return self.bzrdir.clone(url,
507
 
                                     revision_id=revision_id,
508
 
                                     basis=basis).open_branch()
509
 
        # new cleaner api.
510
 
        # generate args by hand 
511
 
        if len(args) > 1:
512
 
            revision_id = args[1]
513
 
        else:
514
 
            revision_id = kwargs.get('revision_id', None)
515
 
        if len(args):
516
 
            to_bzrdir = args[0]
517
 
        else:
518
 
            # no default to raise if not provided.
519
 
            to_bzrdir = kwargs.get('to_bzrdir')
520
676
        result = self._format.initialize(to_bzrdir)
521
677
        self.copy_content_into(result, revision_id=revision_id)
522
678
        return  result
533
689
        result.set_parent(self.bzrdir.root_transport.base)
534
690
        return result
535
691
 
536
 
    @needs_read_lock
537
 
    def copy_content_into(self, destination, revision_id=None):
538
 
        """Copy the content of self into destination.
539
 
 
540
 
        revision_id: if not None, the revision history in the new branch will
541
 
                     be truncated to end with revision_id.
 
692
    def _synchronize_history(self, destination, revision_id):
 
693
        """Synchronize last revision and revision history between branches.
 
694
 
 
695
        This version is most efficient when the destination is also a
 
696
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
697
        history is the true lefthand parent history, and all of the revisions
 
698
        are in the destination's repository.  If not, set_revision_history
 
699
        will fail.
 
700
 
 
701
        :param destination: The branch to copy the history into
 
702
        :param revision_id: The revision-id to truncate history at.  May
 
703
          be None to copy complete history.
542
704
        """
543
705
        new_history = self.revision_history()
544
706
        if revision_id is not None:
 
707
            revision_id = osutils.safe_revision_id(revision_id)
545
708
            try:
546
709
                new_history = new_history[:new_history.index(revision_id) + 1]
547
710
            except ValueError:
548
711
                rev = self.repository.get_revision(revision_id)
549
712
                new_history = rev.get_history(self.repository)[1:]
550
713
        destination.set_revision_history(new_history)
 
714
 
 
715
    @needs_read_lock
 
716
    def copy_content_into(self, destination, revision_id=None):
 
717
        """Copy the content of self into destination.
 
718
 
 
719
        revision_id: if not None, the revision history in the new branch will
 
720
                     be truncated to end with revision_id.
 
721
        """
 
722
        self._synchronize_history(destination, revision_id)
551
723
        try:
552
724
            parent = self.get_parent()
553
725
        except errors.InaccessibleParent, e:
555
727
        else:
556
728
            if parent:
557
729
                destination.set_parent(parent)
 
730
        self.tags.merge_to(destination.tags)
558
731
 
559
732
    @needs_read_lock
560
733
    def check(self):
588
761
 
589
762
    def _get_checkout_format(self):
590
763
        """Return the most suitable metadir for a checkout of this branch.
591
 
        Weaves are used if this branch's repostory uses weaves.
 
764
        Weaves are used if this branch's repository uses weaves.
592
765
        """
593
766
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
594
 
            from bzrlib import repository
 
767
            from bzrlib.repofmt import weaverepo
595
768
            format = bzrdir.BzrDirMetaFormat1()
596
 
            format.repository_format = repository.RepositoryFormat7()
 
769
            format.repository_format = weaverepo.RepositoryFormat7()
597
770
        else:
598
 
            format = self.repository.bzrdir.cloning_metadir()
 
771
            format = self.repository.bzrdir.checkout_metadir()
 
772
            format.set_branch_format(self._format)
599
773
        return format
600
774
 
601
 
    def create_checkout(self, to_location, revision_id=None, 
 
775
    def create_checkout(self, to_location, revision_id=None,
602
776
                        lightweight=False):
603
777
        """Create a checkout of a branch.
604
778
        
609
783
        :return: The tree of the created checkout
610
784
        """
611
785
        t = transport.get_transport(to_location)
612
 
        try:
613
 
            t.mkdir('.')
614
 
        except errors.FileExists:
615
 
            pass
 
786
        t.ensure_base()
616
787
        if lightweight:
617
 
            checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
 
788
            format = self._get_checkout_format()
 
789
            checkout = format.initialize_on_transport(t)
618
790
            BranchReferenceFormat().initialize(checkout, self)
619
791
        else:
620
792
            format = self._get_checkout_format()
625
797
            # pull up to the specified revision_id to set the initial 
626
798
            # branch tip correctly, and seed it with history.
627
799
            checkout_branch.pull(self, stop_revision=revision_id)
628
 
        return checkout.create_workingtree(revision_id)
 
800
        tree = checkout.create_workingtree(revision_id)
 
801
        basis_tree = tree.basis_tree()
 
802
        basis_tree.lock_read()
 
803
        try:
 
804
            for path, file_id in basis_tree.iter_references():
 
805
                reference_parent = self.reference_parent(file_id, path)
 
806
                reference_parent.create_checkout(tree.abspath(path),
 
807
                    basis_tree.get_reference_revision(file_id, path),
 
808
                    lightweight)
 
809
        finally:
 
810
            basis_tree.unlock()
 
811
        return tree
 
812
 
 
813
    def reference_parent(self, file_id, path):
 
814
        """Return the parent branch for a tree-reference file_id
 
815
        :param file_id: The file_id of the tree reference
 
816
        :param path: The path of the file_id in the tree
 
817
        :return: A branch associated with the file_id
 
818
        """
 
819
        # FIXME should provide multiple branches, based on config
 
820
        return Branch.open(self.bzrdir.root_transport.clone(path).base)
 
821
 
 
822
    def supports_tags(self):
 
823
        return self._format.supports_tags()
629
824
 
630
825
 
631
826
class BranchFormat(object):
669
864
        """Return the current default format."""
670
865
        return klass._default_format
671
866
 
 
867
    def get_reference(self, a_bzrdir):
 
868
        """Get the target reference of the branch in a_bzrdir.
 
869
 
 
870
        format probing must have been completed before calling
 
871
        this method - it is assumed that the format of the branch
 
872
        in a_bzrdir is correct.
 
873
 
 
874
        :param a_bzrdir: The bzrdir to get the branch data from.
 
875
        :return: None if the branch is not a reference branch.
 
876
        """
 
877
        return None
 
878
 
672
879
    def get_format_string(self):
673
880
        """Return the ASCII format string that identifies this format."""
674
881
        raise NotImplementedError(self.get_format_string)
675
882
 
676
883
    def get_format_description(self):
677
884
        """Return the short format description for this format."""
678
 
        raise NotImplementedError(self.get_format_string)
 
885
        raise NotImplementedError(self.get_format_description)
 
886
 
 
887
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
 
888
                           set_format=True):
 
889
        """Initialize a branch in a bzrdir, with specified files
 
890
 
 
891
        :param a_bzrdir: The bzrdir to initialize the branch in
 
892
        :param utf8_files: The files to create as a list of
 
893
            (filename, content) tuples
 
894
        :param set_format: If True, set the format with
 
895
            self.get_format_string.  (BzrBranch4 has its format set
 
896
            elsewhere)
 
897
        :return: a branch in this format
 
898
        """
 
899
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
900
        branch_transport = a_bzrdir.get_branch_transport(self)
 
901
        lock_map = {
 
902
            'metadir': ('lock', lockdir.LockDir),
 
903
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
904
        }
 
905
        lock_name, lock_class = lock_map[lock_type]
 
906
        control_files = lockable_files.LockableFiles(branch_transport,
 
907
            lock_name, lock_class)
 
908
        control_files.create_lock()
 
909
        control_files.lock_write()
 
910
        if set_format:
 
911
            control_files.put_utf8('format', self.get_format_string())
 
912
        try:
 
913
            for file, content in utf8_files:
 
914
                control_files.put_utf8(file, content)
 
915
        finally:
 
916
            control_files.unlock()
 
917
        return self.open(a_bzrdir, _found=True)
679
918
 
680
919
    def initialize(self, a_bzrdir):
681
920
        """Create a branch of this format in a_bzrdir."""
714
953
    def __str__(self):
715
954
        return self.get_format_string().rstrip()
716
955
 
 
956
    def supports_tags(self):
 
957
        """True if this format supports tags stored in the branch"""
 
958
        return False  # by default
 
959
 
 
960
    # XXX: Probably doesn't really belong here -- mbp 20070212
 
961
    def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
 
962
            lock_class):
 
963
        branch_transport = a_bzrdir.get_branch_transport(self)
 
964
        control_files = lockable_files.LockableFiles(branch_transport,
 
965
            lock_filename, lock_class)
 
966
        control_files.create_lock()
 
967
        control_files.lock_write()
 
968
        try:
 
969
            for filename, content in utf8_files:
 
970
                control_files.put_utf8(filename, content)
 
971
        finally:
 
972
            control_files.unlock()
 
973
 
 
974
 
 
975
class BranchHooks(Hooks):
 
976
    """A dictionary mapping hook name to a list of callables for branch hooks.
 
977
    
 
978
    e.g. ['set_rh'] Is the list of items to be called when the
 
979
    set_revision_history function is invoked.
 
980
    """
 
981
 
 
982
    def __init__(self):
 
983
        """Create the default hooks.
 
984
 
 
985
        These are all empty initially, because by default nothing should get
 
986
        notified.
 
987
        """
 
988
        Hooks.__init__(self)
 
989
        # Introduced in 0.15:
 
990
        # invoked whenever the revision history has been set
 
991
        # with set_revision_history. The api signature is
 
992
        # (branch, revision_history), and the branch will
 
993
        # be write-locked.
 
994
        self['set_rh'] = []
 
995
        # invoked after a push operation completes.
 
996
        # the api signature is
 
997
        # (push_result)
 
998
        # containing the members
 
999
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
 
1000
        # where local is the local branch or None, master is the target 
 
1001
        # master branch, and the rest should be self explanatory. The source
 
1002
        # is read locked and the target branches write locked. Source will
 
1003
        # be the local low-latency branch.
 
1004
        self['post_push'] = []
 
1005
        # invoked after a pull operation completes.
 
1006
        # the api signature is
 
1007
        # (pull_result)
 
1008
        # containing the members
 
1009
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
 
1010
        # where local is the local branch or None, master is the target 
 
1011
        # master branch, and the rest should be self explanatory. The source
 
1012
        # is read locked and the target branches write locked. The local
 
1013
        # branch is the low-latency branch.
 
1014
        self['post_pull'] = []
 
1015
        # invoked after a commit operation completes.
 
1016
        # the api signature is 
 
1017
        # (local, master, old_revno, old_revid, new_revno, new_revid)
 
1018
        # old_revid is NULL_REVISION for the first commit to a branch.
 
1019
        self['post_commit'] = []
 
1020
        # invoked after a uncommit operation completes.
 
1021
        # the api signature is
 
1022
        # (local, master, old_revno, old_revid, new_revno, new_revid) where
 
1023
        # local is the local branch or None, master is the target branch,
 
1024
        # and an empty branch recieves new_revno of 0, new_revid of None.
 
1025
        self['post_uncommit'] = []
 
1026
 
 
1027
 
 
1028
# install the default hooks into the Branch class.
 
1029
Branch.hooks = BranchHooks()
 
1030
 
717
1031
 
718
1032
class BzrBranchFormat4(BranchFormat):
719
1033
    """Bzr branch format 4.
729
1043
 
730
1044
    def initialize(self, a_bzrdir):
731
1045
        """Create a branch of this format in a_bzrdir."""
732
 
        mutter('creating branch in %s', a_bzrdir.transport.base)
733
 
        branch_transport = a_bzrdir.get_branch_transport(self)
734
1046
        utf8_files = [('revision-history', ''),
735
1047
                      ('branch-name', ''),
736
1048
                      ]
737
 
        control_files = lockable_files.LockableFiles(branch_transport,
738
 
                             'branch-lock', lockable_files.TransportLock)
739
 
        control_files.create_lock()
740
 
        control_files.lock_write()
741
 
        try:
742
 
            for file, content in utf8_files:
743
 
                control_files.put_utf8(file, content)
744
 
        finally:
745
 
            control_files.unlock()
746
 
        return self.open(a_bzrdir, _found=True)
 
1049
        return self._initialize_helper(a_bzrdir, utf8_files,
 
1050
                                       lock_type='branch4', set_format=False)
747
1051
 
748
1052
    def __init__(self):
749
1053
        super(BzrBranchFormat4, self).__init__()
790
1094
        
791
1095
    def initialize(self, a_bzrdir):
792
1096
        """Create a branch of this format in a_bzrdir."""
793
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
794
 
        branch_transport = a_bzrdir.get_branch_transport(self)
795
1097
        utf8_files = [('revision-history', ''),
796
1098
                      ('branch-name', ''),
797
1099
                      ]
798
 
        control_files = lockable_files.LockableFiles(branch_transport, 'lock',
799
 
                                                     lockdir.LockDir)
800
 
        control_files.create_lock()
801
 
        control_files.lock_write()
802
 
        control_files.put_utf8('format', self.get_format_string())
803
 
        try:
804
 
            for file, content in utf8_files:
805
 
                control_files.put_utf8(file, content)
806
 
        finally:
807
 
            control_files.unlock()
808
 
        return self.open(a_bzrdir, _found=True, )
 
1100
        return self._initialize_helper(a_bzrdir, utf8_files)
809
1101
 
810
1102
    def __init__(self):
811
1103
        super(BzrBranchFormat5, self).__init__()
820
1112
        if not _found:
821
1113
            format = BranchFormat.find_format(a_bzrdir)
822
1114
            assert format.__class__ == self.__class__
 
1115
        try:
 
1116
            transport = a_bzrdir.get_branch_transport(None)
 
1117
            control_files = lockable_files.LockableFiles(transport, 'lock',
 
1118
                                                         lockdir.LockDir)
 
1119
            return BzrBranch5(_format=self,
 
1120
                              _control_files=control_files,
 
1121
                              a_bzrdir=a_bzrdir,
 
1122
                              _repository=a_bzrdir.find_repository())
 
1123
        except NoSuchFile:
 
1124
            raise NotBranchError(path=transport.base)
 
1125
 
 
1126
 
 
1127
class BzrBranchFormat6(BzrBranchFormat5):
 
1128
    """Branch format with last-revision
 
1129
 
 
1130
    Unlike previous formats, this has no explicit revision history. Instead,
 
1131
    this just stores the last-revision, and the left-hand history leading
 
1132
    up to there is the history.
 
1133
 
 
1134
    This format was introduced in bzr 0.15
 
1135
    """
 
1136
 
 
1137
    def get_format_string(self):
 
1138
        """See BranchFormat.get_format_string()."""
 
1139
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
 
1140
 
 
1141
    def get_format_description(self):
 
1142
        """See BranchFormat.get_format_description()."""
 
1143
        return "Branch format 6"
 
1144
 
 
1145
    def initialize(self, a_bzrdir):
 
1146
        """Create a branch of this format in a_bzrdir."""
 
1147
        utf8_files = [('last-revision', '0 null:\n'),
 
1148
                      ('branch-name', ''),
 
1149
                      ('branch.conf', ''),
 
1150
                      ('tags', ''),
 
1151
                      ]
 
1152
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1153
 
 
1154
    def open(self, a_bzrdir, _found=False):
 
1155
        """Return the branch object for a_bzrdir
 
1156
 
 
1157
        _found is a private parameter, do not use it. It is used to indicate
 
1158
               if format probing has already be done.
 
1159
        """
 
1160
        if not _found:
 
1161
            format = BranchFormat.find_format(a_bzrdir)
 
1162
            assert format.__class__ == self.__class__
823
1163
        transport = a_bzrdir.get_branch_transport(None)
824
1164
        control_files = lockable_files.LockableFiles(transport, 'lock',
825
1165
                                                     lockdir.LockDir)
826
 
        return BzrBranch5(_format=self,
 
1166
        return BzrBranch6(_format=self,
827
1167
                          _control_files=control_files,
828
1168
                          a_bzrdir=a_bzrdir,
829
1169
                          _repository=a_bzrdir.find_repository())
830
1170
 
831
 
    def __str__(self):
832
 
        return "Bazaar-NG Metadir branch format 5"
 
1171
    def supports_tags(self):
 
1172
        return True
833
1173
 
834
1174
 
835
1175
class BranchReferenceFormat(BranchFormat):
851
1191
        """See BranchFormat.get_format_description()."""
852
1192
        return "Checkout reference format 1"
853
1193
        
 
1194
    def get_reference(self, a_bzrdir):
 
1195
        """See BranchFormat.get_reference()."""
 
1196
        transport = a_bzrdir.get_branch_transport(None)
 
1197
        return transport.get('location').read()
 
1198
 
854
1199
    def initialize(self, a_bzrdir, target_branch=None):
855
1200
        """Create a branch of this format in a_bzrdir."""
856
1201
        if target_branch is None:
878
1223
            # emit some sort of warning/error to the caller ?!
879
1224
        return clone
880
1225
 
881
 
    def open(self, a_bzrdir, _found=False):
 
1226
    def open(self, a_bzrdir, _found=False, location=None):
882
1227
        """Return the branch that the branch reference in a_bzrdir points at.
883
1228
 
884
1229
        _found is a private parameter, do not use it. It is used to indicate
887
1232
        if not _found:
888
1233
            format = BranchFormat.find_format(a_bzrdir)
889
1234
            assert format.__class__ == self.__class__
890
 
        transport = a_bzrdir.get_branch_transport(None)
891
 
        real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
 
1235
        if location is None:
 
1236
            location = self.get_reference(a_bzrdir)
 
1237
        real_bzrdir = bzrdir.BzrDir.open(location)
892
1238
        result = real_bzrdir.open_branch()
893
1239
        # this changes the behaviour of result.clone to create a new reference
894
1240
        # rather than a copy of the content of the branch.
907
1253
__default_format = BzrBranchFormat5()
908
1254
BranchFormat.register_format(__default_format)
909
1255
BranchFormat.register_format(BranchReferenceFormat())
 
1256
BranchFormat.register_format(BzrBranchFormat6())
910
1257
BranchFormat.set_default_format(__default_format)
911
1258
_legacy_formats = [BzrBranchFormat4(),
912
1259
                   ]
919
1266
    it's writable, and can be accessed via the normal filesystem API.
920
1267
    """
921
1268
    
922
 
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
923
 
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
 
1269
    def __init__(self, _format=None,
924
1270
                 _control_files=None, a_bzrdir=None, _repository=None):
925
 
        """Create new branch object at a particular location.
926
 
 
927
 
        transport -- A Transport object, defining how to access files.
928
 
        
929
 
        init -- If True, create new control files in a previously
930
 
             unversioned directory.  If False, the branch must already
931
 
             be versioned.
932
 
 
933
 
        relax_version_check -- If true, the usual check for the branch
934
 
            version is not applied.  This is intended only for
935
 
            upgrade/recovery type use; it's not guaranteed that
936
 
            all operations will work on old format branches.
937
 
        """
 
1271
        """Create new branch object at a particular location."""
 
1272
        Branch.__init__(self)
938
1273
        if a_bzrdir is None:
939
 
            self.bzrdir = bzrdir.BzrDir.open(transport.base)
 
1274
            raise ValueError('a_bzrdir must be supplied')
940
1275
        else:
941
1276
            self.bzrdir = a_bzrdir
942
 
        self._transport = self.bzrdir.transport.clone('..')
943
 
        self._base = self._transport.base
 
1277
        # self._transport used to point to the directory containing the
 
1278
        # control directory, but was not used - now it's just the transport
 
1279
        # for the branch control files.  mbp 20070212
 
1280
        self._base = self.bzrdir.transport.clone('..').base
944
1281
        self._format = _format
945
1282
        if _control_files is None:
946
1283
            raise ValueError('BzrBranch _control_files is None')
947
1284
        self.control_files = _control_files
948
 
        if deprecated_passed(init):
949
 
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
950
 
                 "deprecated as of bzr 0.8. Please use Branch.create().",
951
 
                 DeprecationWarning,
952
 
                 stacklevel=2)
953
 
            if init:
954
 
                # this is slower than before deprecation, oh well never mind.
955
 
                # -> its deprecated.
956
 
                self._initialize(transport.base)
957
 
        self._check_format(_format)
958
 
        if deprecated_passed(relax_version_check):
959
 
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
960
 
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
961
 
                 "Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
962
 
                 "open() method.",
963
 
                 DeprecationWarning,
964
 
                 stacklevel=2)
965
 
            if (not relax_version_check
966
 
                and not self._format.is_supported()):
967
 
                raise errors.UnsupportedFormatError(format=fmt)
968
 
        if deprecated_passed(transport):
969
 
            warn("BzrBranch.__init__(transport=XXX...): The transport "
970
 
                 "parameter is deprecated as of bzr 0.8. "
971
 
                 "Please use Branch.open, or bzrdir.open_branch().",
972
 
                 DeprecationWarning,
973
 
                 stacklevel=2)
 
1285
        self._transport = _control_files._transport
974
1286
        self.repository = _repository
975
1287
 
976
1288
    def __str__(self):
979
1291
    __repr__ = __str__
980
1292
 
981
1293
    def _get_base(self):
 
1294
        """Returns the directory containing the control directory."""
982
1295
        return self._base
983
1296
 
984
1297
    base = property(_get_base, doc="The URL for the root of this branch.")
985
1298
 
986
 
    def _finish_transaction(self):
987
 
        """Exit the current transaction."""
988
 
        return self.control_files._finish_transaction()
989
 
 
990
 
    def get_transaction(self):
991
 
        """Return the current active transaction.
992
 
 
993
 
        If no transaction is active, this returns a passthrough object
994
 
        for which all data is immediately flushed and no caching happens.
995
 
        """
996
 
        # this is an explicit function so that we can do tricky stuff
997
 
        # when the storage in rev_storage is elsewhere.
998
 
        # we probably need to hook the two 'lock a location' and 
999
 
        # 'have a transaction' together more delicately, so that
1000
 
        # we can have two locks (branch and storage) and one transaction
1001
 
        # ... and finishing the transaction unlocks both, but unlocking
1002
 
        # does not. - RBC 20051121
1003
 
        return self.control_files.get_transaction()
1004
 
 
1005
 
    def _set_transaction(self, transaction):
1006
 
        """Set a new active transaction."""
1007
 
        return self.control_files._set_transaction(transaction)
1008
 
 
1009
1299
    def abspath(self, name):
1010
1300
        """See Branch.abspath."""
1011
1301
        return self.control_files._transport.abspath(name)
1012
1302
 
1013
 
    def _check_format(self, format):
1014
 
        """Identify the branch format if needed.
1015
 
 
1016
 
        The format is stored as a reference to the format object in
1017
 
        self._format for code that needs to check it later.
1018
 
 
1019
 
        The format parameter is either None or the branch format class
1020
 
        used to open this branch.
1021
 
 
1022
 
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1023
 
        """
1024
 
        if format is None:
1025
 
            format = BranchFormat.find_format(self.bzrdir)
1026
 
        self._format = format
1027
 
        mutter("got branch format %s", self._format)
1028
 
 
 
1303
 
 
1304
    @deprecated_method(zero_sixteen)
1029
1305
    @needs_read_lock
1030
1306
    def get_root_id(self):
1031
1307
        """See Branch.get_root_id."""
1035
1311
    def is_locked(self):
1036
1312
        return self.control_files.is_locked()
1037
1313
 
1038
 
    def lock_write(self):
1039
 
        self.repository.lock_write()
 
1314
    def lock_write(self, token=None):
 
1315
        repo_token = self.repository.lock_write()
1040
1316
        try:
1041
 
            self.control_files.lock_write()
 
1317
            token = self.control_files.lock_write(token=token)
1042
1318
        except:
1043
1319
            self.repository.unlock()
1044
1320
            raise
 
1321
        return token
1045
1322
 
1046
1323
    def lock_read(self):
1047
1324
        self.repository.lock_read()
1057
1334
            self.control_files.unlock()
1058
1335
        finally:
1059
1336
            self.repository.unlock()
 
1337
        if not self.control_files.is_locked():
 
1338
            # we just released the lock
 
1339
            self._clear_cached_state()
1060
1340
        
1061
1341
    def peek_lock_mode(self):
1062
1342
        if self.control_files._lock_count == 0:
1075
1355
    @needs_write_lock
1076
1356
    def append_revision(self, *revision_ids):
1077
1357
        """See Branch.append_revision."""
 
1358
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1078
1359
        for revision_id in revision_ids:
 
1360
            _mod_revision.check_not_reserved_id(revision_id)
1079
1361
            mutter("add {%s} to revision-history" % revision_id)
1080
1362
        rev_history = self.revision_history()
1081
1363
        rev_history.extend(revision_ids)
1082
1364
        self.set_revision_history(rev_history)
1083
1365
 
 
1366
    def _write_revision_history(self, history):
 
1367
        """Factored out of set_revision_history.
 
1368
 
 
1369
        This performs the actual writing to disk.
 
1370
        It is intended to be called by BzrBranch5.set_revision_history."""
 
1371
        self.control_files.put_bytes(
 
1372
            'revision-history', '\n'.join(history))
 
1373
 
1084
1374
    @needs_write_lock
1085
1375
    def set_revision_history(self, rev_history):
1086
1376
        """See Branch.set_revision_history."""
1087
 
        self.control_files.put_utf8(
1088
 
            'revision-history', '\n'.join(rev_history))
1089
 
        transaction = self.get_transaction()
1090
 
        history = transaction.map.find_revision_history()
1091
 
        if history is not None:
1092
 
            # update the revision history in the identity map.
1093
 
            history[:] = list(rev_history)
1094
 
            # this call is disabled because revision_history is 
1095
 
            # not really an object yet, and the transaction is for objects.
1096
 
            # transaction.register_dirty(history)
1097
 
        else:
1098
 
            transaction.map.add_revision_history(rev_history)
1099
 
            # this call is disabled because revision_history is 
1100
 
            # not really an object yet, and the transaction is for objects.
1101
 
            # transaction.register_clean(history)
1102
 
 
1103
 
    @needs_read_lock
1104
 
    def revision_history(self):
1105
 
        """See Branch.revision_history."""
1106
 
        transaction = self.get_transaction()
1107
 
        history = transaction.map.find_revision_history()
1108
 
        if history is not None:
1109
 
            # mutter("cache hit for revision-history in %s", self)
1110
 
            return list(history)
1111
 
        decode_utf8 = cache_utf8.decode
1112
 
        history = [decode_utf8(l.rstrip('\r\n')) for l in
1113
 
                self.control_files.get('revision-history').readlines()]
1114
 
        transaction.map.add_revision_history(history)
1115
 
        # this call is disabled because revision_history is 
1116
 
        # not really an object yet, and the transaction is for objects.
1117
 
        # transaction.register_clean(history, precious=True)
1118
 
        return list(history)
 
1377
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
 
1378
        self._clear_cached_state()
 
1379
        self._write_revision_history(rev_history)
 
1380
        self._cache_revision_history(rev_history)
 
1381
        for hook in Branch.hooks['set_rh']:
 
1382
            hook(self, rev_history)
1119
1383
 
1120
1384
    @needs_write_lock
1121
 
    def generate_revision_history(self, revision_id, last_rev=None, 
1122
 
        other_branch=None):
1123
 
        """Create a new revision history that will finish with revision_id.
1124
 
        
1125
 
        :param revision_id: the new tip to use.
1126
 
        :param last_rev: The previous last_revision. If not None, then this
1127
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
1128
 
        :param other_branch: The other branch that DivergedBranches should
1129
 
            raise with respect to.
1130
 
        """
 
1385
    def set_last_revision_info(self, revno, revision_id):
 
1386
        revision_id = osutils.safe_revision_id(revision_id)
 
1387
        history = self._lefthand_history(revision_id)
 
1388
        assert len(history) == revno, '%d != %d' % (len(history), revno)
 
1389
        self.set_revision_history(history)
 
1390
 
 
1391
    def _gen_revision_history(self):
 
1392
        history = self.control_files.get('revision-history').read().split('\n')
 
1393
        if history[-1:] == ['']:
 
1394
            # There shouldn't be a trailing newline, but just in case.
 
1395
            history.pop()
 
1396
        return history
 
1397
 
 
1398
    def _lefthand_history(self, revision_id, last_rev=None,
 
1399
                          other_branch=None):
1131
1400
        # stop_revision must be a descendant of last_revision
1132
1401
        stop_graph = self.repository.get_revision_graph(revision_id)
1133
1402
        if last_rev is not None and last_rev not in stop_graph:
1144
1413
            except IndexError:
1145
1414
                current_rev_id = None
1146
1415
        new_history.reverse()
1147
 
        self.set_revision_history(new_history)
 
1416
        return new_history
 
1417
 
 
1418
    @needs_write_lock
 
1419
    def generate_revision_history(self, revision_id, last_rev=None,
 
1420
        other_branch=None):
 
1421
        """Create a new revision history that will finish with revision_id.
 
1422
 
 
1423
        :param revision_id: the new tip to use.
 
1424
        :param last_rev: The previous last_revision. If not None, then this
 
1425
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
1426
        :param other_branch: The other branch that DivergedBranches should
 
1427
            raise with respect to.
 
1428
        """
 
1429
        revision_id = osutils.safe_revision_id(revision_id)
 
1430
        self.set_revision_history(self._lefthand_history(revision_id,
 
1431
            last_rev, other_branch))
1148
1432
 
1149
1433
    @needs_write_lock
1150
1434
    def update_revisions(self, other, stop_revision=None):
1156
1440
                if stop_revision is None:
1157
1441
                    # if there are no commits, we're done.
1158
1442
                    return
 
1443
            else:
 
1444
                stop_revision = osutils.safe_revision_id(stop_revision)
1159
1445
            # whats the current last revision, before we fetch [and change it
1160
1446
            # possibly]
1161
1447
            last_rev = self.last_revision()
1186
1472
        return self.bzrdir.open_workingtree()
1187
1473
 
1188
1474
    @needs_write_lock
1189
 
    def pull(self, source, overwrite=False, stop_revision=None):
1190
 
        """See Branch.pull."""
 
1475
    def pull(self, source, overwrite=False, stop_revision=None,
 
1476
        _hook_master=None, _run_hooks=True):
 
1477
        """See Branch.pull.
 
1478
 
 
1479
        :param _hook_master: Private parameter - set the branch to 
 
1480
            be supplied as the master to push hooks.
 
1481
        :param _run_hooks: Private parameter - allow disabling of
 
1482
            hooks, used when pushing to a master branch.
 
1483
        """
 
1484
        result = PullResult()
 
1485
        result.source_branch = source
 
1486
        result.target_branch = self
1191
1487
        source.lock_read()
1192
1488
        try:
1193
 
            old_count = len(self.revision_history())
 
1489
            result.old_revno, result.old_revid = self.last_revision_info()
1194
1490
            try:
1195
1491
                self.update_revisions(source, stop_revision)
1196
1492
            except DivergedBranches:
1197
1493
                if not overwrite:
1198
1494
                    raise
1199
1495
            if overwrite:
1200
 
                self.set_revision_history(source.revision_history())
1201
 
            new_count = len(self.revision_history())
1202
 
            return new_count - old_count
 
1496
                if stop_revision is None:
 
1497
                    stop_revision = source.last_revision()
 
1498
                self.generate_revision_history(stop_revision)
 
1499
            result.tag_conflicts = source.tags.merge_to(self.tags)
 
1500
            result.new_revno, result.new_revid = self.last_revision_info()
 
1501
            if _hook_master:
 
1502
                result.master_branch = _hook_master
 
1503
                result.local_branch = self
 
1504
            else:
 
1505
                result.master_branch = self
 
1506
                result.local_branch = None
 
1507
            if _run_hooks:
 
1508
                for hook in Branch.hooks['post_pull']:
 
1509
                    hook(result)
1203
1510
        finally:
1204
1511
            source.unlock()
 
1512
        return result
 
1513
 
 
1514
    def _get_parent_location(self):
 
1515
        _locs = ['parent', 'pull', 'x-pull']
 
1516
        for l in _locs:
 
1517
            try:
 
1518
                return self.control_files.get(l).read().strip('\n')
 
1519
            except NoSuchFile:
 
1520
                pass
 
1521
        return None
 
1522
 
 
1523
    @needs_read_lock
 
1524
    def push(self, target, overwrite=False, stop_revision=None,
 
1525
        _hook_master=None, _run_hooks=True):
 
1526
        """See Branch.push.
 
1527
        
 
1528
        :param _hook_master: Private parameter - set the branch to 
 
1529
            be supplied as the master to push hooks.
 
1530
        :param _run_hooks: Private parameter - allow disabling of
 
1531
            hooks, used when pushing to a master branch.
 
1532
        """
 
1533
        result = PushResult()
 
1534
        result.source_branch = self
 
1535
        result.target_branch = target
 
1536
        target.lock_write()
 
1537
        try:
 
1538
            result.old_revno, result.old_revid = target.last_revision_info()
 
1539
            try:
 
1540
                target.update_revisions(self, stop_revision)
 
1541
            except DivergedBranches:
 
1542
                if not overwrite:
 
1543
                    raise
 
1544
            if overwrite:
 
1545
                target.set_revision_history(self.revision_history())
 
1546
            result.tag_conflicts = self.tags.merge_to(target.tags)
 
1547
            result.new_revno, result.new_revid = target.last_revision_info()
 
1548
            if _hook_master:
 
1549
                result.master_branch = _hook_master
 
1550
                result.local_branch = target
 
1551
            else:
 
1552
                result.master_branch = target
 
1553
                result.local_branch = None
 
1554
            if _run_hooks:
 
1555
                for hook in Branch.hooks['post_push']:
 
1556
                    hook(result)
 
1557
        finally:
 
1558
            target.unlock()
 
1559
        return result
1205
1560
 
1206
1561
    def get_parent(self):
1207
1562
        """See Branch.get_parent."""
1208
1563
 
1209
 
        _locs = ['parent', 'pull', 'x-pull']
1210
1564
        assert self.base[-1] == '/'
1211
 
        for l in _locs:
1212
 
            try:
1213
 
                parent = self.control_files.get(l).read().strip('\n')
1214
 
            except NoSuchFile:
1215
 
                continue
1216
 
            # This is an old-format absolute path to a local branch
1217
 
            # turn it into a url
1218
 
            if parent.startswith('/'):
1219
 
                parent = urlutils.local_path_to_url(parent.decode('utf8'))
1220
 
            try:
1221
 
                return urlutils.join(self.base[:-1], parent)
1222
 
            except errors.InvalidURLJoin, e:
1223
 
                raise errors.InaccessibleParent(parent, self.base)
1224
 
        return None
1225
 
 
1226
 
    def get_push_location(self):
1227
 
        """See Branch.get_push_location."""
1228
 
        push_loc = self.get_config().get_user_option('push_location')
1229
 
        return push_loc
 
1565
        parent = self._get_parent_location()
 
1566
        if parent is None:
 
1567
            return parent
 
1568
        # This is an old-format absolute path to a local branch
 
1569
        # turn it into a url
 
1570
        if parent.startswith('/'):
 
1571
            parent = urlutils.local_path_to_url(parent.decode('utf8'))
 
1572
        try:
 
1573
            return urlutils.join(self.base[:-1], parent)
 
1574
        except errors.InvalidURLJoin, e:
 
1575
            raise errors.InaccessibleParent(parent, self.base)
1230
1576
 
1231
1577
    def set_push_location(self, location):
1232
1578
        """See Branch.set_push_location."""
1233
 
        self.get_config().set_user_option('push_location', location, 
1234
 
                                          local=True)
 
1579
        self.get_config().set_user_option(
 
1580
            'push_location', location,
 
1581
            store=_mod_config.STORE_LOCATION_NORECURSE)
1235
1582
 
1236
1583
    @needs_write_lock
1237
1584
    def set_parent(self, url):
1241
1588
        # FIXUP this and get_parent in a future branch format bump:
1242
1589
        # read and rewrite the file, and have the new format code read
1243
1590
        # using .get not .get_utf8. RBC 20060125
1244
 
        if url is None:
1245
 
            self.control_files._transport.delete('parent')
1246
 
        else:
 
1591
        if url is not None:
1247
1592
            if isinstance(url, unicode):
1248
1593
                try: 
1249
1594
                    url = url.encode('ascii')
1250
1595
                except UnicodeEncodeError:
1251
 
                    raise bzrlib.errors.InvalidURL(url,
 
1596
                    raise errors.InvalidURL(url,
1252
1597
                        "Urls must be 7-bit ascii, "
1253
1598
                        "use bzrlib.urlutils.escape")
1254
 
                    
1255
1599
            url = urlutils.relative_url(self.base, url)
1256
 
            self.control_files.put('parent', StringIO(url + '\n'))
 
1600
        self._set_parent_location(url)
 
1601
 
 
1602
    def _set_parent_location(self, url):
 
1603
        if url is None:
 
1604
            self.control_files._transport.delete('parent')
 
1605
        else:
 
1606
            assert isinstance(url, str)
 
1607
            self.control_files.put_bytes('parent', url + '\n')
1257
1608
 
1258
1609
    @deprecated_function(zero_nine)
1259
1610
    def tree_config(self):
1279
1630
                                         _repository=_repository)
1280
1631
        
1281
1632
    @needs_write_lock
1282
 
    def pull(self, source, overwrite=False, stop_revision=None):
1283
 
        """Updates branch.pull to be bound branch aware."""
 
1633
    def pull(self, source, overwrite=False, stop_revision=None,
 
1634
        _run_hooks=True):
 
1635
        """Extends branch.pull to be bound branch aware.
 
1636
        
 
1637
        :param _run_hooks: Private parameter used to force hook running
 
1638
            off during bound branch double-pushing.
 
1639
        """
1284
1640
        bound_location = self.get_bound_location()
1285
 
        if source.base != bound_location:
 
1641
        master_branch = None
 
1642
        if bound_location and source.base != bound_location:
1286
1643
            # not pulling from master, so we need to update master.
1287
1644
            master_branch = self.get_master_branch()
1288
 
            if master_branch:
1289
 
                master_branch.pull(source)
1290
 
                source = master_branch
1291
 
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
 
1645
            master_branch.lock_write()
 
1646
        try:
 
1647
            if master_branch:
 
1648
                # pull from source into master.
 
1649
                master_branch.pull(source, overwrite, stop_revision,
 
1650
                    _run_hooks=False)
 
1651
            return super(BzrBranch5, self).pull(source, overwrite,
 
1652
                stop_revision, _hook_master=master_branch,
 
1653
                _run_hooks=_run_hooks)
 
1654
        finally:
 
1655
            if master_branch:
 
1656
                master_branch.unlock()
 
1657
 
 
1658
    @needs_read_lock
 
1659
    def push(self, target, overwrite=False, stop_revision=None):
 
1660
        """Updates branch.push to be bound branch aware."""
 
1661
        bound_location = target.get_bound_location()
 
1662
        master_branch = None
 
1663
        if bound_location and target.base != bound_location:
 
1664
            # not pushing to master, so we need to update master.
 
1665
            master_branch = target.get_master_branch()
 
1666
            master_branch.lock_write()
 
1667
        try:
 
1668
            if master_branch:
 
1669
                # push into the master from this branch.
 
1670
                super(BzrBranch5, self).push(master_branch, overwrite,
 
1671
                    stop_revision, _run_hooks=False)
 
1672
            # and push into the target branch from this. Note that we push from
 
1673
            # this branch again, because its considered the highest bandwidth
 
1674
            # repository.
 
1675
            return super(BzrBranch5, self).push(target, overwrite,
 
1676
                stop_revision, _hook_master=master_branch)
 
1677
        finally:
 
1678
            if master_branch:
 
1679
                master_branch.unlock()
1292
1680
 
1293
1681
    def get_bound_location(self):
1294
1682
        try:
1395
1783
        return None
1396
1784
 
1397
1785
 
 
1786
class BzrBranchExperimental(BzrBranch5):
 
1787
    """Bzr experimental branch format
 
1788
 
 
1789
    This format has:
 
1790
     - a revision-history file.
 
1791
     - a format string
 
1792
     - a lock dir guarding the branch itself
 
1793
     - all of this stored in a branch/ subdirectory
 
1794
     - works with shared repositories.
 
1795
     - a tag dictionary in the branch
 
1796
 
 
1797
    This format is new in bzr 0.15, but shouldn't be used for real data, 
 
1798
    only for testing.
 
1799
 
 
1800
    This class acts as it's own BranchFormat.
 
1801
    """
 
1802
 
 
1803
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
1804
 
 
1805
    @classmethod
 
1806
    def get_format_string(cls):
 
1807
        """See BranchFormat.get_format_string()."""
 
1808
        return "Bazaar-NG branch format experimental\n"
 
1809
 
 
1810
    @classmethod
 
1811
    def get_format_description(cls):
 
1812
        """See BranchFormat.get_format_description()."""
 
1813
        return "Experimental branch format"
 
1814
 
 
1815
    @classmethod
 
1816
    def get_reference(cls, a_bzrdir):
 
1817
        """Get the target reference of the branch in a_bzrdir.
 
1818
 
 
1819
        format probing must have been completed before calling
 
1820
        this method - it is assumed that the format of the branch
 
1821
        in a_bzrdir is correct.
 
1822
 
 
1823
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1824
        :return: None if the branch is not a reference branch.
 
1825
        """
 
1826
        return None
 
1827
 
 
1828
    @classmethod
 
1829
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
 
1830
            lock_class):
 
1831
        branch_transport = a_bzrdir.get_branch_transport(cls)
 
1832
        control_files = lockable_files.LockableFiles(branch_transport,
 
1833
            lock_filename, lock_class)
 
1834
        control_files.create_lock()
 
1835
        control_files.lock_write()
 
1836
        try:
 
1837
            for filename, content in utf8_files:
 
1838
                control_files.put_utf8(filename, content)
 
1839
        finally:
 
1840
            control_files.unlock()
 
1841
        
 
1842
    @classmethod
 
1843
    def initialize(cls, a_bzrdir):
 
1844
        """Create a branch of this format in a_bzrdir."""
 
1845
        utf8_files = [('format', cls.get_format_string()),
 
1846
                      ('revision-history', ''),
 
1847
                      ('branch-name', ''),
 
1848
                      ('tags', ''),
 
1849
                      ]
 
1850
        cls._initialize_control_files(a_bzrdir, utf8_files,
 
1851
            'lock', lockdir.LockDir)
 
1852
        return cls.open(a_bzrdir, _found=True)
 
1853
 
 
1854
    @classmethod
 
1855
    def open(cls, a_bzrdir, _found=False):
 
1856
        """Return the branch object for a_bzrdir
 
1857
 
 
1858
        _found is a private parameter, do not use it. It is used to indicate
 
1859
               if format probing has already be done.
 
1860
        """
 
1861
        if not _found:
 
1862
            format = BranchFormat.find_format(a_bzrdir)
 
1863
            assert format.__class__ == cls
 
1864
        transport = a_bzrdir.get_branch_transport(None)
 
1865
        control_files = lockable_files.LockableFiles(transport, 'lock',
 
1866
                                                     lockdir.LockDir)
 
1867
        return cls(_format=cls,
 
1868
            _control_files=control_files,
 
1869
            a_bzrdir=a_bzrdir,
 
1870
            _repository=a_bzrdir.find_repository())
 
1871
 
 
1872
    @classmethod
 
1873
    def is_supported(cls):
 
1874
        return True
 
1875
 
 
1876
    def _make_tags(self):
 
1877
        return BasicTags(self)
 
1878
 
 
1879
    @classmethod
 
1880
    def supports_tags(cls):
 
1881
        return True
 
1882
 
 
1883
 
 
1884
BranchFormat.register_format(BzrBranchExperimental)
 
1885
 
 
1886
 
 
1887
class BzrBranch6(BzrBranch5):
 
1888
 
 
1889
    @needs_read_lock
 
1890
    def last_revision_info(self):
 
1891
        revision_string = self.control_files.get('last-revision').read()
 
1892
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
1893
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
1894
        revno = int(revno)
 
1895
        return revno, revision_id
 
1896
 
 
1897
    def last_revision(self):
 
1898
        """Return last revision id, or None"""
 
1899
        revision_id = self.last_revision_info()[1]
 
1900
        if revision_id == _mod_revision.NULL_REVISION:
 
1901
            revision_id = None
 
1902
        return revision_id
 
1903
 
 
1904
    def _write_last_revision_info(self, revno, revision_id):
 
1905
        """Simply write out the revision id, with no checks.
 
1906
 
 
1907
        Use set_last_revision_info to perform this safely.
 
1908
 
 
1909
        Does not update the revision_history cache.
 
1910
        Intended to be called by set_last_revision_info and
 
1911
        _write_revision_history.
 
1912
        """
 
1913
        if revision_id is None:
 
1914
            revision_id = 'null:'
 
1915
        out_string = '%d %s\n' % (revno, revision_id)
 
1916
        self.control_files.put_bytes('last-revision', out_string)
 
1917
 
 
1918
    @needs_write_lock
 
1919
    def set_last_revision_info(self, revno, revision_id):
 
1920
        revision_id = osutils.safe_revision_id(revision_id)
 
1921
        if self._get_append_revisions_only():
 
1922
            self._check_history_violation(revision_id)
 
1923
        self._write_last_revision_info(revno, revision_id)
 
1924
        self._clear_cached_state()
 
1925
 
 
1926
    def _check_history_violation(self, revision_id):
 
1927
        last_revision = self.last_revision()
 
1928
        if last_revision is None:
 
1929
            return
 
1930
        if last_revision not in self._lefthand_history(revision_id):
 
1931
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
1932
 
 
1933
    def _gen_revision_history(self):
 
1934
        """Generate the revision history from last revision
 
1935
        """
 
1936
        history = list(self.repository.iter_reverse_revision_history(
 
1937
            self.last_revision()))
 
1938
        history.reverse()
 
1939
        return history
 
1940
 
 
1941
    def _write_revision_history(self, history):
 
1942
        """Factored out of set_revision_history.
 
1943
 
 
1944
        This performs the actual writing to disk, with format-specific checks.
 
1945
        It is intended to be called by BzrBranch5.set_revision_history.
 
1946
        """
 
1947
        if len(history) == 0:
 
1948
            last_revision = 'null:'
 
1949
        else:
 
1950
            if history != self._lefthand_history(history[-1]):
 
1951
                raise errors.NotLefthandHistory(history)
 
1952
            last_revision = history[-1]
 
1953
        if self._get_append_revisions_only():
 
1954
            self._check_history_violation(last_revision)
 
1955
        self._write_last_revision_info(len(history), last_revision)
 
1956
 
 
1957
    @needs_write_lock
 
1958
    def append_revision(self, *revision_ids):
 
1959
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
 
1960
        if len(revision_ids) == 0:
 
1961
            return
 
1962
        prev_revno, prev_revision = self.last_revision_info()
 
1963
        for revision in self.repository.get_revisions(revision_ids):
 
1964
            if prev_revision == _mod_revision.NULL_REVISION:
 
1965
                if revision.parent_ids != []:
 
1966
                    raise errors.NotLeftParentDescendant(self, prev_revision,
 
1967
                                                         revision.revision_id)
 
1968
            else:
 
1969
                if revision.parent_ids[0] != prev_revision:
 
1970
                    raise errors.NotLeftParentDescendant(self, prev_revision,
 
1971
                                                         revision.revision_id)
 
1972
            prev_revision = revision.revision_id
 
1973
        self.set_last_revision_info(prev_revno + len(revision_ids),
 
1974
                                    revision_ids[-1])
 
1975
 
 
1976
    @needs_write_lock
 
1977
    def _set_parent_location(self, url):
 
1978
        """Set the parent branch"""
 
1979
        self._set_config_location('parent_location', url, make_relative=True)
 
1980
 
 
1981
    @needs_read_lock
 
1982
    def _get_parent_location(self):
 
1983
        """Set the parent branch"""
 
1984
        return self._get_config_location('parent_location')
 
1985
 
 
1986
    def set_push_location(self, location):
 
1987
        """See Branch.set_push_location."""
 
1988
        self._set_config_location('push_location', location)
 
1989
 
 
1990
    def set_bound_location(self, location):
 
1991
        """See Branch.set_push_location."""
 
1992
        result = None
 
1993
        config = self.get_config()
 
1994
        if location is None:
 
1995
            if config.get_user_option('bound') != 'True':
 
1996
                return False
 
1997
            else:
 
1998
                config.set_user_option('bound', 'False')
 
1999
                return True
 
2000
        else:
 
2001
            self._set_config_location('bound_location', location,
 
2002
                                      config=config)
 
2003
            config.set_user_option('bound', 'True')
 
2004
        return True
 
2005
 
 
2006
    def _get_bound_location(self, bound):
 
2007
        """Return the bound location in the config file.
 
2008
 
 
2009
        Return None if the bound parameter does not match"""
 
2010
        config = self.get_config()
 
2011
        config_bound = (config.get_user_option('bound') == 'True')
 
2012
        if config_bound != bound:
 
2013
            return None
 
2014
        return self._get_config_location('bound_location', config=config)
 
2015
 
 
2016
    def get_bound_location(self):
 
2017
        """See Branch.set_push_location."""
 
2018
        return self._get_bound_location(True)
 
2019
 
 
2020
    def get_old_bound_location(self):
 
2021
        """See Branch.get_old_bound_location"""
 
2022
        return self._get_bound_location(False)
 
2023
 
 
2024
    def set_append_revisions_only(self, enabled):
 
2025
        if enabled:
 
2026
            value = 'True'
 
2027
        else:
 
2028
            value = 'False'
 
2029
        self.get_config().set_user_option('append_revisions_only', value)
 
2030
 
 
2031
    def _get_append_revisions_only(self):
 
2032
        value = self.get_config().get_user_option('append_revisions_only')
 
2033
        return value == 'True'
 
2034
 
 
2035
    def _synchronize_history(self, destination, revision_id):
 
2036
        """Synchronize last revision and revision history between branches.
 
2037
 
 
2038
        This version is most efficient when the destination is also a
 
2039
        BzrBranch6, but works for BzrBranch5, as long as the destination's
 
2040
        repository contains all the lefthand ancestors of the intended
 
2041
        last_revision.  If not, set_last_revision_info will fail.
 
2042
 
 
2043
        :param destination: The branch to copy the history into
 
2044
        :param revision_id: The revision-id to truncate history at.  May
 
2045
          be None to copy complete history.
 
2046
        """
 
2047
        if revision_id is None:
 
2048
            revno, revision_id = self.last_revision_info()
 
2049
        else:
 
2050
            revno = self.revision_id_to_revno(revision_id)
 
2051
        destination.set_last_revision_info(revno, revision_id)
 
2052
 
 
2053
    def _make_tags(self):
 
2054
        return BasicTags(self)
 
2055
 
 
2056
 
1398
2057
class BranchTestProviderAdapter(object):
1399
2058
    """A tool to generate a suite testing multiple branch formats at once.
1400
2059
 
1404
2063
    easy to identify.
1405
2064
    """
1406
2065
 
1407
 
    def __init__(self, transport_server, transport_readonly_server, formats):
 
2066
    def __init__(self, transport_server, transport_readonly_server, formats,
 
2067
        vfs_transport_factory=None):
1408
2068
        self._transport_server = transport_server
1409
2069
        self._transport_readonly_server = transport_readonly_server
1410
2070
        self._formats = formats
1418
2078
            new_test.bzrdir_format = bzrdir_format
1419
2079
            new_test.branch_format = branch_format
1420
2080
            def make_new_test_id():
1421
 
                new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
 
2081
                # the format can be either a class or an instance
 
2082
                name = getattr(branch_format, '__name__',
 
2083
                        branch_format.__class__.__name__)
 
2084
                new_id = "%s(%s)" % (new_test.id(), name)
1422
2085
                return lambda: new_id
1423
2086
            new_test.id = make_new_test_id()
1424
2087
            result.addTest(new_test)
1425
2088
        return result
1426
2089
 
1427
2090
 
 
2091
######################################################################
 
2092
# results of operations
 
2093
 
 
2094
 
 
2095
class _Result(object):
 
2096
 
 
2097
    def _show_tag_conficts(self, to_file):
 
2098
        if not getattr(self, 'tag_conflicts', None):
 
2099
            return
 
2100
        to_file.write('Conflicting tags:\n')
 
2101
        for name, value1, value2 in self.tag_conflicts:
 
2102
            to_file.write('    %s\n' % (name, ))
 
2103
 
 
2104
 
 
2105
class PullResult(_Result):
 
2106
    """Result of a Branch.pull operation.
 
2107
 
 
2108
    :ivar old_revno: Revision number before pull.
 
2109
    :ivar new_revno: Revision number after pull.
 
2110
    :ivar old_revid: Tip revision id before pull.
 
2111
    :ivar new_revid: Tip revision id after pull.
 
2112
    :ivar source_branch: Source (local) branch object.
 
2113
    :ivar master_branch: Master branch of the target, or None.
 
2114
    :ivar target_branch: Target/destination branch object.
 
2115
    """
 
2116
 
 
2117
    def __int__(self):
 
2118
        # DEPRECATED: pull used to return the change in revno
 
2119
        return self.new_revno - self.old_revno
 
2120
 
 
2121
    def report(self, to_file):
 
2122
        if self.old_revid == self.new_revid:
 
2123
            to_file.write('No revisions to pull.\n')
 
2124
        else:
 
2125
            to_file.write('Now on revision %d.\n' % self.new_revno)
 
2126
        self._show_tag_conficts(to_file)
 
2127
 
 
2128
 
 
2129
class PushResult(_Result):
 
2130
    """Result of a Branch.push operation.
 
2131
 
 
2132
    :ivar old_revno: Revision number before push.
 
2133
    :ivar new_revno: Revision number after push.
 
2134
    :ivar old_revid: Tip revision id before push.
 
2135
    :ivar new_revid: Tip revision id after push.
 
2136
    :ivar source_branch: Source branch object.
 
2137
    :ivar master_branch: Master branch of the target, or None.
 
2138
    :ivar target_branch: Target/destination branch object.
 
2139
    """
 
2140
 
 
2141
    def __int__(self):
 
2142
        # DEPRECATED: push used to return the change in revno
 
2143
        return self.new_revno - self.old_revno
 
2144
 
 
2145
    def report(self, to_file):
 
2146
        """Write a human-readable description of the result."""
 
2147
        if self.old_revid == self.new_revid:
 
2148
            to_file.write('No new revisions to push.\n')
 
2149
        else:
 
2150
            to_file.write('Pushed up to revision %d.\n' % self.new_revno)
 
2151
        self._show_tag_conficts(to_file)
 
2152
 
 
2153
 
1428
2154
class BranchCheckResult(object):
1429
2155
    """Results of checking branch consistency.
1430
2156
 
1445
2171
             self.branch._format)
1446
2172
 
1447
2173
 
1448
 
######################################################################
1449
 
# predicates
1450
 
 
1451
 
 
1452
 
@deprecated_function(zero_eight)
1453
 
def is_control_file(*args, **kwargs):
1454
 
    """See bzrlib.workingtree.is_control_file."""
1455
 
    return bzrlib.workingtree.is_control_file(*args, **kwargs)
 
2174
class Converter5to6(object):
 
2175
    """Perform an in-place upgrade of format 5 to format 6"""
 
2176
 
 
2177
    def convert(self, branch):
 
2178
        # Data for 5 and 6 can peacefully coexist.
 
2179
        format = BzrBranchFormat6()
 
2180
        new_branch = format.open(branch.bzrdir, _found=True)
 
2181
 
 
2182
        # Copy source data into target
 
2183
        new_branch.set_last_revision_info(*branch.last_revision_info())
 
2184
        new_branch.set_parent(branch.get_parent())
 
2185
        new_branch.set_bound_location(branch.get_bound_location())
 
2186
        new_branch.set_push_location(branch.get_push_location())
 
2187
 
 
2188
        # New branch has no tags by default
 
2189
        new_branch.tags._set_tag_dict({})
 
2190
 
 
2191
        # Copying done; now update target format
 
2192
        new_branch.control_files.put_utf8('format',
 
2193
            format.get_format_string())
 
2194
 
 
2195
        # Clean up old files
 
2196
        new_branch.control_files._transport.delete('revision-history')
 
2197
        try:
 
2198
            branch.set_parent(None)
 
2199
        except NoSuchFile:
 
2200
            pass
 
2201
        branch.set_bound_location(None)