/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 bzrlib/branch.py

  • Committer: John Arbash Meinel
  • Date: 2006-09-20 14:51:03 UTC
  • mfrom: (0.8.23 version_info)
  • mto: This revision was merged to the branch mainline in revision 2028.
  • Revision ID: john@arbash-meinel.com-20060920145103-02725c6d6c886040
[merge] version-info plugin, and cleanup for layout in bzr

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
from copy import deepcopy
18
19
from cStringIO import StringIO
19
 
 
20
 
from bzrlib.lazy_import import lazy_import
21
 
lazy_import(globals(), """
22
 
from copy import deepcopy
23
20
from unittest import TestSuite
24
21
from warnings import warn
25
22
 
27
24
from bzrlib import (
28
25
        bzrdir,
29
26
        cache_utf8,
30
 
        config as _mod_config,
31
27
        errors,
32
28
        lockdir,
33
 
        lockable_files,
34
29
        osutils,
35
 
        revision as _mod_revision,
 
30
        revision,
36
31
        transport,
37
32
        tree,
38
33
        ui,
39
34
        urlutils,
40
35
        )
41
 
from bzrlib.config import BranchConfig, TreeConfig
42
 
from bzrlib.lockable_files import LockableFiles, TransportLock
43
 
from bzrlib.tag import (
44
 
    BasicTags,
45
 
    DisabledTags,
46
 
    )
47
 
""")
48
 
 
 
36
from bzrlib.config import TreeConfig
49
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
50
 
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
51
 
                           HistoryMissing, InvalidRevisionId,
52
 
                           InvalidRevisionNumber, LockError, NoSuchFile,
 
38
import bzrlib.errors as errors
 
39
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches, 
 
40
                           HistoryMissing, InvalidRevisionId, 
 
41
                           InvalidRevisionNumber, LockError, NoSuchFile, 
53
42
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
54
 
                           NotBranchError, UninitializableFormat,
55
 
                           UnlistableStore, UnlistableBranch,
 
43
                           NotBranchError, UninitializableFormat, 
 
44
                           UnlistableStore, UnlistableBranch, 
56
45
                           )
57
 
from bzrlib.hooks import Hooks
 
46
from bzrlib.lockable_files import LockableFiles, TransportLock
58
47
from bzrlib.symbol_versioning import (deprecated_function,
59
48
                                      deprecated_method,
60
49
                                      DEPRECATED_PARAMETER,
61
50
                                      deprecated_passed,
62
 
                                      zero_eight, zero_nine, zero_sixteen,
 
51
                                      zero_eight, zero_nine,
63
52
                                      )
64
53
from bzrlib.trace import mutter, note
65
54
 
66
55
 
67
56
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
68
57
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
69
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
58
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
70
59
 
71
60
 
72
61
# TODO: Maybe include checks for common corruption of newlines, etc?
86
75
 
87
76
    base
88
77
        Base directory/url of the branch.
89
 
 
90
 
    hooks: An instance of BranchHooks.
91
78
    """
92
79
    # this is really an instance variable - FIXME move it there
93
80
    # - RBC 20060112
94
81
    base = None
95
82
 
96
 
    # override this to set the strategy for storing tags
97
 
    def _make_tags(self):
98
 
        return DisabledTags(self)
99
 
 
100
83
    def __init__(self, *ignored, **ignored_too):
101
 
        self.tags = self._make_tags()
102
 
        self._revision_history_cache = None
 
84
        raise NotImplementedError('The Branch class is abstract')
103
85
 
104
86
    def break_lock(self):
105
87
        """Break a lock if one is present from another instance.
166
148
        pass
167
149
 
168
150
    def get_config(self):
169
 
        return BranchConfig(self)
 
151
        return bzrlib.config.BranchConfig(self)
170
152
 
171
153
    def _get_nick(self):
172
154
        return self.get_config().get_nickname()
195
177
    def get_physical_lock_status(self):
196
178
        raise NotImplementedError(self.get_physical_lock_status)
197
179
 
198
 
    def leave_lock_in_place(self):
199
 
        """Tell this branch object not to release the physical lock when this
200
 
        object is unlocked.
201
 
        
202
 
        If lock_write doesn't return a token, then this method is not supported.
203
 
        """
204
 
        self.control_files.leave_in_place()
205
 
 
206
 
    def dont_leave_lock_in_place(self):
207
 
        """Tell this branch object to release the physical lock when this
208
 
        object is unlocked, even if it didn't originally acquire it.
209
 
 
210
 
        If lock_write doesn't return a token, then this method is not supported.
211
 
        """
212
 
        self.control_files.dont_leave_in_place()
213
 
 
214
180
    def abspath(self, name):
215
181
        """Return absolute filename for something in the branch
216
182
        
251
217
        try:
252
218
            if last_revision is None:
253
219
                pb.update('get source history')
254
 
                last_revision = from_branch.last_revision()
255
 
                if last_revision is None:
256
 
                    last_revision = _mod_revision.NULL_REVISION
 
220
                from_history = from_branch.revision_history()
 
221
                if from_history:
 
222
                    last_revision = from_history[-1]
 
223
                else:
 
224
                    # no history in the source branch
 
225
                    last_revision = revision.NULL_REVISION
257
226
            return self.repository.fetch(from_branch.repository,
258
227
                                         revision_id=last_revision,
259
228
                                         pb=nested_pb)
270
239
        """
271
240
        return None
272
241
    
273
 
    def get_old_bound_location(self):
274
 
        """Return the URL of the branch we used to be bound to
275
 
        """
276
 
        raise errors.UpgradeRequired(self.base)
277
 
 
278
242
    def get_commit_builder(self, parents, config=None, timestamp=None, 
279
243
                           timezone=None, committer=None, revprops=None, 
280
244
                           revision_id=None):
292
256
        if config is None:
293
257
            config = self.get_config()
294
258
        
295
 
        return self.repository.get_commit_builder(self, parents, config,
 
259
        return self.repository.get_commit_builder(self, parents, config, 
296
260
            timestamp, timezone, committer, revprops, revision_id)
297
261
 
298
262
    def get_master_branch(self):
314
278
            raise InvalidRevisionNumber(revno)
315
279
        return self.repository.get_revision_delta(rh[revno-1])
316
280
 
317
 
    @deprecated_method(zero_sixteen)
318
281
    def get_root_id(self):
319
 
        """Return the id of this branches root
320
 
 
321
 
        Deprecated: branches don't have root ids-- trees do.
322
 
        Use basis_tree().get_root_id() instead.
323
 
        """
 
282
        """Return the id of this branches root"""
324
283
        raise NotImplementedError(self.get_root_id)
325
284
 
326
285
    def print_file(self, file, revision_id):
333
292
    def set_revision_history(self, rev_history):
334
293
        raise NotImplementedError(self.set_revision_history)
335
294
 
336
 
    def _cache_revision_history(self, rev_history):
337
 
        """Set the cached revision history to rev_history.
338
 
 
339
 
        The revision_history method will use this cache to avoid regenerating
340
 
        the revision history.
341
 
 
342
 
        This API is semi-public; it only for use by subclasses, all other code
343
 
        should consider it to be private.
344
 
        """
345
 
        self._revision_history_cache = rev_history
346
 
 
347
 
    def _clear_cached_state(self):
348
 
        """Clear any cached data on this branch, e.g. cached revision history.
349
 
 
350
 
        This means the next call to revision_history will need to call
351
 
        _gen_revision_history.
352
 
 
353
 
        This API is semi-public; it only for use by subclasses, all other code
354
 
        should consider it to be private.
355
 
        """
356
 
        self._revision_history_cache = None
357
 
 
358
 
    def _gen_revision_history(self):
359
 
        """Return sequence of revision hashes on to this branch.
360
 
        
361
 
        Unlike revision_history, this method always regenerates or rereads the
362
 
        revision history, i.e. it does not cache the result, so repeated calls
363
 
        may be expensive.
364
 
 
365
 
        Concrete subclasses should override this instead of revision_history so
366
 
        that subclasses do not need to deal with caching logic.
367
 
        
368
 
        This API is semi-public; it only for use by subclasses, all other code
369
 
        should consider it to be private.
370
 
        """
371
 
        raise NotImplementedError(self._gen_revision_history)
372
 
 
373
 
    @needs_read_lock
374
295
    def revision_history(self):
375
 
        """Return sequence of revision hashes on to this branch.
376
 
        
377
 
        This method will cache the revision history for as long as it is safe to
378
 
        do so.
379
 
        """
380
 
        if self._revision_history_cache is not None:
381
 
            history = self._revision_history_cache
382
 
        else:
383
 
            history = self._gen_revision_history()
384
 
            self._cache_revision_history(history)
385
 
        return list(history)
 
296
        """Return sequence of revision hashes on to this branch."""
 
297
        raise NotImplementedError(self.revision_history)
386
298
 
387
299
    def revno(self):
388
300
        """Return current revision number for this branch.
396
308
        """Older format branches cannot bind or unbind."""
397
309
        raise errors.UpgradeRequired(self.base)
398
310
 
399
 
    def set_append_revisions_only(self, enabled):
400
 
        """Older format branches are never restricted to append-only"""
401
 
        raise errors.UpgradeRequired(self.base)
402
 
 
403
311
    def last_revision(self):
404
312
        """Return last revision id, or None"""
405
313
        ph = self.revision_history()
408
316
        else:
409
317
            return None
410
318
 
411
 
    def last_revision_info(self):
412
 
        """Return information about the last revision.
413
 
 
414
 
        :return: A tuple (revno, last_revision_id).
415
 
        """
416
 
        rh = self.revision_history()
417
 
        revno = len(rh)
418
 
        if revno:
419
 
            return (revno, rh[-1])
420
 
        else:
421
 
            return (0, _mod_revision.NULL_REVISION)
422
 
 
423
319
    def missing_revisions(self, other, stop_revision=None):
424
320
        """Return a list of new revisions that would perfectly fit.
425
321
        
456
352
        """Given a revision id, return its revno"""
457
353
        if revision_id is None:
458
354
            return 0
459
 
        revision_id = osutils.safe_revision_id(revision_id)
460
355
        history = self.revision_history()
461
356
        try:
462
357
            return history.index(revision_id) + 1
474
369
        return history[revno - 1]
475
370
 
476
371
    def pull(self, source, overwrite=False, stop_revision=None):
477
 
        """Mirror source into this branch.
478
 
 
479
 
        This branch is considered to be 'local', having low latency.
480
 
 
481
 
        :returns: PullResult instance
482
 
        """
483
372
        raise NotImplementedError(self.pull)
484
373
 
485
 
    def push(self, target, overwrite=False, stop_revision=None):
486
 
        """Mirror this branch into target.
487
 
 
488
 
        This branch is considered to be 'local', having low latency.
489
 
        """
490
 
        raise NotImplementedError(self.push)
491
 
 
492
374
    def basis_tree(self):
493
375
        """Return `Tree` object for last revision."""
494
376
        return self.repository.revision_tree(self.last_revision())
525
407
        """
526
408
        raise NotImplementedError(self.get_parent)
527
409
 
528
 
    def _set_config_location(self, name, url, config=None,
529
 
                             make_relative=False):
530
 
        if config is None:
531
 
            config = self.get_config()
532
 
        if url is None:
533
 
            url = ''
534
 
        elif make_relative:
535
 
            url = urlutils.relative_url(self.base, url)
536
 
        config.set_user_option(name, url)
537
 
 
538
 
    def _get_config_location(self, name, config=None):
539
 
        if config is None:
540
 
            config = self.get_config()
541
 
        location = config.get_user_option(name)
542
 
        if location == '':
543
 
            location = None
544
 
        return location
545
 
 
546
410
    def get_submit_branch(self):
547
411
        """Return the submit location of the branch.
548
412
 
561
425
        """
562
426
        self.get_config().set_user_option('submit_branch', location)
563
427
 
564
 
    def get_public_branch(self):
565
 
        """Return the public location of the branch.
566
 
 
567
 
        This is is used by merge directives.
568
 
        """
569
 
        return self._get_config_location('public_branch')
570
 
 
571
 
    def set_public_branch(self, location):
572
 
        """Return the submit location of the branch.
573
 
 
574
 
        This is the default location for bundle.  The usual
575
 
        pattern is that the user can override it by specifying a
576
 
        location.
577
 
        """
578
 
        self._set_config_location('public_branch', location)
579
 
 
580
428
    def get_push_location(self):
581
429
        """Return the None or the location to push this branch to."""
582
430
        raise NotImplementedError(self.get_push_location)
613
461
            raise InvalidRevisionNumber(revno)
614
462
 
615
463
    @needs_read_lock
616
 
    def clone(self, to_bzrdir, revision_id=None):
 
464
    def clone(self, *args, **kwargs):
617
465
        """Clone this branch into to_bzrdir preserving all semantic values.
618
466
        
619
467
        revision_id: if not None, the revision history in the new branch will
620
468
                     be truncated to end with revision_id.
621
469
        """
 
470
        # for API compatibility, until 0.8 releases we provide the old api:
 
471
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
 
472
        # after 0.8 releases, the *args and **kwargs should be changed:
 
473
        # def clone(self, to_bzrdir, revision_id=None):
 
474
        if (kwargs.get('to_location', None) or
 
475
            kwargs.get('revision', None) or
 
476
            kwargs.get('basis_branch', None) or
 
477
            (len(args) and isinstance(args[0], basestring))):
 
478
            # backwards compatibility api:
 
479
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
 
480
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
 
481
            # get basis_branch
 
482
            if len(args) > 2:
 
483
                basis_branch = args[2]
 
484
            else:
 
485
                basis_branch = kwargs.get('basis_branch', None)
 
486
            if basis_branch:
 
487
                basis = basis_branch.bzrdir
 
488
            else:
 
489
                basis = None
 
490
            # get revision
 
491
            if len(args) > 1:
 
492
                revision_id = args[1]
 
493
            else:
 
494
                revision_id = kwargs.get('revision', None)
 
495
            # get location
 
496
            if len(args):
 
497
                url = args[0]
 
498
            else:
 
499
                # no default to raise if not provided.
 
500
                url = kwargs.get('to_location')
 
501
            return self.bzrdir.clone(url,
 
502
                                     revision_id=revision_id,
 
503
                                     basis=basis).open_branch()
 
504
        # new cleaner api.
 
505
        # generate args by hand 
 
506
        if len(args) > 1:
 
507
            revision_id = args[1]
 
508
        else:
 
509
            revision_id = kwargs.get('revision_id', None)
 
510
        if len(args):
 
511
            to_bzrdir = args[0]
 
512
        else:
 
513
            # no default to raise if not provided.
 
514
            to_bzrdir = kwargs.get('to_bzrdir')
622
515
        result = self._format.initialize(to_bzrdir)
623
516
        self.copy_content_into(result, revision_id=revision_id)
624
517
        return  result
635
528
        result.set_parent(self.bzrdir.root_transport.base)
636
529
        return result
637
530
 
638
 
    def _synchronize_history(self, destination, revision_id):
639
 
        """Synchronize last revision and revision history between branches.
640
 
 
641
 
        This version is most efficient when the destination is also a
642
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
643
 
        history is the true lefthand parent history, and all of the revisions
644
 
        are in the destination's repository.  If not, set_revision_history
645
 
        will fail.
646
 
 
647
 
        :param destination: The branch to copy the history into
648
 
        :param revision_id: The revision-id to truncate history at.  May
649
 
          be None to copy complete history.
 
531
    @needs_read_lock
 
532
    def copy_content_into(self, destination, revision_id=None):
 
533
        """Copy the content of self into destination.
 
534
 
 
535
        revision_id: if not None, the revision history in the new branch will
 
536
                     be truncated to end with revision_id.
650
537
        """
651
538
        new_history = self.revision_history()
652
539
        if revision_id is not None:
653
 
            revision_id = osutils.safe_revision_id(revision_id)
654
540
            try:
655
541
                new_history = new_history[:new_history.index(revision_id) + 1]
656
542
            except ValueError:
657
543
                rev = self.repository.get_revision(revision_id)
658
544
                new_history = rev.get_history(self.repository)[1:]
659
545
        destination.set_revision_history(new_history)
660
 
 
661
 
    @needs_read_lock
662
 
    def copy_content_into(self, destination, revision_id=None):
663
 
        """Copy the content of self into destination.
664
 
 
665
 
        revision_id: if not None, the revision history in the new branch will
666
 
                     be truncated to end with revision_id.
667
 
        """
668
 
        self._synchronize_history(destination, revision_id)
669
546
        try:
670
547
            parent = self.get_parent()
671
548
        except errors.InaccessibleParent, e:
673
550
        else:
674
551
            if parent:
675
552
                destination.set_parent(parent)
676
 
        self.tags.merge_to(destination.tags)
677
553
 
678
554
    @needs_read_lock
679
555
    def check(self):
710
586
        Weaves are used if this branch's repostory uses weaves.
711
587
        """
712
588
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
713
 
            from bzrlib.repofmt import weaverepo
 
589
            from bzrlib import repository
714
590
            format = bzrdir.BzrDirMetaFormat1()
715
 
            format.repository_format = weaverepo.RepositoryFormat7()
 
591
            format.repository_format = repository.RepositoryFormat7()
716
592
        else:
717
 
            format = self.repository.bzrdir.checkout_metadir()
718
 
            format.set_branch_format(self._format)
 
593
            format = self.repository.bzrdir.cloning_metadir()
719
594
        return format
720
595
 
721
 
    def create_checkout(self, to_location, revision_id=None,
 
596
    def create_checkout(self, to_location, revision_id=None, 
722
597
                        lightweight=False):
723
598
        """Create a checkout of a branch.
724
599
        
734
609
        except errors.FileExists:
735
610
            pass
736
611
        if lightweight:
737
 
            format = self._get_checkout_format()
738
 
            checkout = format.initialize_on_transport(t)
 
612
            checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
739
613
            BranchReferenceFormat().initialize(checkout, self)
740
614
        else:
741
615
            format = self._get_checkout_format()
746
620
            # pull up to the specified revision_id to set the initial 
747
621
            # branch tip correctly, and seed it with history.
748
622
            checkout_branch.pull(self, stop_revision=revision_id)
749
 
        tree = checkout.create_workingtree(revision_id)
750
 
        basis_tree = tree.basis_tree()
751
 
        basis_tree.lock_read()
752
 
        try:
753
 
            for path, file_id in basis_tree.iter_references():
754
 
                reference_parent = self.reference_parent(file_id, path)
755
 
                reference_parent.create_checkout(tree.abspath(path),
756
 
                    basis_tree.get_reference_revision(file_id, path),
757
 
                    lightweight)
758
 
        finally:
759
 
            basis_tree.unlock()
760
 
        return tree
761
 
 
762
 
    def reference_parent(self, file_id, path):
763
 
        """Return the parent branch for a tree-reference file_id
764
 
        :param file_id: The file_id of the tree reference
765
 
        :param path: The path of the file_id in the tree
766
 
        :return: A branch associated with the file_id
767
 
        """
768
 
        # FIXME should provide multiple branches, based on config
769
 
        return Branch.open(self.bzrdir.root_transport.clone(path).base)
770
 
 
771
 
    def supports_tags(self):
772
 
        return self._format.supports_tags()
 
623
        return checkout.create_workingtree(revision_id)
773
624
 
774
625
 
775
626
class BranchFormat(object):
819
670
 
820
671
    def get_format_description(self):
821
672
        """Return the short format description for this format."""
822
 
        raise NotImplementedError(self.get_format_description)
823
 
 
824
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
825
 
                           set_format=True):
826
 
        """Initialize a branch in a bzrdir, with specified files
827
 
 
828
 
        :param a_bzrdir: The bzrdir to initialize the branch in
829
 
        :param utf8_files: The files to create as a list of
830
 
            (filename, content) tuples
831
 
        :param set_format: If True, set the format with
832
 
            self.get_format_string.  (BzrBranch4 has its format set
833
 
            elsewhere)
834
 
        :return: a branch in this format
835
 
        """
836
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
837
 
        branch_transport = a_bzrdir.get_branch_transport(self)
838
 
        lock_map = {
839
 
            'metadir': ('lock', lockdir.LockDir),
840
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
841
 
        }
842
 
        lock_name, lock_class = lock_map[lock_type]
843
 
        control_files = lockable_files.LockableFiles(branch_transport,
844
 
            lock_name, lock_class)
845
 
        control_files.create_lock()
846
 
        control_files.lock_write()
847
 
        if set_format:
848
 
            control_files.put_utf8('format', self.get_format_string())
849
 
        try:
850
 
            for file, content in utf8_files:
851
 
                control_files.put_utf8(file, content)
852
 
        finally:
853
 
            control_files.unlock()
854
 
        return self.open(a_bzrdir, _found=True)
 
673
        raise NotImplementedError(self.get_format_string)
855
674
 
856
675
    def initialize(self, a_bzrdir):
857
676
        """Create a branch of this format in a_bzrdir."""
890
709
    def __str__(self):
891
710
        return self.get_format_string().rstrip()
892
711
 
893
 
    def supports_tags(self):
894
 
        """True if this format supports tags stored in the branch"""
895
 
        return False  # by default
896
 
 
897
 
    # XXX: Probably doesn't really belong here -- mbp 20070212
898
 
    def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
899
 
            lock_class):
900
 
        branch_transport = a_bzrdir.get_branch_transport(self)
901
 
        control_files = lockable_files.LockableFiles(branch_transport,
902
 
            lock_filename, lock_class)
903
 
        control_files.create_lock()
904
 
        control_files.lock_write()
905
 
        try:
906
 
            for filename, content in utf8_files:
907
 
                control_files.put_utf8(filename, content)
908
 
        finally:
909
 
            control_files.unlock()
910
 
 
911
 
 
912
 
class BranchHooks(Hooks):
913
 
    """A dictionary mapping hook name to a list of callables for branch hooks.
914
 
    
915
 
    e.g. ['set_rh'] Is the list of items to be called when the
916
 
    set_revision_history function is invoked.
917
 
    """
918
 
 
919
 
    def __init__(self):
920
 
        """Create the default hooks.
921
 
 
922
 
        These are all empty initially, because by default nothing should get
923
 
        notified.
924
 
        """
925
 
        Hooks.__init__(self)
926
 
        # Introduced in 0.15:
927
 
        # invoked whenever the revision history has been set
928
 
        # with set_revision_history. The api signature is
929
 
        # (branch, revision_history), and the branch will
930
 
        # be write-locked.
931
 
        self['set_rh'] = []
932
 
        # invoked after a push operation completes.
933
 
        # the api signature is
934
 
        # (push_result)
935
 
        # containing the members
936
 
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
937
 
        # where local is the local branch or None, master is the target 
938
 
        # master branch, and the rest should be self explanatory. The source
939
 
        # is read locked and the target branches write locked. Source will
940
 
        # be the local low-latency branch.
941
 
        self['post_push'] = []
942
 
        # invoked after a pull operation completes.
943
 
        # the api signature is
944
 
        # (pull_result)
945
 
        # containing the members
946
 
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
947
 
        # where local is the local branch or None, master is the target 
948
 
        # master branch, and the rest should be self explanatory. The source
949
 
        # is read locked and the target branches write locked. The local
950
 
        # branch is the low-latency branch.
951
 
        self['post_pull'] = []
952
 
        # invoked after a commit operation completes.
953
 
        # the api signature is 
954
 
        # (local, master, old_revno, old_revid, new_revno, new_revid)
955
 
        # old_revid is NULL_REVISION for the first commit to a branch.
956
 
        self['post_commit'] = []
957
 
        # invoked after a uncommit operation completes.
958
 
        # the api signature is
959
 
        # (local, master, old_revno, old_revid, new_revno, new_revid) where
960
 
        # local is the local branch or None, master is the target branch,
961
 
        # and an empty branch recieves new_revno of 0, new_revid of None.
962
 
        self['post_uncommit'] = []
963
 
 
964
 
 
965
 
# install the default hooks into the Branch class.
966
 
Branch.hooks = BranchHooks()
967
 
 
968
712
 
969
713
class BzrBranchFormat4(BranchFormat):
970
714
    """Bzr branch format 4.
980
724
 
981
725
    def initialize(self, a_bzrdir):
982
726
        """Create a branch of this format in a_bzrdir."""
 
727
        mutter('creating branch in %s', a_bzrdir.transport.base)
 
728
        branch_transport = a_bzrdir.get_branch_transport(self)
983
729
        utf8_files = [('revision-history', ''),
984
730
                      ('branch-name', ''),
985
731
                      ]
986
 
        return self._initialize_helper(a_bzrdir, utf8_files,
987
 
                                       lock_type='branch4', set_format=False)
 
732
        control_files = LockableFiles(branch_transport, 'branch-lock',
 
733
                                      TransportLock)
 
734
        control_files.create_lock()
 
735
        control_files.lock_write()
 
736
        try:
 
737
            for file, content in utf8_files:
 
738
                control_files.put_utf8(file, content)
 
739
        finally:
 
740
            control_files.unlock()
 
741
        return self.open(a_bzrdir, _found=True)
988
742
 
989
743
    def __init__(self):
990
744
        super(BzrBranchFormat4, self).__init__()
1031
785
        
1032
786
    def initialize(self, a_bzrdir):
1033
787
        """Create a branch of this format in a_bzrdir."""
 
788
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
789
        branch_transport = a_bzrdir.get_branch_transport(self)
1034
790
        utf8_files = [('revision-history', ''),
1035
791
                      ('branch-name', ''),
1036
792
                      ]
1037
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
793
        control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
 
794
        control_files.create_lock()
 
795
        control_files.lock_write()
 
796
        control_files.put_utf8('format', self.get_format_string())
 
797
        try:
 
798
            for file, content in utf8_files:
 
799
                control_files.put_utf8(file, content)
 
800
        finally:
 
801
            control_files.unlock()
 
802
        return self.open(a_bzrdir, _found=True, )
1038
803
 
1039
804
    def __init__(self):
1040
805
        super(BzrBranchFormat5, self).__init__()
1050
815
            format = BranchFormat.find_format(a_bzrdir)
1051
816
            assert format.__class__ == self.__class__
1052
817
        transport = a_bzrdir.get_branch_transport(None)
1053
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1054
 
                                                     lockdir.LockDir)
 
818
        control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
1055
819
        return BzrBranch5(_format=self,
1056
820
                          _control_files=control_files,
1057
821
                          a_bzrdir=a_bzrdir,
1058
822
                          _repository=a_bzrdir.find_repository())
1059
823
 
1060
 
 
1061
 
class BzrBranchFormat6(BzrBranchFormat5):
1062
 
    """Branch format with last-revision
1063
 
 
1064
 
    Unlike previous formats, this has no explicit revision history. Instead,
1065
 
    this just stores the last-revision, and the left-hand history leading
1066
 
    up to there is the history.
1067
 
 
1068
 
    This format was introduced in bzr 0.15
1069
 
    """
1070
 
 
1071
 
    def get_format_string(self):
1072
 
        """See BranchFormat.get_format_string()."""
1073
 
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
1074
 
 
1075
 
    def get_format_description(self):
1076
 
        """See BranchFormat.get_format_description()."""
1077
 
        return "Branch format 6"
1078
 
 
1079
 
    def initialize(self, a_bzrdir):
1080
 
        """Create a branch of this format in a_bzrdir."""
1081
 
        utf8_files = [('last-revision', '0 null:\n'),
1082
 
                      ('branch-name', ''),
1083
 
                      ('branch.conf', ''),
1084
 
                      ('tags', ''),
1085
 
                      ]
1086
 
        return self._initialize_helper(a_bzrdir, utf8_files)
1087
 
 
1088
 
    def open(self, a_bzrdir, _found=False):
1089
 
        """Return the branch object for a_bzrdir
1090
 
 
1091
 
        _found is a private parameter, do not use it. It is used to indicate
1092
 
               if format probing has already be done.
1093
 
        """
1094
 
        if not _found:
1095
 
            format = BranchFormat.find_format(a_bzrdir)
1096
 
            assert format.__class__ == self.__class__
1097
 
        transport = a_bzrdir.get_branch_transport(None)
1098
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1099
 
                                                     lockdir.LockDir)
1100
 
        return BzrBranch6(_format=self,
1101
 
                          _control_files=control_files,
1102
 
                          a_bzrdir=a_bzrdir,
1103
 
                          _repository=a_bzrdir.find_repository())
1104
 
 
1105
 
    def supports_tags(self):
1106
 
        return True
 
824
    def __str__(self):
 
825
        return "Bazaar-NG Metadir branch format 5"
1107
826
 
1108
827
 
1109
828
class BranchReferenceFormat(BranchFormat):
1181
900
__default_format = BzrBranchFormat5()
1182
901
BranchFormat.register_format(__default_format)
1183
902
BranchFormat.register_format(BranchReferenceFormat())
1184
 
BranchFormat.register_format(BzrBranchFormat6())
1185
903
BranchFormat.set_default_format(__default_format)
1186
904
_legacy_formats = [BzrBranchFormat4(),
1187
905
                   ]
1194
912
    it's writable, and can be accessed via the normal filesystem API.
1195
913
    """
1196
914
    
1197
 
    def __init__(self, _format=None,
 
915
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
 
916
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
1198
917
                 _control_files=None, a_bzrdir=None, _repository=None):
1199
 
        """Create new branch object at a particular location."""
1200
 
        Branch.__init__(self)
 
918
        """Create new branch object at a particular location.
 
919
 
 
920
        transport -- A Transport object, defining how to access files.
 
921
        
 
922
        init -- If True, create new control files in a previously
 
923
             unversioned directory.  If False, the branch must already
 
924
             be versioned.
 
925
 
 
926
        relax_version_check -- If true, the usual check for the branch
 
927
            version is not applied.  This is intended only for
 
928
            upgrade/recovery type use; it's not guaranteed that
 
929
            all operations will work on old format branches.
 
930
        """
1201
931
        if a_bzrdir is None:
1202
 
            raise ValueError('a_bzrdir must be supplied')
 
932
            self.bzrdir = bzrdir.BzrDir.open(transport.base)
1203
933
        else:
1204
934
            self.bzrdir = a_bzrdir
1205
 
        # self._transport used to point to the directory containing the
1206
 
        # control directory, but was not used - now it's just the transport
1207
 
        # for the branch control files.  mbp 20070212
1208
 
        self._base = self.bzrdir.transport.clone('..').base
 
935
        self._transport = self.bzrdir.transport.clone('..')
 
936
        self._base = self._transport.base
1209
937
        self._format = _format
1210
938
        if _control_files is None:
1211
939
            raise ValueError('BzrBranch _control_files is None')
1212
940
        self.control_files = _control_files
1213
 
        self._transport = _control_files._transport
 
941
        if deprecated_passed(init):
 
942
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
943
                 "deprecated as of bzr 0.8. Please use Branch.create().",
 
944
                 DeprecationWarning,
 
945
                 stacklevel=2)
 
946
            if init:
 
947
                # this is slower than before deprecation, oh well never mind.
 
948
                # -> its deprecated.
 
949
                self._initialize(transport.base)
 
950
        self._check_format(_format)
 
951
        if deprecated_passed(relax_version_check):
 
952
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
953
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
954
                 "Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
 
955
                 "open() method.",
 
956
                 DeprecationWarning,
 
957
                 stacklevel=2)
 
958
            if (not relax_version_check
 
959
                and not self._format.is_supported()):
 
960
                raise errors.UnsupportedFormatError(format=fmt)
 
961
        if deprecated_passed(transport):
 
962
            warn("BzrBranch.__init__(transport=XXX...): The transport "
 
963
                 "parameter is deprecated as of bzr 0.8. "
 
964
                 "Please use Branch.open, or bzrdir.open_branch().",
 
965
                 DeprecationWarning,
 
966
                 stacklevel=2)
1214
967
        self.repository = _repository
1215
968
 
1216
969
    def __str__(self):
1219
972
    __repr__ = __str__
1220
973
 
1221
974
    def _get_base(self):
1222
 
        """Returns the directory containing the control directory."""
1223
975
        return self._base
1224
976
 
1225
977
    base = property(_get_base, doc="The URL for the root of this branch.")
1226
978
 
 
979
    def _finish_transaction(self):
 
980
        """Exit the current transaction."""
 
981
        return self.control_files._finish_transaction()
 
982
 
 
983
    def get_transaction(self):
 
984
        """Return the current active transaction.
 
985
 
 
986
        If no transaction is active, this returns a passthrough object
 
987
        for which all data is immediately flushed and no caching happens.
 
988
        """
 
989
        # this is an explicit function so that we can do tricky stuff
 
990
        # when the storage in rev_storage is elsewhere.
 
991
        # we probably need to hook the two 'lock a location' and 
 
992
        # 'have a transaction' together more delicately, so that
 
993
        # we can have two locks (branch and storage) and one transaction
 
994
        # ... and finishing the transaction unlocks both, but unlocking
 
995
        # does not. - RBC 20051121
 
996
        return self.control_files.get_transaction()
 
997
 
 
998
    def _set_transaction(self, transaction):
 
999
        """Set a new active transaction."""
 
1000
        return self.control_files._set_transaction(transaction)
 
1001
 
1227
1002
    def abspath(self, name):
1228
1003
        """See Branch.abspath."""
1229
1004
        return self.control_files._transport.abspath(name)
1230
1005
 
1231
 
 
1232
 
    @deprecated_method(zero_sixteen)
 
1006
    def _check_format(self, format):
 
1007
        """Identify the branch format if needed.
 
1008
 
 
1009
        The format is stored as a reference to the format object in
 
1010
        self._format for code that needs to check it later.
 
1011
 
 
1012
        The format parameter is either None or the branch format class
 
1013
        used to open this branch.
 
1014
 
 
1015
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
 
1016
        """
 
1017
        if format is None:
 
1018
            format = BranchFormat.find_format(self.bzrdir)
 
1019
        self._format = format
 
1020
        mutter("got branch format %s", self._format)
 
1021
 
1233
1022
    @needs_read_lock
1234
1023
    def get_root_id(self):
1235
1024
        """See Branch.get_root_id."""
1239
1028
    def is_locked(self):
1240
1029
        return self.control_files.is_locked()
1241
1030
 
1242
 
    def lock_write(self, token=None):
1243
 
        repo_token = self.repository.lock_write()
 
1031
    def lock_write(self):
 
1032
        self.repository.lock_write()
1244
1033
        try:
1245
 
            token = self.control_files.lock_write(token=token)
 
1034
            self.control_files.lock_write()
1246
1035
        except:
1247
1036
            self.repository.unlock()
1248
1037
            raise
1249
 
        return token
1250
1038
 
1251
1039
    def lock_read(self):
1252
1040
        self.repository.lock_read()
1262
1050
            self.control_files.unlock()
1263
1051
        finally:
1264
1052
            self.repository.unlock()
1265
 
        if not self.control_files.is_locked():
1266
 
            # we just released the lock
1267
 
            self._clear_cached_state()
1268
1053
        
1269
1054
    def peek_lock_mode(self):
1270
1055
        if self.control_files._lock_count == 0:
1283
1068
    @needs_write_lock
1284
1069
    def append_revision(self, *revision_ids):
1285
1070
        """See Branch.append_revision."""
1286
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1287
1071
        for revision_id in revision_ids:
1288
 
            _mod_revision.check_not_reserved_id(revision_id)
1289
1072
            mutter("add {%s} to revision-history" % revision_id)
1290
1073
        rev_history = self.revision_history()
1291
1074
        rev_history.extend(revision_ids)
1292
1075
        self.set_revision_history(rev_history)
1293
1076
 
1294
 
    def _write_revision_history(self, history):
1295
 
        """Factored out of set_revision_history.
1296
 
 
1297
 
        This performs the actual writing to disk.
1298
 
        It is intended to be called by BzrBranch5.set_revision_history."""
1299
 
        self.control_files.put_bytes(
1300
 
            'revision-history', '\n'.join(history))
1301
 
 
1302
1077
    @needs_write_lock
1303
1078
    def set_revision_history(self, rev_history):
1304
1079
        """See Branch.set_revision_history."""
1305
 
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1306
 
        self._write_revision_history(rev_history)
1307
 
        self._cache_revision_history(rev_history)
1308
 
        for hook in Branch.hooks['set_rh']:
1309
 
            hook(self, rev_history)
 
1080
        self.control_files.put_utf8(
 
1081
            'revision-history', '\n'.join(rev_history))
 
1082
        transaction = self.get_transaction()
 
1083
        history = transaction.map.find_revision_history()
 
1084
        if history is not None:
 
1085
            # update the revision history in the identity map.
 
1086
            history[:] = list(rev_history)
 
1087
            # this call is disabled because revision_history is 
 
1088
            # not really an object yet, and the transaction is for objects.
 
1089
            # transaction.register_dirty(history)
 
1090
        else:
 
1091
            transaction.map.add_revision_history(rev_history)
 
1092
            # this call is disabled because revision_history is 
 
1093
            # not really an object yet, and the transaction is for objects.
 
1094
            # transaction.register_clean(history)
 
1095
 
 
1096
    @needs_read_lock
 
1097
    def revision_history(self):
 
1098
        """See Branch.revision_history."""
 
1099
        transaction = self.get_transaction()
 
1100
        history = transaction.map.find_revision_history()
 
1101
        if history is not None:
 
1102
            # mutter("cache hit for revision-history in %s", self)
 
1103
            return list(history)
 
1104
        decode_utf8 = cache_utf8.decode
 
1105
        history = [decode_utf8(l.rstrip('\r\n')) for l in
 
1106
                self.control_files.get('revision-history').readlines()]
 
1107
        transaction.map.add_revision_history(history)
 
1108
        # this call is disabled because revision_history is 
 
1109
        # not really an object yet, and the transaction is for objects.
 
1110
        # transaction.register_clean(history, precious=True)
 
1111
        return list(history)
1310
1112
 
1311
1113
    @needs_write_lock
1312
 
    def set_last_revision_info(self, revno, revision_id):
1313
 
        revision_id = osutils.safe_revision_id(revision_id)
1314
 
        history = self._lefthand_history(revision_id)
1315
 
        assert len(history) == revno, '%d != %d' % (len(history), revno)
1316
 
        self.set_revision_history(history)
1317
 
 
1318
 
    def _gen_revision_history(self):
1319
 
        history = self.control_files.get('revision-history').read().split('\n')
1320
 
        if history[-1:] == ['']:
1321
 
            # There shouldn't be a trailing newline, but just in case.
1322
 
            history.pop()
1323
 
        return history
1324
 
 
1325
 
    def _lefthand_history(self, revision_id, last_rev=None,
1326
 
                          other_branch=None):
 
1114
    def generate_revision_history(self, revision_id, last_rev=None, 
 
1115
        other_branch=None):
 
1116
        """Create a new revision history that will finish with revision_id.
 
1117
        
 
1118
        :param revision_id: the new tip to use.
 
1119
        :param last_rev: The previous last_revision. If not None, then this
 
1120
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
1121
        :param other_branch: The other branch that DivergedBranches should
 
1122
            raise with respect to.
 
1123
        """
1327
1124
        # stop_revision must be a descendant of last_revision
1328
1125
        stop_graph = self.repository.get_revision_graph(revision_id)
1329
1126
        if last_rev is not None and last_rev not in stop_graph:
1332
1129
        # make a new revision history from the graph
1333
1130
        current_rev_id = revision_id
1334
1131
        new_history = []
1335
 
        while current_rev_id not in (None, _mod_revision.NULL_REVISION):
 
1132
        while current_rev_id not in (None, revision.NULL_REVISION):
1336
1133
            new_history.append(current_rev_id)
1337
1134
            current_rev_id_parents = stop_graph[current_rev_id]
1338
1135
            try:
1340
1137
            except IndexError:
1341
1138
                current_rev_id = None
1342
1139
        new_history.reverse()
1343
 
        return new_history
1344
 
 
1345
 
    @needs_write_lock
1346
 
    def generate_revision_history(self, revision_id, last_rev=None,
1347
 
        other_branch=None):
1348
 
        """Create a new revision history that will finish with revision_id.
1349
 
 
1350
 
        :param revision_id: the new tip to use.
1351
 
        :param last_rev: The previous last_revision. If not None, then this
1352
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
1353
 
        :param other_branch: The other branch that DivergedBranches should
1354
 
            raise with respect to.
1355
 
        """
1356
 
        revision_id = osutils.safe_revision_id(revision_id)
1357
 
        self.set_revision_history(self._lefthand_history(revision_id,
1358
 
            last_rev, other_branch))
 
1140
        self.set_revision_history(new_history)
1359
1141
 
1360
1142
    @needs_write_lock
1361
1143
    def update_revisions(self, other, stop_revision=None):
1367
1149
                if stop_revision is None:
1368
1150
                    # if there are no commits, we're done.
1369
1151
                    return
1370
 
            else:
1371
 
                stop_revision = osutils.safe_revision_id(stop_revision)
1372
1152
            # whats the current last revision, before we fetch [and change it
1373
1153
            # possibly]
1374
1154
            last_rev = self.last_revision()
1399
1179
        return self.bzrdir.open_workingtree()
1400
1180
 
1401
1181
    @needs_write_lock
1402
 
    def pull(self, source, overwrite=False, stop_revision=None,
1403
 
        _hook_master=None, _run_hooks=True):
1404
 
        """See Branch.pull.
1405
 
 
1406
 
        :param _hook_master: Private parameter - set the branch to 
1407
 
            be supplied as the master to push hooks.
1408
 
        :param _run_hooks: Private parameter - allow disabling of
1409
 
            hooks, used when pushing to a master branch.
1410
 
        """
1411
 
        result = PullResult()
1412
 
        result.source_branch = source
1413
 
        result.target_branch = self
 
1182
    def pull(self, source, overwrite=False, stop_revision=None):
 
1183
        """See Branch.pull."""
1414
1184
        source.lock_read()
1415
1185
        try:
1416
 
            result.old_revno, result.old_revid = self.last_revision_info()
 
1186
            old_count = len(self.revision_history())
1417
1187
            try:
1418
1188
                self.update_revisions(source, stop_revision)
1419
1189
            except DivergedBranches:
1420
1190
                if not overwrite:
1421
1191
                    raise
1422
1192
            if overwrite:
1423
 
                if stop_revision is None:
1424
 
                    stop_revision = source.last_revision()
1425
 
                self.generate_revision_history(stop_revision)
1426
 
            result.tag_conflicts = source.tags.merge_to(self.tags)
1427
 
            result.new_revno, result.new_revid = self.last_revision_info()
1428
 
            if _hook_master:
1429
 
                result.master_branch = _hook_master
1430
 
                result.local_branch = self
1431
 
            else:
1432
 
                result.master_branch = self
1433
 
                result.local_branch = None
1434
 
            if _run_hooks:
1435
 
                for hook in Branch.hooks['post_pull']:
1436
 
                    hook(result)
 
1193
                self.set_revision_history(source.revision_history())
 
1194
            new_count = len(self.revision_history())
 
1195
            return new_count - old_count
1437
1196
        finally:
1438
1197
            source.unlock()
1439
 
        return result
1440
 
 
1441
 
    def _get_parent_location(self):
 
1198
 
 
1199
    def get_parent(self):
 
1200
        """See Branch.get_parent."""
 
1201
 
1442
1202
        _locs = ['parent', 'pull', 'x-pull']
 
1203
        assert self.base[-1] == '/'
1443
1204
        for l in _locs:
1444
1205
            try:
1445
 
                return self.control_files.get(l).read().strip('\n')
 
1206
                parent = self.control_files.get(l).read().strip('\n')
1446
1207
            except NoSuchFile:
1447
 
                pass
 
1208
                continue
 
1209
            # This is an old-format absolute path to a local branch
 
1210
            # turn it into a url
 
1211
            if parent.startswith('/'):
 
1212
                parent = urlutils.local_path_to_url(parent.decode('utf8'))
 
1213
            try:
 
1214
                return urlutils.join(self.base[:-1], parent)
 
1215
            except errors.InvalidURLJoin, e:
 
1216
                raise errors.InaccessibleParent(parent, self.base)
1448
1217
        return None
1449
1218
 
1450
 
    @needs_read_lock
1451
 
    def push(self, target, overwrite=False, stop_revision=None,
1452
 
        _hook_master=None, _run_hooks=True):
1453
 
        """See Branch.push.
1454
 
        
1455
 
        :param _hook_master: Private parameter - set the branch to 
1456
 
            be supplied as the master to push hooks.
1457
 
        :param _run_hooks: Private parameter - allow disabling of
1458
 
            hooks, used when pushing to a master branch.
1459
 
        """
1460
 
        result = PushResult()
1461
 
        result.source_branch = self
1462
 
        result.target_branch = target
1463
 
        target.lock_write()
1464
 
        try:
1465
 
            result.old_revno, result.old_revid = target.last_revision_info()
1466
 
            try:
1467
 
                target.update_revisions(self, stop_revision)
1468
 
            except DivergedBranches:
1469
 
                if not overwrite:
1470
 
                    raise
1471
 
            if overwrite:
1472
 
                target.set_revision_history(self.revision_history())
1473
 
            result.tag_conflicts = self.tags.merge_to(target.tags)
1474
 
            result.new_revno, result.new_revid = target.last_revision_info()
1475
 
            if _hook_master:
1476
 
                result.master_branch = _hook_master
1477
 
                result.local_branch = target
1478
 
            else:
1479
 
                result.master_branch = target
1480
 
                result.local_branch = None
1481
 
            if _run_hooks:
1482
 
                for hook in Branch.hooks['post_push']:
1483
 
                    hook(result)
1484
 
        finally:
1485
 
            target.unlock()
1486
 
        return result
1487
 
 
1488
 
    def get_parent(self):
1489
 
        """See Branch.get_parent."""
1490
 
 
1491
 
        assert self.base[-1] == '/'
1492
 
        parent = self._get_parent_location()
1493
 
        if parent is None:
1494
 
            return parent
1495
 
        # This is an old-format absolute path to a local branch
1496
 
        # turn it into a url
1497
 
        if parent.startswith('/'):
1498
 
            parent = urlutils.local_path_to_url(parent.decode('utf8'))
1499
 
        try:
1500
 
            return urlutils.join(self.base[:-1], parent)
1501
 
        except errors.InvalidURLJoin, e:
1502
 
            raise errors.InaccessibleParent(parent, self.base)
1503
 
 
1504
1219
    def get_push_location(self):
1505
1220
        """See Branch.get_push_location."""
1506
1221
        push_loc = self.get_config().get_user_option('push_location')
1508
1223
 
1509
1224
    def set_push_location(self, location):
1510
1225
        """See Branch.set_push_location."""
1511
 
        self.get_config().set_user_option(
1512
 
            'push_location', location,
1513
 
            store=_mod_config.STORE_LOCATION_NORECURSE)
 
1226
        self.get_config().set_user_option('push_location', location, 
 
1227
                                          local=True)
1514
1228
 
1515
1229
    @needs_write_lock
1516
1230
    def set_parent(self, url):
1520
1234
        # FIXUP this and get_parent in a future branch format bump:
1521
1235
        # read and rewrite the file, and have the new format code read
1522
1236
        # using .get not .get_utf8. RBC 20060125
1523
 
        if url is not None:
 
1237
        if url is None:
 
1238
            self.control_files._transport.delete('parent')
 
1239
        else:
1524
1240
            if isinstance(url, unicode):
1525
1241
                try: 
1526
1242
                    url = url.encode('ascii')
1528
1244
                    raise bzrlib.errors.InvalidURL(url,
1529
1245
                        "Urls must be 7-bit ascii, "
1530
1246
                        "use bzrlib.urlutils.escape")
 
1247
                    
1531
1248
            url = urlutils.relative_url(self.base, url)
1532
 
        self._set_parent_location(url)
1533
 
 
1534
 
    def _set_parent_location(self, url):
1535
 
        if url is None:
1536
 
            self.control_files._transport.delete('parent')
1537
 
        else:
1538
 
            assert isinstance(url, str)
1539
 
            self.control_files.put_bytes('parent', url + '\n')
 
1249
            self.control_files.put('parent', StringIO(url + '\n'))
1540
1250
 
1541
1251
    @deprecated_function(zero_nine)
1542
1252
    def tree_config(self):
1562
1272
                                         _repository=_repository)
1563
1273
        
1564
1274
    @needs_write_lock
1565
 
    def pull(self, source, overwrite=False, stop_revision=None,
1566
 
        _run_hooks=True):
1567
 
        """Extends branch.pull to be bound branch aware.
1568
 
        
1569
 
        :param _run_hooks: Private parameter used to force hook running
1570
 
            off during bound branch double-pushing.
1571
 
        """
 
1275
    def pull(self, source, overwrite=False, stop_revision=None):
 
1276
        """Updates branch.pull to be bound branch aware."""
1572
1277
        bound_location = self.get_bound_location()
1573
 
        master_branch = None
1574
 
        if bound_location and source.base != bound_location:
 
1278
        if source.base != bound_location:
1575
1279
            # not pulling from master, so we need to update master.
1576
1280
            master_branch = self.get_master_branch()
1577
 
            master_branch.lock_write()
1578
 
        try:
1579
 
            if master_branch:
1580
 
                # pull from source into master.
1581
 
                master_branch.pull(source, overwrite, stop_revision,
1582
 
                    _run_hooks=False)
1583
 
            return super(BzrBranch5, self).pull(source, overwrite,
1584
 
                stop_revision, _hook_master=master_branch,
1585
 
                _run_hooks=_run_hooks)
1586
 
        finally:
1587
 
            if master_branch:
1588
 
                master_branch.unlock()
1589
 
 
1590
 
    @needs_read_lock
1591
 
    def push(self, target, overwrite=False, stop_revision=None):
1592
 
        """Updates branch.push to be bound branch aware."""
1593
 
        bound_location = target.get_bound_location()
1594
 
        master_branch = None
1595
 
        if bound_location and target.base != bound_location:
1596
 
            # not pushing to master, so we need to update master.
1597
 
            master_branch = target.get_master_branch()
1598
 
            master_branch.lock_write()
1599
 
        try:
1600
 
            if master_branch:
1601
 
                # push into the master from this branch.
1602
 
                super(BzrBranch5, self).push(master_branch, overwrite,
1603
 
                    stop_revision, _run_hooks=False)
1604
 
            # and push into the target branch from this. Note that we push from
1605
 
            # this branch again, because its considered the highest bandwidth
1606
 
            # repository.
1607
 
            return super(BzrBranch5, self).push(target, overwrite,
1608
 
                stop_revision, _hook_master=master_branch)
1609
 
        finally:
1610
 
            if master_branch:
1611
 
                master_branch.unlock()
 
1281
            if master_branch:
 
1282
                master_branch.pull(source)
 
1283
                source = master_branch
 
1284
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
1612
1285
 
1613
1286
    def get_bound_location(self):
1614
1287
        try:
1715
1388
        return None
1716
1389
 
1717
1390
 
1718
 
class BzrBranchExperimental(BzrBranch5):
1719
 
    """Bzr experimental branch format
1720
 
 
1721
 
    This format has:
1722
 
     - a revision-history file.
1723
 
     - a format string
1724
 
     - a lock dir guarding the branch itself
1725
 
     - all of this stored in a branch/ subdirectory
1726
 
     - works with shared repositories.
1727
 
     - a tag dictionary in the branch
1728
 
 
1729
 
    This format is new in bzr 0.15, but shouldn't be used for real data, 
1730
 
    only for testing.
1731
 
 
1732
 
    This class acts as it's own BranchFormat.
1733
 
    """
1734
 
 
1735
 
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1736
 
 
1737
 
    @classmethod
1738
 
    def get_format_string(cls):
1739
 
        """See BranchFormat.get_format_string()."""
1740
 
        return "Bazaar-NG branch format experimental\n"
1741
 
 
1742
 
    @classmethod
1743
 
    def get_format_description(cls):
1744
 
        """See BranchFormat.get_format_description()."""
1745
 
        return "Experimental branch format"
1746
 
 
1747
 
    @classmethod
1748
 
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1749
 
            lock_class):
1750
 
        branch_transport = a_bzrdir.get_branch_transport(cls)
1751
 
        control_files = lockable_files.LockableFiles(branch_transport,
1752
 
            lock_filename, lock_class)
1753
 
        control_files.create_lock()
1754
 
        control_files.lock_write()
1755
 
        try:
1756
 
            for filename, content in utf8_files:
1757
 
                control_files.put_utf8(filename, content)
1758
 
        finally:
1759
 
            control_files.unlock()
1760
 
        
1761
 
    @classmethod
1762
 
    def initialize(cls, a_bzrdir):
1763
 
        """Create a branch of this format in a_bzrdir."""
1764
 
        utf8_files = [('format', cls.get_format_string()),
1765
 
                      ('revision-history', ''),
1766
 
                      ('branch-name', ''),
1767
 
                      ('tags', ''),
1768
 
                      ]
1769
 
        cls._initialize_control_files(a_bzrdir, utf8_files,
1770
 
            'lock', lockdir.LockDir)
1771
 
        return cls.open(a_bzrdir, _found=True)
1772
 
 
1773
 
    @classmethod
1774
 
    def open(cls, a_bzrdir, _found=False):
1775
 
        """Return the branch object for a_bzrdir
1776
 
 
1777
 
        _found is a private parameter, do not use it. It is used to indicate
1778
 
               if format probing has already be done.
1779
 
        """
1780
 
        if not _found:
1781
 
            format = BranchFormat.find_format(a_bzrdir)
1782
 
            assert format.__class__ == cls
1783
 
        transport = a_bzrdir.get_branch_transport(None)
1784
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1785
 
                                                     lockdir.LockDir)
1786
 
        return cls(_format=cls,
1787
 
            _control_files=control_files,
1788
 
            a_bzrdir=a_bzrdir,
1789
 
            _repository=a_bzrdir.find_repository())
1790
 
 
1791
 
    @classmethod
1792
 
    def is_supported(cls):
1793
 
        return True
1794
 
 
1795
 
    def _make_tags(self):
1796
 
        return BasicTags(self)
1797
 
 
1798
 
    @classmethod
1799
 
    def supports_tags(cls):
1800
 
        return True
1801
 
 
1802
 
 
1803
 
BranchFormat.register_format(BzrBranchExperimental)
1804
 
 
1805
 
 
1806
 
class BzrBranch6(BzrBranch5):
1807
 
 
1808
 
    @needs_read_lock
1809
 
    def last_revision_info(self):
1810
 
        revision_string = self.control_files.get('last-revision').read()
1811
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1812
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
1813
 
        revno = int(revno)
1814
 
        return revno, revision_id
1815
 
 
1816
 
    def last_revision(self):
1817
 
        """Return last revision id, or None"""
1818
 
        revision_id = self.last_revision_info()[1]
1819
 
        if revision_id == _mod_revision.NULL_REVISION:
1820
 
            revision_id = None
1821
 
        return revision_id
1822
 
 
1823
 
    def _write_last_revision_info(self, revno, revision_id):
1824
 
        """Simply write out the revision id, with no checks.
1825
 
 
1826
 
        Use set_last_revision_info to perform this safely.
1827
 
 
1828
 
        Does not update the revision_history cache.
1829
 
        Intended to be called by set_last_revision_info and
1830
 
        _write_revision_history.
1831
 
        """
1832
 
        if revision_id is None:
1833
 
            revision_id = 'null:'
1834
 
        out_string = '%d %s\n' % (revno, revision_id)
1835
 
        self.control_files.put_bytes('last-revision', out_string)
1836
 
 
1837
 
    @needs_write_lock
1838
 
    def set_last_revision_info(self, revno, revision_id):
1839
 
        revision_id = osutils.safe_revision_id(revision_id)
1840
 
        if self._get_append_revisions_only():
1841
 
            self._check_history_violation(revision_id)
1842
 
        self._write_last_revision_info(revno, revision_id)
1843
 
        self._clear_cached_state()
1844
 
 
1845
 
    def _check_history_violation(self, revision_id):
1846
 
        last_revision = self.last_revision()
1847
 
        if last_revision is None:
1848
 
            return
1849
 
        if last_revision not in self._lefthand_history(revision_id):
1850
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
1851
 
 
1852
 
    def _gen_revision_history(self):
1853
 
        """Generate the revision history from last revision
1854
 
        """
1855
 
        history = list(self.repository.iter_reverse_revision_history(
1856
 
            self.last_revision()))
1857
 
        history.reverse()
1858
 
        return history
1859
 
 
1860
 
    def _write_revision_history(self, history):
1861
 
        """Factored out of set_revision_history.
1862
 
 
1863
 
        This performs the actual writing to disk, with format-specific checks.
1864
 
        It is intended to be called by BzrBranch5.set_revision_history.
1865
 
        """
1866
 
        if len(history) == 0:
1867
 
            last_revision = 'null:'
1868
 
        else:
1869
 
            if history != self._lefthand_history(history[-1]):
1870
 
                raise errors.NotLefthandHistory(history)
1871
 
            last_revision = history[-1]
1872
 
        if self._get_append_revisions_only():
1873
 
            self._check_history_violation(last_revision)
1874
 
        self._write_last_revision_info(len(history), last_revision)
1875
 
 
1876
 
    @needs_write_lock
1877
 
    def append_revision(self, *revision_ids):
1878
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1879
 
        if len(revision_ids) == 0:
1880
 
            return
1881
 
        prev_revno, prev_revision = self.last_revision_info()
1882
 
        for revision in self.repository.get_revisions(revision_ids):
1883
 
            if prev_revision == _mod_revision.NULL_REVISION:
1884
 
                if revision.parent_ids != []:
1885
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
1886
 
                                                         revision.revision_id)
1887
 
            else:
1888
 
                if revision.parent_ids[0] != prev_revision:
1889
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
1890
 
                                                         revision.revision_id)
1891
 
            prev_revision = revision.revision_id
1892
 
        self.set_last_revision_info(prev_revno + len(revision_ids),
1893
 
                                    revision_ids[-1])
1894
 
 
1895
 
    @needs_write_lock
1896
 
    def _set_parent_location(self, url):
1897
 
        """Set the parent branch"""
1898
 
        self._set_config_location('parent_location', url, make_relative=True)
1899
 
 
1900
 
    @needs_read_lock
1901
 
    def _get_parent_location(self):
1902
 
        """Set the parent branch"""
1903
 
        return self._get_config_location('parent_location')
1904
 
 
1905
 
    def set_push_location(self, location):
1906
 
        """See Branch.set_push_location."""
1907
 
        self._set_config_location('push_location', location)
1908
 
 
1909
 
    def set_bound_location(self, location):
1910
 
        """See Branch.set_push_location."""
1911
 
        result = None
1912
 
        config = self.get_config()
1913
 
        if location is None:
1914
 
            if config.get_user_option('bound') != 'True':
1915
 
                return False
1916
 
            else:
1917
 
                config.set_user_option('bound', 'False')
1918
 
                return True
1919
 
        else:
1920
 
            self._set_config_location('bound_location', location,
1921
 
                                      config=config)
1922
 
            config.set_user_option('bound', 'True')
1923
 
        return True
1924
 
 
1925
 
    def _get_bound_location(self, bound):
1926
 
        """Return the bound location in the config file.
1927
 
 
1928
 
        Return None if the bound parameter does not match"""
1929
 
        config = self.get_config()
1930
 
        config_bound = (config.get_user_option('bound') == 'True')
1931
 
        if config_bound != bound:
1932
 
            return None
1933
 
        return self._get_config_location('bound_location', config=config)
1934
 
 
1935
 
    def get_bound_location(self):
1936
 
        """See Branch.set_push_location."""
1937
 
        return self._get_bound_location(True)
1938
 
 
1939
 
    def get_old_bound_location(self):
1940
 
        """See Branch.get_old_bound_location"""
1941
 
        return self._get_bound_location(False)
1942
 
 
1943
 
    def set_append_revisions_only(self, enabled):
1944
 
        if enabled:
1945
 
            value = 'True'
1946
 
        else:
1947
 
            value = 'False'
1948
 
        self.get_config().set_user_option('append_revisions_only', value)
1949
 
 
1950
 
    def _get_append_revisions_only(self):
1951
 
        value = self.get_config().get_user_option('append_revisions_only')
1952
 
        return value == 'True'
1953
 
 
1954
 
    def _synchronize_history(self, destination, revision_id):
1955
 
        """Synchronize last revision and revision history between branches.
1956
 
 
1957
 
        This version is most efficient when the destination is also a
1958
 
        BzrBranch6, but works for BzrBranch5, as long as the destination's
1959
 
        repository contains all the lefthand ancestors of the intended
1960
 
        last_revision.  If not, set_last_revision_info will fail.
1961
 
 
1962
 
        :param destination: The branch to copy the history into
1963
 
        :param revision_id: The revision-id to truncate history at.  May
1964
 
          be None to copy complete history.
1965
 
        """
1966
 
        if revision_id is None:
1967
 
            revno, revision_id = self.last_revision_info()
1968
 
        else:
1969
 
            revno = self.revision_id_to_revno(revision_id)
1970
 
        destination.set_last_revision_info(revno, revision_id)
1971
 
 
1972
 
    def _make_tags(self):
1973
 
        return BasicTags(self)
1974
 
 
1975
 
 
1976
1391
class BranchTestProviderAdapter(object):
1977
1392
    """A tool to generate a suite testing multiple branch formats at once.
1978
1393
 
1996
1411
            new_test.bzrdir_format = bzrdir_format
1997
1412
            new_test.branch_format = branch_format
1998
1413
            def make_new_test_id():
1999
 
                # the format can be either a class or an instance
2000
 
                name = getattr(branch_format, '__name__',
2001
 
                        branch_format.__class__.__name__)
2002
 
                new_id = "%s(%s)" % (new_test.id(), name)
 
1414
                new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
2003
1415
                return lambda: new_id
2004
1416
            new_test.id = make_new_test_id()
2005
1417
            result.addTest(new_test)
2006
1418
        return result
2007
1419
 
2008
1420
 
2009
 
######################################################################
2010
 
# results of operations
2011
 
 
2012
 
 
2013
 
class _Result(object):
2014
 
 
2015
 
    def _show_tag_conficts(self, to_file):
2016
 
        if not getattr(self, 'tag_conflicts', None):
2017
 
            return
2018
 
        to_file.write('Conflicting tags:\n')
2019
 
        for name, value1, value2 in self.tag_conflicts:
2020
 
            to_file.write('    %s\n' % (name, ))
2021
 
 
2022
 
 
2023
 
class PullResult(_Result):
2024
 
    """Result of a Branch.pull operation.
2025
 
 
2026
 
    :ivar old_revno: Revision number before pull.
2027
 
    :ivar new_revno: Revision number after pull.
2028
 
    :ivar old_revid: Tip revision id before pull.
2029
 
    :ivar new_revid: Tip revision id after pull.
2030
 
    :ivar source_branch: Source (local) branch object.
2031
 
    :ivar master_branch: Master branch of the target, or None.
2032
 
    :ivar target_branch: Target/destination branch object.
2033
 
    """
2034
 
 
2035
 
    def __int__(self):
2036
 
        # DEPRECATED: pull used to return the change in revno
2037
 
        return self.new_revno - self.old_revno
2038
 
 
2039
 
    def report(self, to_file):
2040
 
        if self.old_revid == self.new_revid:
2041
 
            to_file.write('No revisions to pull.\n')
2042
 
        else:
2043
 
            to_file.write('Now on revision %d.\n' % self.new_revno)
2044
 
        self._show_tag_conficts(to_file)
2045
 
 
2046
 
 
2047
 
class PushResult(_Result):
2048
 
    """Result of a Branch.push operation.
2049
 
 
2050
 
    :ivar old_revno: Revision number before push.
2051
 
    :ivar new_revno: Revision number after push.
2052
 
    :ivar old_revid: Tip revision id before push.
2053
 
    :ivar new_revid: Tip revision id after push.
2054
 
    :ivar source_branch: Source branch object.
2055
 
    :ivar master_branch: Master branch of the target, or None.
2056
 
    :ivar target_branch: Target/destination branch object.
2057
 
    """
2058
 
 
2059
 
    def __int__(self):
2060
 
        # DEPRECATED: push used to return the change in revno
2061
 
        return self.new_revno - self.old_revno
2062
 
 
2063
 
    def report(self, to_file):
2064
 
        """Write a human-readable description of the result."""
2065
 
        if self.old_revid == self.new_revid:
2066
 
            to_file.write('No new revisions to push.\n')
2067
 
        else:
2068
 
            to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2069
 
        self._show_tag_conficts(to_file)
2070
 
 
2071
 
 
2072
1421
class BranchCheckResult(object):
2073
1422
    """Results of checking branch consistency.
2074
1423
 
2089
1438
             self.branch._format)
2090
1439
 
2091
1440
 
2092
 
class Converter5to6(object):
2093
 
    """Perform an in-place upgrade of format 5 to format 6"""
2094
 
 
2095
 
    def convert(self, branch):
2096
 
        # Data for 5 and 6 can peacefully coexist.
2097
 
        format = BzrBranchFormat6()
2098
 
        new_branch = format.open(branch.bzrdir, _found=True)
2099
 
 
2100
 
        # Copy source data into target
2101
 
        new_branch.set_last_revision_info(*branch.last_revision_info())
2102
 
        new_branch.set_parent(branch.get_parent())
2103
 
        new_branch.set_bound_location(branch.get_bound_location())
2104
 
        new_branch.set_push_location(branch.get_push_location())
2105
 
 
2106
 
        # New branch has no tags by default
2107
 
        new_branch.tags._set_tag_dict({})
2108
 
 
2109
 
        # Copying done; now update target format
2110
 
        new_branch.control_files.put_utf8('format',
2111
 
            format.get_format_string())
2112
 
 
2113
 
        # Clean up old files
2114
 
        new_branch.control_files._transport.delete('revision-history')
2115
 
        try:
2116
 
            branch.set_parent(None)
2117
 
        except NoSuchFile:
2118
 
            pass
2119
 
        branch.set_bound_location(None)
 
1441
######################################################################
 
1442
# predicates
 
1443
 
 
1444
 
 
1445
@deprecated_function(zero_eight)
 
1446
def is_control_file(*args, **kwargs):
 
1447
    """See bzrlib.workingtree.is_control_file."""
 
1448
    return bzrlib.workingtree.is_control_file(*args, **kwargs)