~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 16:43:12 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730164312-b025fd3ff0cee59e
rename  gpl.txt => COPYING.txt

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
 
26
23
import bzrlib
27
 
from bzrlib import (
28
 
        bzrdir,
29
 
        cache_utf8,
30
 
        config as _mod_config,
31
 
        errors,
32
 
        lockdir,
33
 
        lockable_files,
34
 
        osutils,
35
 
        revision as _mod_revision,
36
 
        transport,
37
 
        tree,
38
 
        tsort,
39
 
        ui,
40
 
        urlutils,
41
 
        )
42
 
from bzrlib.config import BranchConfig, TreeConfig
43
 
from bzrlib.lockable_files import LockableFiles, TransportLock
44
 
from bzrlib.tag import (
45
 
    BasicTags,
46
 
    DisabledTags,
47
 
    )
48
 
""")
49
 
 
 
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
 
25
        tree, \
 
26
        ui, \
 
27
        urlutils
 
28
from bzrlib.config import TreeConfig
50
29
from bzrlib.decorators import needs_read_lock, needs_write_lock
51
 
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
52
 
                           HistoryMissing, InvalidRevisionId,
53
 
                           InvalidRevisionNumber, LockError, NoSuchFile,
 
30
import bzrlib.errors as errors
 
31
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches, 
 
32
                           HistoryMissing, InvalidRevisionId, 
 
33
                           InvalidRevisionNumber, LockError, NoSuchFile, 
54
34
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
55
 
                           NotBranchError, UninitializableFormat,
56
 
                           UnlistableStore, UnlistableBranch,
 
35
                           NotBranchError, UninitializableFormat, 
 
36
                           UnlistableStore, UnlistableBranch, 
57
37
                           )
58
 
from bzrlib.hooks import Hooks
 
38
from bzrlib.lockable_files import LockableFiles, TransportLock
59
39
from bzrlib.symbol_versioning import (deprecated_function,
60
40
                                      deprecated_method,
61
41
                                      DEPRECATED_PARAMETER,
62
42
                                      deprecated_passed,
63
 
                                      zero_eight, zero_nine, zero_sixteen,
 
43
                                      zero_eight, zero_nine,
64
44
                                      )
65
45
from bzrlib.trace import mutter, note
66
46
 
67
47
 
68
48
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
69
49
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
70
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
50
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
71
51
 
72
52
 
73
53
# TODO: Maybe include checks for common corruption of newlines, etc?
87
67
 
88
68
    base
89
69
        Base directory/url of the branch.
90
 
 
91
 
    hooks: An instance of BranchHooks.
92
70
    """
93
71
    # this is really an instance variable - FIXME move it there
94
72
    # - RBC 20060112
95
73
    base = None
96
74
 
97
 
    # override this to set the strategy for storing tags
98
 
    def _make_tags(self):
99
 
        return DisabledTags(self)
100
 
 
101
75
    def __init__(self, *ignored, **ignored_too):
102
 
        self.tags = self._make_tags()
103
 
        self._revision_history_cache = None
104
 
        self._revision_id_to_revno_cache = None
 
76
        raise NotImplementedError('The Branch class is abstract')
105
77
 
106
78
    def break_lock(self):
107
79
        """Break a lock if one is present from another instance.
158
130
        """
159
131
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
160
132
 
161
 
    @deprecated_function(zero_eight)
162
133
    def setup_caching(self, cache_root):
163
134
        """Subclasses that care about caching should override this, and set
164
135
        up cached stores located under cache_root.
165
 
        
166
 
        NOTE: This is unused.
167
136
        """
168
 
        pass
 
137
        # seems to be unused, 2006-01-13 mbp
 
138
        warn('%s is deprecated' % self.setup_caching)
 
139
        self.cache_root = cache_root
169
140
 
170
141
    def get_config(self):
171
 
        return BranchConfig(self)
 
142
        return bzrlib.config.BranchConfig(self)
172
143
 
173
144
    def _get_nick(self):
174
145
        return self.get_config().get_nickname()
179
150
    nick = property(_get_nick, _set_nick)
180
151
 
181
152
    def is_locked(self):
182
 
        raise NotImplementedError(self.is_locked)
 
153
        raise NotImplementedError('is_locked is abstract')
183
154
 
184
155
    def lock_write(self):
185
 
        raise NotImplementedError(self.lock_write)
 
156
        raise NotImplementedError('lock_write is abstract')
186
157
 
187
158
    def lock_read(self):
188
 
        raise NotImplementedError(self.lock_read)
 
159
        raise NotImplementedError('lock_read is abstract')
189
160
 
190
161
    def unlock(self):
191
 
        raise NotImplementedError(self.unlock)
 
162
        raise NotImplementedError('unlock is abstract')
192
163
 
193
164
    def peek_lock_mode(self):
194
165
        """Return lock mode for the Branch: 'r', 'w' or None"""
195
166
        raise NotImplementedError(self.peek_lock_mode)
196
167
 
197
168
    def get_physical_lock_status(self):
198
 
        raise NotImplementedError(self.get_physical_lock_status)
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()
 
169
        raise NotImplementedError('get_physical_lock_status is abstract')
257
170
 
258
171
    def abspath(self, name):
259
172
        """Return absolute filename for something in the branch
261
174
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
262
175
        method and not a tree method.
263
176
        """
264
 
        raise NotImplementedError(self.abspath)
 
177
        raise NotImplementedError('abspath is abstract')
265
178
 
266
179
    def bind(self, other):
267
180
        """Bind the local branch the other branch.
295
208
        try:
296
209
            if last_revision is None:
297
210
                pb.update('get source history')
298
 
                last_revision = from_branch.last_revision()
299
 
                if last_revision is None:
300
 
                    last_revision = _mod_revision.NULL_REVISION
 
211
                from_history = from_branch.revision_history()
 
212
                if from_history:
 
213
                    last_revision = from_history[-1]
 
214
                else:
 
215
                    # no history in the source branch
 
216
                    last_revision = revision.NULL_REVISION
301
217
            return self.repository.fetch(from_branch.repository,
302
218
                                         revision_id=last_revision,
303
219
                                         pb=nested_pb)
314
230
        """
315
231
        return None
316
232
    
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
 
 
322
233
    def get_commit_builder(self, parents, config=None, timestamp=None, 
323
234
                           timezone=None, committer=None, revprops=None, 
324
235
                           revision_id=None):
336
247
        if config is None:
337
248
            config = self.get_config()
338
249
        
339
 
        return self.repository.get_commit_builder(self, parents, config,
 
250
        return self.repository.get_commit_builder(self, parents, config, 
340
251
            timestamp, timezone, committer, revprops, revision_id)
341
252
 
342
253
    def get_master_branch(self):
358
269
            raise InvalidRevisionNumber(revno)
359
270
        return self.repository.get_revision_delta(rh[revno-1])
360
271
 
361
 
    @deprecated_method(zero_sixteen)
362
272
    def get_root_id(self):
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
 
        """
368
 
        raise NotImplementedError(self.get_root_id)
 
273
        """Return the id of this branches root"""
 
274
        raise NotImplementedError('get_root_id is abstract')
369
275
 
370
276
    def print_file(self, file, revision_id):
371
277
        """Print `file` to stdout."""
372
 
        raise NotImplementedError(self.print_file)
 
278
        raise NotImplementedError('print_file is abstract')
373
279
 
374
280
    def append_revision(self, *revision_ids):
375
 
        raise NotImplementedError(self.append_revision)
 
281
        raise NotImplementedError('append_revision is abstract')
376
282
 
377
283
    def set_revision_history(self, rev_history):
378
 
        raise NotImplementedError(self.set_revision_history)
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
 
284
        raise NotImplementedError('set_revision_history is abstract')
 
285
 
427
286
    def revision_history(self):
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)
 
287
        """Return sequence of revision hashes on to this branch."""
 
288
        raise NotImplementedError('revision_history is abstract')
439
289
 
440
290
    def revno(self):
441
291
        """Return current revision number for this branch.
449
299
        """Older format branches cannot bind or unbind."""
450
300
        raise errors.UpgradeRequired(self.base)
451
301
 
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
 
 
456
302
    def last_revision(self):
457
 
        """Return last revision id, or None"""
 
303
        """Return last patch hash, or None if no history."""
458
304
        ph = self.revision_history()
459
305
        if ph:
460
306
            return ph[-1]
461
307
        else:
462
308
            return None
463
309
 
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
 
 
476
310
    def missing_revisions(self, other, stop_revision=None):
477
311
        """Return a list of new revisions that would perfectly fit.
478
312
        
503
337
        :param stop_revision: Updated until the given revision
504
338
        :return: None
505
339
        """
506
 
        raise NotImplementedError(self.update_revisions)
 
340
        raise NotImplementedError('update_revisions is abstract')
507
341
 
508
342
    def revision_id_to_revno(self, revision_id):
509
343
        """Given a revision id, return its revno"""
510
344
        if revision_id is None:
511
345
            return 0
512
 
        revision_id = osutils.safe_revision_id(revision_id)
513
346
        history = self.revision_history()
514
347
        try:
515
348
            return history.index(revision_id) + 1
516
349
        except ValueError:
517
 
            raise errors.NoSuchRevision(self, revision_id)
 
350
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
518
351
 
519
352
    def get_rev_id(self, revno, history=None):
520
353
        """Find the revision id of the specified revno."""
522
355
            return None
523
356
        if history is None:
524
357
            history = self.revision_history()
525
 
        if revno <= 0 or revno > len(history):
526
 
            raise errors.NoSuchRevision(self, revno)
 
358
        elif revno <= 0 or revno > len(history):
 
359
            raise bzrlib.errors.NoSuchRevision(self, revno)
527
360
        return history[revno - 1]
528
361
 
529
362
    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
 
        """
536
 
        raise NotImplementedError(self.pull)
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)
 
363
        raise NotImplementedError('pull is abstract')
544
364
 
545
365
    def basis_tree(self):
546
366
        """Return `Tree` object for last revision."""
551
371
 
552
372
        This can change the directory or the filename or both.
553
373
        """
554
 
        raise NotImplementedError(self.rename_one)
 
374
        raise NotImplementedError('rename_one is abstract')
555
375
 
556
376
    def move(self, from_paths, to_name):
557
377
        """Rename files.
567
387
        This returns a list of (from_path, to_path) pairs for each
568
388
        entry that is moved.
569
389
        """
570
 
        raise NotImplementedError(self.move)
 
390
        raise NotImplementedError('move is abstract')
571
391
 
572
392
    def get_parent(self):
573
393
        """Return the parent location of the branch.
576
396
        pattern is that the user can override it by specifying a
577
397
        location.
578
398
        """
579
 
        raise NotImplementedError(self.get_parent)
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
 
399
        raise NotImplementedError('get_parent is abstract')
598
400
 
599
401
    def get_submit_branch(self):
600
402
        """Return the submit location of the branch.
614
416
        """
615
417
        self.get_config().set_user_option('submit_branch', location)
616
418
 
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
 
 
633
419
    def get_push_location(self):
634
420
        """Return the None or the location to push this branch to."""
635
 
        push_loc = self.get_config().get_user_option('push_location')
636
 
        return push_loc
 
421
        raise NotImplementedError('get_push_location is abstract')
637
422
 
638
423
    def set_push_location(self, location):
639
424
        """Set a new push location for this branch."""
640
 
        raise NotImplementedError(self.set_push_location)
 
425
        raise NotImplementedError('set_push_location is abstract')
641
426
 
642
427
    def set_parent(self, url):
643
 
        raise NotImplementedError(self.set_parent)
 
428
        raise NotImplementedError('set_parent is abstract')
644
429
 
645
430
    @needs_write_lock
646
431
    def update(self):
667
452
            raise InvalidRevisionNumber(revno)
668
453
 
669
454
    @needs_read_lock
670
 
    def clone(self, to_bzrdir, revision_id=None):
 
455
    def clone(self, *args, **kwargs):
671
456
        """Clone this branch into to_bzrdir preserving all semantic values.
672
457
        
673
458
        revision_id: if not None, the revision history in the new branch will
674
459
                     be truncated to end with revision_id.
675
460
        """
 
461
        # for API compatibility, until 0.8 releases we provide the old api:
 
462
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
 
463
        # after 0.8 releases, the *args and **kwargs should be changed:
 
464
        # def clone(self, to_bzrdir, revision_id=None):
 
465
        if (kwargs.get('to_location', None) or
 
466
            kwargs.get('revision', None) or
 
467
            kwargs.get('basis_branch', None) or
 
468
            (len(args) and isinstance(args[0], basestring))):
 
469
            # backwards compatibility api:
 
470
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
 
471
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
 
472
            # get basis_branch
 
473
            if len(args) > 2:
 
474
                basis_branch = args[2]
 
475
            else:
 
476
                basis_branch = kwargs.get('basis_branch', None)
 
477
            if basis_branch:
 
478
                basis = basis_branch.bzrdir
 
479
            else:
 
480
                basis = None
 
481
            # get revision
 
482
            if len(args) > 1:
 
483
                revision_id = args[1]
 
484
            else:
 
485
                revision_id = kwargs.get('revision', None)
 
486
            # get location
 
487
            if len(args):
 
488
                url = args[0]
 
489
            else:
 
490
                # no default to raise if not provided.
 
491
                url = kwargs.get('to_location')
 
492
            return self.bzrdir.clone(url,
 
493
                                     revision_id=revision_id,
 
494
                                     basis=basis).open_branch()
 
495
        # new cleaner api.
 
496
        # generate args by hand 
 
497
        if len(args) > 1:
 
498
            revision_id = args[1]
 
499
        else:
 
500
            revision_id = kwargs.get('revision_id', None)
 
501
        if len(args):
 
502
            to_bzrdir = args[0]
 
503
        else:
 
504
            # no default to raise if not provided.
 
505
            to_bzrdir = kwargs.get('to_bzrdir')
676
506
        result = self._format.initialize(to_bzrdir)
677
507
        self.copy_content_into(result, revision_id=revision_id)
678
508
        return  result
689
519
        result.set_parent(self.bzrdir.root_transport.base)
690
520
        return result
691
521
 
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.
 
522
    @needs_read_lock
 
523
    def copy_content_into(self, destination, revision_id=None):
 
524
        """Copy the content of self into destination.
 
525
 
 
526
        revision_id: if not None, the revision history in the new branch will
 
527
                     be truncated to end with revision_id.
704
528
        """
705
529
        new_history = self.revision_history()
706
530
        if revision_id is not None:
707
 
            revision_id = osutils.safe_revision_id(revision_id)
708
531
            try:
709
532
                new_history = new_history[:new_history.index(revision_id) + 1]
710
533
            except ValueError:
711
534
                rev = self.repository.get_revision(revision_id)
712
535
                new_history = rev.get_history(self.repository)[1:]
713
536
        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)
723
 
        try:
724
 
            parent = self.get_parent()
725
 
        except errors.InaccessibleParent, e:
726
 
            mutter('parent was not accessible to copy: %s', e)
727
 
        else:
728
 
            if parent:
729
 
                destination.set_parent(parent)
730
 
        self.tags.merge_to(destination.tags)
 
537
        parent = self.get_parent()
 
538
        if parent:
 
539
            destination.set_parent(parent)
731
540
 
732
541
    @needs_read_lock
733
542
    def check(self):
759
568
            mainline_parent_id = revision_id
760
569
        return BranchCheckResult(self)
761
570
 
762
 
    def _get_checkout_format(self):
763
 
        """Return the most suitable metadir for a checkout of this branch.
764
 
        Weaves are used if this branch's repository uses weaves.
765
 
        """
766
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
767
 
            from bzrlib.repofmt import weaverepo
768
 
            format = bzrdir.BzrDirMetaFormat1()
769
 
            format.repository_format = weaverepo.RepositoryFormat7()
770
 
        else:
771
 
            format = self.repository.bzrdir.checkout_metadir()
772
 
            format.set_branch_format(self._format)
773
 
        return format
774
 
 
775
 
    def create_checkout(self, to_location, revision_id=None,
776
 
                        lightweight=False):
777
 
        """Create a checkout of a branch.
778
 
        
779
 
        :param to_location: The url to produce the checkout at
780
 
        :param revision_id: The revision to check out
781
 
        :param lightweight: If True, produce a lightweight checkout, otherwise,
782
 
        produce a bound branch (heavyweight checkout)
783
 
        :return: The tree of the created checkout
784
 
        """
785
 
        t = transport.get_transport(to_location)
786
 
        try:
787
 
            t.mkdir('.')
788
 
        except errors.FileExists:
789
 
            pass
790
 
        if lightweight:
791
 
            format = self._get_checkout_format()
792
 
            checkout = format.initialize_on_transport(t)
793
 
            BranchReferenceFormat().initialize(checkout, self)
794
 
        else:
795
 
            format = self._get_checkout_format()
796
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
797
 
                to_location, force_new_tree=False, format=format)
798
 
            checkout = checkout_branch.bzrdir
799
 
            checkout_branch.bind(self)
800
 
            # pull up to the specified revision_id to set the initial 
801
 
            # branch tip correctly, and seed it with history.
802
 
            checkout_branch.pull(self, stop_revision=revision_id)
803
 
        tree = checkout.create_workingtree(revision_id)
804
 
        basis_tree = tree.basis_tree()
805
 
        basis_tree.lock_read()
806
 
        try:
807
 
            for path, file_id in basis_tree.iter_references():
808
 
                reference_parent = self.reference_parent(file_id, path)
809
 
                reference_parent.create_checkout(tree.abspath(path),
810
 
                    basis_tree.get_reference_revision(file_id, path),
811
 
                    lightweight)
812
 
        finally:
813
 
            basis_tree.unlock()
814
 
        return tree
815
 
 
816
 
    def reference_parent(self, file_id, path):
817
 
        """Return the parent branch for a tree-reference file_id
818
 
        :param file_id: The file_id of the tree reference
819
 
        :param path: The path of the file_id in the tree
820
 
        :return: A branch associated with the file_id
821
 
        """
822
 
        # FIXME should provide multiple branches, based on config
823
 
        return Branch.open(self.bzrdir.root_transport.clone(path).base)
824
 
 
825
 
    def supports_tags(self):
826
 
        return self._format.supports_tags()
827
 
 
828
571
 
829
572
class BranchFormat(object):
830
573
    """An encapsulation of the initialization and open routines for a format.
867
610
        """Return the current default format."""
868
611
        return klass._default_format
869
612
 
870
 
    def get_reference(self, a_bzrdir):
871
 
        """Get the target reference of the branch in a_bzrdir.
872
 
 
873
 
        format probing must have been completed before calling
874
 
        this method - it is assumed that the format of the branch
875
 
        in a_bzrdir is correct.
876
 
 
877
 
        :param a_bzrdir: The bzrdir to get the branch data from.
878
 
        :return: None if the branch is not a reference branch.
879
 
        """
880
 
        return None
881
 
 
882
613
    def get_format_string(self):
883
614
        """Return the ASCII format string that identifies this format."""
884
615
        raise NotImplementedError(self.get_format_string)
885
616
 
886
617
    def get_format_description(self):
887
618
        """Return the short format description for this format."""
888
 
        raise NotImplementedError(self.get_format_description)
889
 
 
890
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
891
 
                           set_format=True):
892
 
        """Initialize a branch in a bzrdir, with specified files
893
 
 
894
 
        :param a_bzrdir: The bzrdir to initialize the branch in
895
 
        :param utf8_files: The files to create as a list of
896
 
            (filename, content) tuples
897
 
        :param set_format: If True, set the format with
898
 
            self.get_format_string.  (BzrBranch4 has its format set
899
 
            elsewhere)
900
 
        :return: a branch in this format
901
 
        """
902
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
903
 
        branch_transport = a_bzrdir.get_branch_transport(self)
904
 
        lock_map = {
905
 
            'metadir': ('lock', lockdir.LockDir),
906
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
907
 
        }
908
 
        lock_name, lock_class = lock_map[lock_type]
909
 
        control_files = lockable_files.LockableFiles(branch_transport,
910
 
            lock_name, lock_class)
911
 
        control_files.create_lock()
912
 
        control_files.lock_write()
913
 
        if set_format:
914
 
            control_files.put_utf8('format', self.get_format_string())
915
 
        try:
916
 
            for file, content in utf8_files:
917
 
                control_files.put_utf8(file, content)
918
 
        finally:
919
 
            control_files.unlock()
920
 
        return self.open(a_bzrdir, _found=True)
 
619
        raise NotImplementedError(self.get_format_string)
921
620
 
922
621
    def initialize(self, a_bzrdir):
923
622
        """Create a branch of this format in a_bzrdir."""
956
655
    def __str__(self):
957
656
        return self.get_format_string().rstrip()
958
657
 
959
 
    def supports_tags(self):
960
 
        """True if this format supports tags stored in the branch"""
961
 
        return False  # by default
962
 
 
963
 
    # XXX: Probably doesn't really belong here -- mbp 20070212
964
 
    def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
965
 
            lock_class):
966
 
        branch_transport = a_bzrdir.get_branch_transport(self)
967
 
        control_files = lockable_files.LockableFiles(branch_transport,
968
 
            lock_filename, lock_class)
969
 
        control_files.create_lock()
970
 
        control_files.lock_write()
971
 
        try:
972
 
            for filename, content in utf8_files:
973
 
                control_files.put_utf8(filename, content)
974
 
        finally:
975
 
            control_files.unlock()
976
 
 
977
 
 
978
 
class BranchHooks(Hooks):
979
 
    """A dictionary mapping hook name to a list of callables for branch hooks.
980
 
    
981
 
    e.g. ['set_rh'] Is the list of items to be called when the
982
 
    set_revision_history function is invoked.
983
 
    """
984
 
 
985
 
    def __init__(self):
986
 
        """Create the default hooks.
987
 
 
988
 
        These are all empty initially, because by default nothing should get
989
 
        notified.
990
 
        """
991
 
        Hooks.__init__(self)
992
 
        # Introduced in 0.15:
993
 
        # invoked whenever the revision history has been set
994
 
        # with set_revision_history. The api signature is
995
 
        # (branch, revision_history), and the branch will
996
 
        # be write-locked.
997
 
        self['set_rh'] = []
998
 
        # invoked after a push operation completes.
999
 
        # the api signature is
1000
 
        # (push_result)
1001
 
        # containing the members
1002
 
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1003
 
        # where local is the local branch or None, master is the target 
1004
 
        # master branch, and the rest should be self explanatory. The source
1005
 
        # is read locked and the target branches write locked. Source will
1006
 
        # be the local low-latency branch.
1007
 
        self['post_push'] = []
1008
 
        # invoked after a pull operation completes.
1009
 
        # the api signature is
1010
 
        # (pull_result)
1011
 
        # containing the members
1012
 
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1013
 
        # where local is the local branch or None, master is the target 
1014
 
        # master branch, and the rest should be self explanatory. The source
1015
 
        # is read locked and the target branches write locked. The local
1016
 
        # branch is the low-latency branch.
1017
 
        self['post_pull'] = []
1018
 
        # invoked after a commit operation completes.
1019
 
        # the api signature is 
1020
 
        # (local, master, old_revno, old_revid, new_revno, new_revid)
1021
 
        # old_revid is NULL_REVISION for the first commit to a branch.
1022
 
        self['post_commit'] = []
1023
 
        # invoked after a uncommit operation completes.
1024
 
        # the api signature is
1025
 
        # (local, master, old_revno, old_revid, new_revno, new_revid) where
1026
 
        # local is the local branch or None, master is the target branch,
1027
 
        # and an empty branch recieves new_revno of 0, new_revid of None.
1028
 
        self['post_uncommit'] = []
1029
 
 
1030
 
 
1031
 
# install the default hooks into the Branch class.
1032
 
Branch.hooks = BranchHooks()
1033
 
 
1034
658
 
1035
659
class BzrBranchFormat4(BranchFormat):
1036
660
    """Bzr branch format 4.
1046
670
 
1047
671
    def initialize(self, a_bzrdir):
1048
672
        """Create a branch of this format in a_bzrdir."""
 
673
        mutter('creating branch in %s', a_bzrdir.transport.base)
 
674
        branch_transport = a_bzrdir.get_branch_transport(self)
1049
675
        utf8_files = [('revision-history', ''),
1050
676
                      ('branch-name', ''),
1051
677
                      ]
1052
 
        return self._initialize_helper(a_bzrdir, utf8_files,
1053
 
                                       lock_type='branch4', set_format=False)
 
678
        control_files = LockableFiles(branch_transport, 'branch-lock',
 
679
                                      TransportLock)
 
680
        control_files.create_lock()
 
681
        control_files.lock_write()
 
682
        try:
 
683
            for file, content in utf8_files:
 
684
                control_files.put_utf8(file, content)
 
685
        finally:
 
686
            control_files.unlock()
 
687
        return self.open(a_bzrdir, _found=True)
1054
688
 
1055
689
    def __init__(self):
1056
690
        super(BzrBranchFormat4, self).__init__()
1097
731
        
1098
732
    def initialize(self, a_bzrdir):
1099
733
        """Create a branch of this format in a_bzrdir."""
 
734
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
735
        branch_transport = a_bzrdir.get_branch_transport(self)
1100
736
        utf8_files = [('revision-history', ''),
1101
737
                      ('branch-name', ''),
1102
738
                      ]
1103
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
739
        control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
 
740
        control_files.create_lock()
 
741
        control_files.lock_write()
 
742
        control_files.put_utf8('format', self.get_format_string())
 
743
        try:
 
744
            for file, content in utf8_files:
 
745
                control_files.put_utf8(file, content)
 
746
        finally:
 
747
            control_files.unlock()
 
748
        return self.open(a_bzrdir, _found=True, )
1104
749
 
1105
750
    def __init__(self):
1106
751
        super(BzrBranchFormat5, self).__init__()
1115
760
        if not _found:
1116
761
            format = BranchFormat.find_format(a_bzrdir)
1117
762
            assert format.__class__ == self.__class__
1118
 
        try:
1119
 
            transport = a_bzrdir.get_branch_transport(None)
1120
 
            control_files = lockable_files.LockableFiles(transport, 'lock',
1121
 
                                                         lockdir.LockDir)
1122
 
            return BzrBranch5(_format=self,
1123
 
                              _control_files=control_files,
1124
 
                              a_bzrdir=a_bzrdir,
1125
 
                              _repository=a_bzrdir.find_repository())
1126
 
        except NoSuchFile:
1127
 
            raise NotBranchError(path=transport.base)
1128
 
 
1129
 
 
1130
 
class BzrBranchFormat6(BzrBranchFormat5):
1131
 
    """Branch format with last-revision
1132
 
 
1133
 
    Unlike previous formats, this has no explicit revision history. Instead,
1134
 
    this just stores the last-revision, and the left-hand history leading
1135
 
    up to there is the history.
1136
 
 
1137
 
    This format was introduced in bzr 0.15
1138
 
    """
1139
 
 
1140
 
    def get_format_string(self):
1141
 
        """See BranchFormat.get_format_string()."""
1142
 
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
1143
 
 
1144
 
    def get_format_description(self):
1145
 
        """See BranchFormat.get_format_description()."""
1146
 
        return "Branch format 6"
1147
 
 
1148
 
    def initialize(self, a_bzrdir):
1149
 
        """Create a branch of this format in a_bzrdir."""
1150
 
        utf8_files = [('last-revision', '0 null:\n'),
1151
 
                      ('branch-name', ''),
1152
 
                      ('branch.conf', ''),
1153
 
                      ('tags', ''),
1154
 
                      ]
1155
 
        return self._initialize_helper(a_bzrdir, utf8_files)
1156
 
 
1157
 
    def open(self, a_bzrdir, _found=False):
1158
 
        """Return the branch object for a_bzrdir
1159
 
 
1160
 
        _found is a private parameter, do not use it. It is used to indicate
1161
 
               if format probing has already be done.
1162
 
        """
1163
 
        if not _found:
1164
 
            format = BranchFormat.find_format(a_bzrdir)
1165
 
            assert format.__class__ == self.__class__
1166
763
        transport = a_bzrdir.get_branch_transport(None)
1167
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1168
 
                                                     lockdir.LockDir)
1169
 
        return BzrBranch6(_format=self,
 
764
        control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
 
765
        return BzrBranch5(_format=self,
1170
766
                          _control_files=control_files,
1171
767
                          a_bzrdir=a_bzrdir,
1172
768
                          _repository=a_bzrdir.find_repository())
1173
769
 
1174
 
    def supports_tags(self):
1175
 
        return True
 
770
    def __str__(self):
 
771
        return "Bazaar-NG Metadir branch format 5"
1176
772
 
1177
773
 
1178
774
class BranchReferenceFormat(BranchFormat):
1194
790
        """See BranchFormat.get_format_description()."""
1195
791
        return "Checkout reference format 1"
1196
792
        
1197
 
    def get_reference(self, a_bzrdir):
1198
 
        """See BranchFormat.get_reference()."""
1199
 
        transport = a_bzrdir.get_branch_transport(None)
1200
 
        return transport.get('location').read()
1201
 
 
1202
793
    def initialize(self, a_bzrdir, target_branch=None):
1203
794
        """Create a branch of this format in a_bzrdir."""
1204
795
        if target_branch is None:
1207
798
            raise errors.UninitializableFormat(self)
1208
799
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
1209
800
        branch_transport = a_bzrdir.get_branch_transport(self)
1210
 
        branch_transport.put_bytes('location',
1211
 
            target_branch.bzrdir.root_transport.base)
1212
 
        branch_transport.put_bytes('format', self.get_format_string())
 
801
        # FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
 
802
        branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
 
803
        branch_transport.put('format', StringIO(self.get_format_string()))
1213
804
        return self.open(a_bzrdir, _found=True)
1214
805
 
1215
806
    def __init__(self):
1226
817
            # emit some sort of warning/error to the caller ?!
1227
818
        return clone
1228
819
 
1229
 
    def open(self, a_bzrdir, _found=False, location=None):
 
820
    def open(self, a_bzrdir, _found=False):
1230
821
        """Return the branch that the branch reference in a_bzrdir points at.
1231
822
 
1232
823
        _found is a private parameter, do not use it. It is used to indicate
1235
826
        if not _found:
1236
827
            format = BranchFormat.find_format(a_bzrdir)
1237
828
            assert format.__class__ == self.__class__
1238
 
        if location is None:
1239
 
            location = self.get_reference(a_bzrdir)
1240
 
        real_bzrdir = bzrdir.BzrDir.open(location)
 
829
        transport = a_bzrdir.get_branch_transport(None)
 
830
        real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
1241
831
        result = real_bzrdir.open_branch()
1242
832
        # this changes the behaviour of result.clone to create a new reference
1243
833
        # rather than a copy of the content of the branch.
1256
846
__default_format = BzrBranchFormat5()
1257
847
BranchFormat.register_format(__default_format)
1258
848
BranchFormat.register_format(BranchReferenceFormat())
1259
 
BranchFormat.register_format(BzrBranchFormat6())
1260
849
BranchFormat.set_default_format(__default_format)
1261
850
_legacy_formats = [BzrBranchFormat4(),
1262
851
                   ]
1269
858
    it's writable, and can be accessed via the normal filesystem API.
1270
859
    """
1271
860
    
1272
 
    def __init__(self, _format=None,
 
861
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
 
862
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
1273
863
                 _control_files=None, a_bzrdir=None, _repository=None):
1274
 
        """Create new branch object at a particular location."""
1275
 
        Branch.__init__(self)
 
864
        """Create new branch object at a particular location.
 
865
 
 
866
        transport -- A Transport object, defining how to access files.
 
867
        
 
868
        init -- If True, create new control files in a previously
 
869
             unversioned directory.  If False, the branch must already
 
870
             be versioned.
 
871
 
 
872
        relax_version_check -- If true, the usual check for the branch
 
873
            version is not applied.  This is intended only for
 
874
            upgrade/recovery type use; it's not guaranteed that
 
875
            all operations will work on old format branches.
 
876
        """
1276
877
        if a_bzrdir is None:
1277
 
            raise ValueError('a_bzrdir must be supplied')
 
878
            self.bzrdir = bzrdir.BzrDir.open(transport.base)
1278
879
        else:
1279
880
            self.bzrdir = a_bzrdir
1280
 
        # self._transport used to point to the directory containing the
1281
 
        # control directory, but was not used - now it's just the transport
1282
 
        # for the branch control files.  mbp 20070212
1283
 
        self._base = self.bzrdir.transport.clone('..').base
 
881
        self._transport = self.bzrdir.transport.clone('..')
 
882
        self._base = self._transport.base
1284
883
        self._format = _format
1285
884
        if _control_files is None:
1286
885
            raise ValueError('BzrBranch _control_files is None')
1287
886
        self.control_files = _control_files
1288
 
        self._transport = _control_files._transport
 
887
        if deprecated_passed(init):
 
888
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
889
                 "deprecated as of bzr 0.8. Please use Branch.create().",
 
890
                 DeprecationWarning,
 
891
                 stacklevel=2)
 
892
            if init:
 
893
                # this is slower than before deprecation, oh well never mind.
 
894
                # -> its deprecated.
 
895
                self._initialize(transport.base)
 
896
        self._check_format(_format)
 
897
        if deprecated_passed(relax_version_check):
 
898
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
899
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
900
                 "Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
 
901
                 "open() method.",
 
902
                 DeprecationWarning,
 
903
                 stacklevel=2)
 
904
            if (not relax_version_check
 
905
                and not self._format.is_supported()):
 
906
                raise errors.UnsupportedFormatError(format=fmt)
 
907
        if deprecated_passed(transport):
 
908
            warn("BzrBranch.__init__(transport=XXX...): The transport "
 
909
                 "parameter is deprecated as of bzr 0.8. "
 
910
                 "Please use Branch.open, or bzrdir.open_branch().",
 
911
                 DeprecationWarning,
 
912
                 stacklevel=2)
1289
913
        self.repository = _repository
1290
914
 
1291
915
    def __str__(self):
1293
917
 
1294
918
    __repr__ = __str__
1295
919
 
 
920
    def __del__(self):
 
921
        # TODO: It might be best to do this somewhere else,
 
922
        # but it is nice for a Branch object to automatically
 
923
        # cache it's information.
 
924
        # Alternatively, we could have the Transport objects cache requests
 
925
        # See the earlier discussion about how major objects (like Branch)
 
926
        # should never expect their __del__ function to run.
 
927
        # XXX: cache_root seems to be unused, 2006-01-13 mbp
 
928
        if hasattr(self, 'cache_root') and self.cache_root is not None:
 
929
            try:
 
930
                osutils.rmtree(self.cache_root)
 
931
            except:
 
932
                pass
 
933
            self.cache_root = None
 
934
 
1296
935
    def _get_base(self):
1297
 
        """Returns the directory containing the control directory."""
1298
936
        return self._base
1299
937
 
1300
938
    base = property(_get_base, doc="The URL for the root of this branch.")
1301
939
 
 
940
    def _finish_transaction(self):
 
941
        """Exit the current transaction."""
 
942
        return self.control_files._finish_transaction()
 
943
 
 
944
    def get_transaction(self):
 
945
        """Return the current active transaction.
 
946
 
 
947
        If no transaction is active, this returns a passthrough object
 
948
        for which all data is immediately flushed and no caching happens.
 
949
        """
 
950
        # this is an explicit function so that we can do tricky stuff
 
951
        # when the storage in rev_storage is elsewhere.
 
952
        # we probably need to hook the two 'lock a location' and 
 
953
        # 'have a transaction' together more delicately, so that
 
954
        # we can have two locks (branch and storage) and one transaction
 
955
        # ... and finishing the transaction unlocks both, but unlocking
 
956
        # does not. - RBC 20051121
 
957
        return self.control_files.get_transaction()
 
958
 
 
959
    def _set_transaction(self, transaction):
 
960
        """Set a new active transaction."""
 
961
        return self.control_files._set_transaction(transaction)
 
962
 
1302
963
    def abspath(self, name):
1303
964
        """See Branch.abspath."""
1304
965
        return self.control_files._transport.abspath(name)
1305
966
 
1306
 
 
1307
 
    @deprecated_method(zero_sixteen)
 
967
    def _check_format(self, format):
 
968
        """Identify the branch format if needed.
 
969
 
 
970
        The format is stored as a reference to the format object in
 
971
        self._format for code that needs to check it later.
 
972
 
 
973
        The format parameter is either None or the branch format class
 
974
        used to open this branch.
 
975
 
 
976
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
 
977
        """
 
978
        if format is None:
 
979
            format = BranchFormat.find_format(self.bzrdir)
 
980
        self._format = format
 
981
        mutter("got branch format %s", self._format)
 
982
 
1308
983
    @needs_read_lock
1309
984
    def get_root_id(self):
1310
985
        """See Branch.get_root_id."""
1314
989
    def is_locked(self):
1315
990
        return self.control_files.is_locked()
1316
991
 
1317
 
    def lock_write(self, token=None):
1318
 
        repo_token = self.repository.lock_write()
 
992
    def lock_write(self):
 
993
        self.repository.lock_write()
1319
994
        try:
1320
 
            token = self.control_files.lock_write(token=token)
 
995
            self.control_files.lock_write()
1321
996
        except:
1322
997
            self.repository.unlock()
1323
998
            raise
1324
 
        return token
1325
999
 
1326
1000
    def lock_read(self):
1327
1001
        self.repository.lock_read()
1337
1011
            self.control_files.unlock()
1338
1012
        finally:
1339
1013
            self.repository.unlock()
1340
 
        if not self.control_files.is_locked():
1341
 
            # we just released the lock
1342
 
            self._clear_cached_state()
1343
1014
        
1344
1015
    def peek_lock_mode(self):
1345
1016
        if self.control_files._lock_count == 0:
1358
1029
    @needs_write_lock
1359
1030
    def append_revision(self, *revision_ids):
1360
1031
        """See Branch.append_revision."""
1361
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1362
1032
        for revision_id in revision_ids:
1363
 
            _mod_revision.check_not_reserved_id(revision_id)
1364
1033
            mutter("add {%s} to revision-history" % revision_id)
1365
1034
        rev_history = self.revision_history()
1366
1035
        rev_history.extend(revision_ids)
1367
1036
        self.set_revision_history(rev_history)
1368
1037
 
1369
 
    def _write_revision_history(self, history):
1370
 
        """Factored out of set_revision_history.
1371
 
 
1372
 
        This performs the actual writing to disk.
1373
 
        It is intended to be called by BzrBranch5.set_revision_history."""
1374
 
        self.control_files.put_bytes(
1375
 
            'revision-history', '\n'.join(history))
1376
 
 
1377
1038
    @needs_write_lock
1378
1039
    def set_revision_history(self, rev_history):
1379
1040
        """See Branch.set_revision_history."""
1380
 
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1381
 
        self._clear_cached_state()
1382
 
        self._write_revision_history(rev_history)
1383
 
        self._cache_revision_history(rev_history)
1384
 
        for hook in Branch.hooks['set_rh']:
1385
 
            hook(self, rev_history)
 
1041
        self.control_files.put_utf8(
 
1042
            'revision-history', '\n'.join(rev_history))
 
1043
        transaction = self.get_transaction()
 
1044
        history = transaction.map.find_revision_history()
 
1045
        if history is not None:
 
1046
            # update the revision history in the identity map.
 
1047
            history[:] = list(rev_history)
 
1048
            # this call is disabled because revision_history is 
 
1049
            # not really an object yet, and the transaction is for objects.
 
1050
            # transaction.register_dirty(history)
 
1051
        else:
 
1052
            transaction.map.add_revision_history(rev_history)
 
1053
            # this call is disabled because revision_history is 
 
1054
            # not really an object yet, and the transaction is for objects.
 
1055
            # transaction.register_clean(history)
 
1056
 
 
1057
    @needs_read_lock
 
1058
    def revision_history(self):
 
1059
        """See Branch.revision_history."""
 
1060
        transaction = self.get_transaction()
 
1061
        history = transaction.map.find_revision_history()
 
1062
        if history is not None:
 
1063
            mutter("cache hit for revision-history in %s", self)
 
1064
            return list(history)
 
1065
        history = [l.rstrip('\r\n') for l in
 
1066
                self.control_files.get_utf8('revision-history').readlines()]
 
1067
        transaction.map.add_revision_history(history)
 
1068
        # this call is disabled because revision_history is 
 
1069
        # not really an object yet, and the transaction is for objects.
 
1070
        # transaction.register_clean(history, precious=True)
 
1071
        return list(history)
1386
1072
 
1387
1073
    @needs_write_lock
1388
 
    def set_last_revision_info(self, revno, revision_id):
1389
 
        revision_id = osutils.safe_revision_id(revision_id)
1390
 
        history = self._lefthand_history(revision_id)
1391
 
        assert len(history) == revno, '%d != %d' % (len(history), revno)
1392
 
        self.set_revision_history(history)
1393
 
 
1394
 
    def _gen_revision_history(self):
1395
 
        history = self.control_files.get('revision-history').read().split('\n')
1396
 
        if history[-1:] == ['']:
1397
 
            # There shouldn't be a trailing newline, but just in case.
1398
 
            history.pop()
1399
 
        return history
1400
 
 
1401
 
    def _lefthand_history(self, revision_id, last_rev=None,
1402
 
                          other_branch=None):
 
1074
    def generate_revision_history(self, revision_id, last_rev=None, 
 
1075
        other_branch=None):
 
1076
        """Create a new revision history that will finish with revision_id.
 
1077
        
 
1078
        :param revision_id: the new tip to use.
 
1079
        :param last_rev: The previous last_revision. If not None, then this
 
1080
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
1081
        :param other_branch: The other branch that DivergedBranches should
 
1082
            raise with respect to.
 
1083
        """
1403
1084
        # stop_revision must be a descendant of last_revision
1404
1085
        stop_graph = self.repository.get_revision_graph(revision_id)
1405
1086
        if last_rev is not None and last_rev not in stop_graph:
1408
1089
        # make a new revision history from the graph
1409
1090
        current_rev_id = revision_id
1410
1091
        new_history = []
1411
 
        while current_rev_id not in (None, _mod_revision.NULL_REVISION):
 
1092
        while current_rev_id not in (None, revision.NULL_REVISION):
1412
1093
            new_history.append(current_rev_id)
1413
1094
            current_rev_id_parents = stop_graph[current_rev_id]
1414
1095
            try:
1416
1097
            except IndexError:
1417
1098
                current_rev_id = None
1418
1099
        new_history.reverse()
1419
 
        return new_history
1420
 
 
1421
 
    @needs_write_lock
1422
 
    def generate_revision_history(self, revision_id, last_rev=None,
1423
 
        other_branch=None):
1424
 
        """Create a new revision history that will finish with revision_id.
1425
 
 
1426
 
        :param revision_id: the new tip to use.
1427
 
        :param last_rev: The previous last_revision. If not None, then this
1428
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
1429
 
        :param other_branch: The other branch that DivergedBranches should
1430
 
            raise with respect to.
1431
 
        """
1432
 
        revision_id = osutils.safe_revision_id(revision_id)
1433
 
        self.set_revision_history(self._lefthand_history(revision_id,
1434
 
            last_rev, other_branch))
 
1100
        self.set_revision_history(new_history)
1435
1101
 
1436
1102
    @needs_write_lock
1437
1103
    def update_revisions(self, other, stop_revision=None):
1443
1109
                if stop_revision is None:
1444
1110
                    # if there are no commits, we're done.
1445
1111
                    return
1446
 
            else:
1447
 
                stop_revision = osutils.safe_revision_id(stop_revision)
1448
1112
            # whats the current last revision, before we fetch [and change it
1449
1113
            # possibly]
1450
1114
            last_rev = self.last_revision()
1475
1139
        return self.bzrdir.open_workingtree()
1476
1140
 
1477
1141
    @needs_write_lock
1478
 
    def pull(self, source, overwrite=False, stop_revision=None,
1479
 
        _hook_master=None, _run_hooks=True):
1480
 
        """See Branch.pull.
1481
 
 
1482
 
        :param _hook_master: Private parameter - set the branch to 
1483
 
            be supplied as the master to push hooks.
1484
 
        :param _run_hooks: Private parameter - allow disabling of
1485
 
            hooks, used when pushing to a master branch.
1486
 
        """
1487
 
        result = PullResult()
1488
 
        result.source_branch = source
1489
 
        result.target_branch = self
 
1142
    def pull(self, source, overwrite=False, stop_revision=None):
 
1143
        """See Branch.pull."""
1490
1144
        source.lock_read()
1491
1145
        try:
1492
 
            result.old_revno, result.old_revid = self.last_revision_info()
 
1146
            old_count = len(self.revision_history())
1493
1147
            try:
1494
 
                self.update_revisions(source, stop_revision)
 
1148
                self.update_revisions(source,stop_revision)
1495
1149
            except DivergedBranches:
1496
1150
                if not overwrite:
1497
1151
                    raise
1498
1152
            if overwrite:
1499
 
                if stop_revision is None:
1500
 
                    stop_revision = source.last_revision()
1501
 
                self.generate_revision_history(stop_revision)
1502
 
            result.tag_conflicts = source.tags.merge_to(self.tags)
1503
 
            result.new_revno, result.new_revid = self.last_revision_info()
1504
 
            if _hook_master:
1505
 
                result.master_branch = _hook_master
1506
 
                result.local_branch = self
1507
 
            else:
1508
 
                result.master_branch = self
1509
 
                result.local_branch = None
1510
 
            if _run_hooks:
1511
 
                for hook in Branch.hooks['post_pull']:
1512
 
                    hook(result)
 
1153
                self.set_revision_history(source.revision_history())
 
1154
            new_count = len(self.revision_history())
 
1155
            return new_count - old_count
1513
1156
        finally:
1514
1157
            source.unlock()
1515
 
        return result
1516
 
 
1517
 
    def _get_parent_location(self):
 
1158
 
 
1159
    def get_parent(self):
 
1160
        """See Branch.get_parent."""
 
1161
 
1518
1162
        _locs = ['parent', 'pull', 'x-pull']
 
1163
        assert self.base[-1] == '/'
1519
1164
        for l in _locs:
1520
1165
            try:
1521
 
                return self.control_files.get(l).read().strip('\n')
 
1166
                parent = self.control_files.get(l).read().strip('\n')
1522
1167
            except NoSuchFile:
1523
 
                pass
1524
 
        return None
1525
 
 
1526
 
    @needs_read_lock
1527
 
    def push(self, target, overwrite=False, stop_revision=None,
1528
 
        _hook_master=None, _run_hooks=True):
1529
 
        """See Branch.push.
1530
 
        
1531
 
        :param _hook_master: Private parameter - set the branch to 
1532
 
            be supplied as the master to push hooks.
1533
 
        :param _run_hooks: Private parameter - allow disabling of
1534
 
            hooks, used when pushing to a master branch.
1535
 
        """
1536
 
        result = PushResult()
1537
 
        result.source_branch = self
1538
 
        result.target_branch = target
1539
 
        target.lock_write()
1540
 
        try:
1541
 
            result.old_revno, result.old_revid = target.last_revision_info()
1542
 
            try:
1543
 
                target.update_revisions(self, stop_revision)
1544
 
            except DivergedBranches:
1545
 
                if not overwrite:
1546
 
                    raise
1547
 
            if overwrite:
1548
 
                target.set_revision_history(self.revision_history())
1549
 
            result.tag_conflicts = self.tags.merge_to(target.tags)
1550
 
            result.new_revno, result.new_revid = target.last_revision_info()
1551
 
            if _hook_master:
1552
 
                result.master_branch = _hook_master
1553
 
                result.local_branch = target
1554
 
            else:
1555
 
                result.master_branch = target
1556
 
                result.local_branch = None
1557
 
            if _run_hooks:
1558
 
                for hook in Branch.hooks['post_push']:
1559
 
                    hook(result)
1560
 
        finally:
1561
 
            target.unlock()
1562
 
        return result
1563
 
 
1564
 
    def get_parent(self):
1565
 
        """See Branch.get_parent."""
1566
 
 
1567
 
        assert self.base[-1] == '/'
1568
 
        parent = self._get_parent_location()
1569
 
        if parent is None:
1570
 
            return parent
1571
 
        # This is an old-format absolute path to a local branch
1572
 
        # turn it into a url
1573
 
        if parent.startswith('/'):
1574
 
            parent = urlutils.local_path_to_url(parent.decode('utf8'))
1575
 
        try:
 
1168
                continue
 
1169
            # This is an old-format absolute path to a local branch
 
1170
            # turn it into a url
 
1171
            if parent.startswith('/'):
 
1172
                parent = urlutils.local_path_to_url(parent.decode('utf8'))
1576
1173
            return urlutils.join(self.base[:-1], parent)
1577
 
        except errors.InvalidURLJoin, e:
1578
 
            raise errors.InaccessibleParent(parent, self.base)
 
1174
        return None
 
1175
 
 
1176
    def get_push_location(self):
 
1177
        """See Branch.get_push_location."""
 
1178
        push_loc = self.get_config().get_user_option('push_location')
 
1179
        return push_loc
1579
1180
 
1580
1181
    def set_push_location(self, location):
1581
1182
        """See Branch.set_push_location."""
1582
 
        self.get_config().set_user_option(
1583
 
            'push_location', location,
1584
 
            store=_mod_config.STORE_LOCATION_NORECURSE)
 
1183
        self.get_config().set_user_option('push_location', location, 
 
1184
                                          local=True)
1585
1185
 
1586
1186
    @needs_write_lock
1587
1187
    def set_parent(self, url):
1591
1191
        # FIXUP this and get_parent in a future branch format bump:
1592
1192
        # read and rewrite the file, and have the new format code read
1593
1193
        # using .get not .get_utf8. RBC 20060125
1594
 
        if url is not None:
 
1194
        if url is None:
 
1195
            self.control_files._transport.delete('parent')
 
1196
        else:
1595
1197
            if isinstance(url, unicode):
1596
1198
                try: 
1597
1199
                    url = url.encode('ascii')
1598
1200
                except UnicodeEncodeError:
1599
 
                    raise errors.InvalidURL(url,
 
1201
                    raise bzrlib.errors.InvalidURL(url,
1600
1202
                        "Urls must be 7-bit ascii, "
1601
1203
                        "use bzrlib.urlutils.escape")
 
1204
                    
1602
1205
            url = urlutils.relative_url(self.base, url)
1603
 
        self._set_parent_location(url)
1604
 
 
1605
 
    def _set_parent_location(self, url):
1606
 
        if url is None:
1607
 
            self.control_files._transport.delete('parent')
1608
 
        else:
1609
 
            assert isinstance(url, str)
1610
 
            self.control_files.put_bytes('parent', url + '\n')
 
1206
            self.control_files.put('parent', url + '\n')
1611
1207
 
1612
1208
    @deprecated_function(zero_nine)
1613
1209
    def tree_config(self):
1633
1229
                                         _repository=_repository)
1634
1230
        
1635
1231
    @needs_write_lock
1636
 
    def pull(self, source, overwrite=False, stop_revision=None,
1637
 
        _run_hooks=True):
1638
 
        """Extends branch.pull to be bound branch aware.
1639
 
        
1640
 
        :param _run_hooks: Private parameter used to force hook running
1641
 
            off during bound branch double-pushing.
1642
 
        """
 
1232
    def pull(self, source, overwrite=False, stop_revision=None):
 
1233
        """Updates branch.pull to be bound branch aware."""
1643
1234
        bound_location = self.get_bound_location()
1644
 
        master_branch = None
1645
 
        if bound_location and source.base != bound_location:
 
1235
        if source.base != bound_location:
1646
1236
            # not pulling from master, so we need to update master.
1647
1237
            master_branch = self.get_master_branch()
1648
 
            master_branch.lock_write()
1649
 
        try:
1650
 
            if master_branch:
1651
 
                # pull from source into master.
1652
 
                master_branch.pull(source, overwrite, stop_revision,
1653
 
                    _run_hooks=False)
1654
 
            return super(BzrBranch5, self).pull(source, overwrite,
1655
 
                stop_revision, _hook_master=master_branch,
1656
 
                _run_hooks=_run_hooks)
1657
 
        finally:
1658
 
            if master_branch:
1659
 
                master_branch.unlock()
1660
 
 
1661
 
    @needs_read_lock
1662
 
    def push(self, target, overwrite=False, stop_revision=None):
1663
 
        """Updates branch.push to be bound branch aware."""
1664
 
        bound_location = target.get_bound_location()
1665
 
        master_branch = None
1666
 
        if bound_location and target.base != bound_location:
1667
 
            # not pushing to master, so we need to update master.
1668
 
            master_branch = target.get_master_branch()
1669
 
            master_branch.lock_write()
1670
 
        try:
1671
 
            if master_branch:
1672
 
                # push into the master from this branch.
1673
 
                super(BzrBranch5, self).push(master_branch, overwrite,
1674
 
                    stop_revision, _run_hooks=False)
1675
 
            # and push into the target branch from this. Note that we push from
1676
 
            # this branch again, because its considered the highest bandwidth
1677
 
            # repository.
1678
 
            return super(BzrBranch5, self).push(target, overwrite,
1679
 
                stop_revision, _hook_master=master_branch)
1680
 
        finally:
1681
 
            if master_branch:
1682
 
                master_branch.unlock()
 
1238
            if master_branch:
 
1239
                master_branch.pull(source)
 
1240
                source = master_branch
 
1241
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
1683
1242
 
1684
1243
    def get_bound_location(self):
1685
1244
        try:
1724
1283
 
1725
1284
    @needs_write_lock
1726
1285
    def bind(self, other):
1727
 
        """Bind this branch to the branch other.
 
1286
        """Bind the local branch the other branch.
1728
1287
 
1729
 
        This does not push or pull data between the branches, though it does
1730
 
        check for divergence to raise an error when the branches are not
1731
 
        either the same, or one a prefix of the other. That behaviour may not
1732
 
        be useful, so that check may be removed in future.
1733
 
        
1734
1288
        :param other: The branch to bind to
1735
1289
        :type other: Branch
1736
1290
        """
1741
1295
        #       but binding itself may not be.
1742
1296
        #       Since we *have* to check at commit time, we don't
1743
1297
        #       *need* to check here
1744
 
 
1745
 
        # we want to raise diverged if:
1746
 
        # last_rev is not in the other_last_rev history, AND
1747
 
        # other_last_rev is not in our history, and do it without pulling
1748
 
        # history around
1749
 
        last_rev = self.last_revision()
1750
 
        if last_rev is not None:
1751
 
            other.lock_read()
1752
 
            try:
1753
 
                other_last_rev = other.last_revision()
1754
 
                if other_last_rev is not None:
1755
 
                    # neither branch is new, we have to do some work to
1756
 
                    # ascertain diversion.
1757
 
                    remote_graph = other.repository.get_revision_graph(
1758
 
                        other_last_rev)
1759
 
                    local_graph = self.repository.get_revision_graph(last_rev)
1760
 
                    if (last_rev not in remote_graph and
1761
 
                        other_last_rev not in local_graph):
1762
 
                        raise errors.DivergedBranches(self, other)
1763
 
            finally:
1764
 
                other.unlock()
 
1298
        self.pull(other)
 
1299
 
 
1300
        # we are now equal to or a suffix of other.
 
1301
 
 
1302
        # Since we have 'pulled' from the remote location,
 
1303
        # now we should try to pull in the opposite direction
 
1304
        # in case the local tree has more revisions than the
 
1305
        # remote one.
 
1306
        # There may be a different check you could do here
 
1307
        # rather than actually trying to install revisions remotely.
 
1308
        # TODO: capture an exception which indicates the remote branch
 
1309
        #       is not writable. 
 
1310
        #       If it is up-to-date, this probably should not be a failure
 
1311
        
 
1312
        # lock other for write so the revision-history syncing cannot race
 
1313
        other.lock_write()
 
1314
        try:
 
1315
            other.pull(self)
 
1316
            # if this does not error, other now has the same last rev we do
 
1317
            # it can only error if the pull from other was concurrent with
 
1318
            # a commit to other from someone else.
 
1319
 
 
1320
            # until we ditch revision-history, we need to sync them up:
 
1321
            self.set_revision_history(other.revision_history())
 
1322
            # now other and self are up to date with each other and have the
 
1323
            # same revision-history.
 
1324
        finally:
 
1325
            other.unlock()
 
1326
 
1765
1327
        self.set_bound_location(other.base)
1766
1328
 
1767
1329
    @needs_write_lock
1786
1348
        return None
1787
1349
 
1788
1350
 
1789
 
class BzrBranchExperimental(BzrBranch5):
1790
 
    """Bzr experimental branch format
1791
 
 
1792
 
    This format has:
1793
 
     - a revision-history file.
1794
 
     - a format string
1795
 
     - a lock dir guarding the branch itself
1796
 
     - all of this stored in a branch/ subdirectory
1797
 
     - works with shared repositories.
1798
 
     - a tag dictionary in the branch
1799
 
 
1800
 
    This format is new in bzr 0.15, but shouldn't be used for real data, 
1801
 
    only for testing.
1802
 
 
1803
 
    This class acts as it's own BranchFormat.
1804
 
    """
1805
 
 
1806
 
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1807
 
 
1808
 
    @classmethod
1809
 
    def get_format_string(cls):
1810
 
        """See BranchFormat.get_format_string()."""
1811
 
        return "Bazaar-NG branch format experimental\n"
1812
 
 
1813
 
    @classmethod
1814
 
    def get_format_description(cls):
1815
 
        """See BranchFormat.get_format_description()."""
1816
 
        return "Experimental branch format"
1817
 
 
1818
 
    @classmethod
1819
 
    def get_reference(cls, a_bzrdir):
1820
 
        """Get the target reference of the branch in a_bzrdir.
1821
 
 
1822
 
        format probing must have been completed before calling
1823
 
        this method - it is assumed that the format of the branch
1824
 
        in a_bzrdir is correct.
1825
 
 
1826
 
        :param a_bzrdir: The bzrdir to get the branch data from.
1827
 
        :return: None if the branch is not a reference branch.
1828
 
        """
1829
 
        return None
1830
 
 
1831
 
    @classmethod
1832
 
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1833
 
            lock_class):
1834
 
        branch_transport = a_bzrdir.get_branch_transport(cls)
1835
 
        control_files = lockable_files.LockableFiles(branch_transport,
1836
 
            lock_filename, lock_class)
1837
 
        control_files.create_lock()
1838
 
        control_files.lock_write()
1839
 
        try:
1840
 
            for filename, content in utf8_files:
1841
 
                control_files.put_utf8(filename, content)
1842
 
        finally:
1843
 
            control_files.unlock()
1844
 
        
1845
 
    @classmethod
1846
 
    def initialize(cls, a_bzrdir):
1847
 
        """Create a branch of this format in a_bzrdir."""
1848
 
        utf8_files = [('format', cls.get_format_string()),
1849
 
                      ('revision-history', ''),
1850
 
                      ('branch-name', ''),
1851
 
                      ('tags', ''),
1852
 
                      ]
1853
 
        cls._initialize_control_files(a_bzrdir, utf8_files,
1854
 
            'lock', lockdir.LockDir)
1855
 
        return cls.open(a_bzrdir, _found=True)
1856
 
 
1857
 
    @classmethod
1858
 
    def open(cls, a_bzrdir, _found=False):
1859
 
        """Return the branch object for a_bzrdir
1860
 
 
1861
 
        _found is a private parameter, do not use it. It is used to indicate
1862
 
               if format probing has already be done.
1863
 
        """
1864
 
        if not _found:
1865
 
            format = BranchFormat.find_format(a_bzrdir)
1866
 
            assert format.__class__ == cls
1867
 
        transport = a_bzrdir.get_branch_transport(None)
1868
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1869
 
                                                     lockdir.LockDir)
1870
 
        return cls(_format=cls,
1871
 
            _control_files=control_files,
1872
 
            a_bzrdir=a_bzrdir,
1873
 
            _repository=a_bzrdir.find_repository())
1874
 
 
1875
 
    @classmethod
1876
 
    def is_supported(cls):
1877
 
        return True
1878
 
 
1879
 
    def _make_tags(self):
1880
 
        return BasicTags(self)
1881
 
 
1882
 
    @classmethod
1883
 
    def supports_tags(cls):
1884
 
        return True
1885
 
 
1886
 
 
1887
 
BranchFormat.register_format(BzrBranchExperimental)
1888
 
 
1889
 
 
1890
 
class BzrBranch6(BzrBranch5):
1891
 
 
1892
 
    @needs_read_lock
1893
 
    def last_revision_info(self):
1894
 
        revision_string = self.control_files.get('last-revision').read()
1895
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1896
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
1897
 
        revno = int(revno)
1898
 
        return revno, revision_id
1899
 
 
1900
 
    def last_revision(self):
1901
 
        """Return last revision id, or None"""
1902
 
        revision_id = self.last_revision_info()[1]
1903
 
        if revision_id == _mod_revision.NULL_REVISION:
1904
 
            revision_id = None
1905
 
        return revision_id
1906
 
 
1907
 
    def _write_last_revision_info(self, revno, revision_id):
1908
 
        """Simply write out the revision id, with no checks.
1909
 
 
1910
 
        Use set_last_revision_info to perform this safely.
1911
 
 
1912
 
        Does not update the revision_history cache.
1913
 
        Intended to be called by set_last_revision_info and
1914
 
        _write_revision_history.
1915
 
        """
1916
 
        if revision_id is None:
1917
 
            revision_id = 'null:'
1918
 
        out_string = '%d %s\n' % (revno, revision_id)
1919
 
        self.control_files.put_bytes('last-revision', out_string)
1920
 
 
1921
 
    @needs_write_lock
1922
 
    def set_last_revision_info(self, revno, revision_id):
1923
 
        revision_id = osutils.safe_revision_id(revision_id)
1924
 
        if self._get_append_revisions_only():
1925
 
            self._check_history_violation(revision_id)
1926
 
        self._write_last_revision_info(revno, revision_id)
1927
 
        self._clear_cached_state()
1928
 
 
1929
 
    def _check_history_violation(self, revision_id):
1930
 
        last_revision = self.last_revision()
1931
 
        if last_revision is None:
1932
 
            return
1933
 
        if last_revision not in self._lefthand_history(revision_id):
1934
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
1935
 
 
1936
 
    def _gen_revision_history(self):
1937
 
        """Generate the revision history from last revision
1938
 
        """
1939
 
        history = list(self.repository.iter_reverse_revision_history(
1940
 
            self.last_revision()))
1941
 
        history.reverse()
1942
 
        return history
1943
 
 
1944
 
    def _write_revision_history(self, history):
1945
 
        """Factored out of set_revision_history.
1946
 
 
1947
 
        This performs the actual writing to disk, with format-specific checks.
1948
 
        It is intended to be called by BzrBranch5.set_revision_history.
1949
 
        """
1950
 
        if len(history) == 0:
1951
 
            last_revision = 'null:'
1952
 
        else:
1953
 
            if history != self._lefthand_history(history[-1]):
1954
 
                raise errors.NotLefthandHistory(history)
1955
 
            last_revision = history[-1]
1956
 
        if self._get_append_revisions_only():
1957
 
            self._check_history_violation(last_revision)
1958
 
        self._write_last_revision_info(len(history), last_revision)
1959
 
 
1960
 
    @needs_write_lock
1961
 
    def append_revision(self, *revision_ids):
1962
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1963
 
        if len(revision_ids) == 0:
1964
 
            return
1965
 
        prev_revno, prev_revision = self.last_revision_info()
1966
 
        for revision in self.repository.get_revisions(revision_ids):
1967
 
            if prev_revision == _mod_revision.NULL_REVISION:
1968
 
                if revision.parent_ids != []:
1969
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
1970
 
                                                         revision.revision_id)
1971
 
            else:
1972
 
                if revision.parent_ids[0] != prev_revision:
1973
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
1974
 
                                                         revision.revision_id)
1975
 
            prev_revision = revision.revision_id
1976
 
        self.set_last_revision_info(prev_revno + len(revision_ids),
1977
 
                                    revision_ids[-1])
1978
 
 
1979
 
    @needs_write_lock
1980
 
    def _set_parent_location(self, url):
1981
 
        """Set the parent branch"""
1982
 
        self._set_config_location('parent_location', url, make_relative=True)
1983
 
 
1984
 
    @needs_read_lock
1985
 
    def _get_parent_location(self):
1986
 
        """Set the parent branch"""
1987
 
        return self._get_config_location('parent_location')
1988
 
 
1989
 
    def set_push_location(self, location):
1990
 
        """See Branch.set_push_location."""
1991
 
        self._set_config_location('push_location', location)
1992
 
 
1993
 
    def set_bound_location(self, location):
1994
 
        """See Branch.set_push_location."""
1995
 
        result = None
1996
 
        config = self.get_config()
1997
 
        if location is None:
1998
 
            if config.get_user_option('bound') != 'True':
1999
 
                return False
2000
 
            else:
2001
 
                config.set_user_option('bound', 'False')
2002
 
                return True
2003
 
        else:
2004
 
            self._set_config_location('bound_location', location,
2005
 
                                      config=config)
2006
 
            config.set_user_option('bound', 'True')
2007
 
        return True
2008
 
 
2009
 
    def _get_bound_location(self, bound):
2010
 
        """Return the bound location in the config file.
2011
 
 
2012
 
        Return None if the bound parameter does not match"""
2013
 
        config = self.get_config()
2014
 
        config_bound = (config.get_user_option('bound') == 'True')
2015
 
        if config_bound != bound:
2016
 
            return None
2017
 
        return self._get_config_location('bound_location', config=config)
2018
 
 
2019
 
    def get_bound_location(self):
2020
 
        """See Branch.set_push_location."""
2021
 
        return self._get_bound_location(True)
2022
 
 
2023
 
    def get_old_bound_location(self):
2024
 
        """See Branch.get_old_bound_location"""
2025
 
        return self._get_bound_location(False)
2026
 
 
2027
 
    def set_append_revisions_only(self, enabled):
2028
 
        if enabled:
2029
 
            value = 'True'
2030
 
        else:
2031
 
            value = 'False'
2032
 
        self.get_config().set_user_option('append_revisions_only', value)
2033
 
 
2034
 
    def _get_append_revisions_only(self):
2035
 
        value = self.get_config().get_user_option('append_revisions_only')
2036
 
        return value == 'True'
2037
 
 
2038
 
    def _synchronize_history(self, destination, revision_id):
2039
 
        """Synchronize last revision and revision history between branches.
2040
 
 
2041
 
        This version is most efficient when the destination is also a
2042
 
        BzrBranch6, but works for BzrBranch5, as long as the destination's
2043
 
        repository contains all the lefthand ancestors of the intended
2044
 
        last_revision.  If not, set_last_revision_info will fail.
2045
 
 
2046
 
        :param destination: The branch to copy the history into
2047
 
        :param revision_id: The revision-id to truncate history at.  May
2048
 
          be None to copy complete history.
2049
 
        """
2050
 
        if revision_id is None:
2051
 
            revno, revision_id = self.last_revision_info()
2052
 
        else:
2053
 
            revno = self.revision_id_to_revno(revision_id)
2054
 
        destination.set_last_revision_info(revno, revision_id)
2055
 
 
2056
 
    def _make_tags(self):
2057
 
        return BasicTags(self)
2058
 
 
2059
 
 
2060
1351
class BranchTestProviderAdapter(object):
2061
1352
    """A tool to generate a suite testing multiple branch formats at once.
2062
1353
 
2066
1357
    easy to identify.
2067
1358
    """
2068
1359
 
2069
 
    def __init__(self, transport_server, transport_readonly_server, formats,
2070
 
        vfs_transport_factory=None):
 
1360
    def __init__(self, transport_server, transport_readonly_server, formats):
2071
1361
        self._transport_server = transport_server
2072
1362
        self._transport_readonly_server = transport_readonly_server
2073
1363
        self._formats = formats
2081
1371
            new_test.bzrdir_format = bzrdir_format
2082
1372
            new_test.branch_format = branch_format
2083
1373
            def make_new_test_id():
2084
 
                # the format can be either a class or an instance
2085
 
                name = getattr(branch_format, '__name__',
2086
 
                        branch_format.__class__.__name__)
2087
 
                new_id = "%s(%s)" % (new_test.id(), name)
 
1374
                new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
2088
1375
                return lambda: new_id
2089
1376
            new_test.id = make_new_test_id()
2090
1377
            result.addTest(new_test)
2091
1378
        return result
2092
1379
 
2093
1380
 
2094
 
######################################################################
2095
 
# results of operations
2096
 
 
2097
 
 
2098
 
class _Result(object):
2099
 
 
2100
 
    def _show_tag_conficts(self, to_file):
2101
 
        if not getattr(self, 'tag_conflicts', None):
2102
 
            return
2103
 
        to_file.write('Conflicting tags:\n')
2104
 
        for name, value1, value2 in self.tag_conflicts:
2105
 
            to_file.write('    %s\n' % (name, ))
2106
 
 
2107
 
 
2108
 
class PullResult(_Result):
2109
 
    """Result of a Branch.pull operation.
2110
 
 
2111
 
    :ivar old_revno: Revision number before pull.
2112
 
    :ivar new_revno: Revision number after pull.
2113
 
    :ivar old_revid: Tip revision id before pull.
2114
 
    :ivar new_revid: Tip revision id after pull.
2115
 
    :ivar source_branch: Source (local) branch object.
2116
 
    :ivar master_branch: Master branch of the target, or None.
2117
 
    :ivar target_branch: Target/destination branch object.
2118
 
    """
2119
 
 
2120
 
    def __int__(self):
2121
 
        # DEPRECATED: pull used to return the change in revno
2122
 
        return self.new_revno - self.old_revno
2123
 
 
2124
 
    def report(self, to_file):
2125
 
        if self.old_revid == self.new_revid:
2126
 
            to_file.write('No revisions to pull.\n')
2127
 
        else:
2128
 
            to_file.write('Now on revision %d.\n' % self.new_revno)
2129
 
        self._show_tag_conficts(to_file)
2130
 
 
2131
 
 
2132
 
class PushResult(_Result):
2133
 
    """Result of a Branch.push operation.
2134
 
 
2135
 
    :ivar old_revno: Revision number before push.
2136
 
    :ivar new_revno: Revision number after push.
2137
 
    :ivar old_revid: Tip revision id before push.
2138
 
    :ivar new_revid: Tip revision id after push.
2139
 
    :ivar source_branch: Source branch object.
2140
 
    :ivar master_branch: Master branch of the target, or None.
2141
 
    :ivar target_branch: Target/destination branch object.
2142
 
    """
2143
 
 
2144
 
    def __int__(self):
2145
 
        # DEPRECATED: push used to return the change in revno
2146
 
        return self.new_revno - self.old_revno
2147
 
 
2148
 
    def report(self, to_file):
2149
 
        """Write a human-readable description of the result."""
2150
 
        if self.old_revid == self.new_revid:
2151
 
            to_file.write('No new revisions to push.\n')
2152
 
        else:
2153
 
            to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2154
 
        self._show_tag_conficts(to_file)
2155
 
 
2156
 
 
2157
1381
class BranchCheckResult(object):
2158
1382
    """Results of checking branch consistency.
2159
1383
 
2174
1398
             self.branch._format)
2175
1399
 
2176
1400
 
2177
 
class Converter5to6(object):
2178
 
    """Perform an in-place upgrade of format 5 to format 6"""
2179
 
 
2180
 
    def convert(self, branch):
2181
 
        # Data for 5 and 6 can peacefully coexist.
2182
 
        format = BzrBranchFormat6()
2183
 
        new_branch = format.open(branch.bzrdir, _found=True)
2184
 
 
2185
 
        # Copy source data into target
2186
 
        new_branch.set_last_revision_info(*branch.last_revision_info())
2187
 
        new_branch.set_parent(branch.get_parent())
2188
 
        new_branch.set_bound_location(branch.get_bound_location())
2189
 
        new_branch.set_push_location(branch.get_push_location())
2190
 
 
2191
 
        # New branch has no tags by default
2192
 
        new_branch.tags._set_tag_dict({})
2193
 
 
2194
 
        # Copying done; now update target format
2195
 
        new_branch.control_files.put_utf8('format',
2196
 
            format.get_format_string())
2197
 
 
2198
 
        # Clean up old files
2199
 
        new_branch.control_files._transport.delete('revision-history')
2200
 
        try:
2201
 
            branch.set_parent(None)
2202
 
        except NoSuchFile:
2203
 
            pass
2204
 
        branch.set_bound_location(None)
 
1401
######################################################################
 
1402
# predicates
 
1403
 
 
1404
 
 
1405
@deprecated_function(zero_eight)
 
1406
def is_control_file(*args, **kwargs):
 
1407
    """See bzrlib.workingtree.is_control_file."""
 
1408
    return bzrlib.workingtree.is_control_file(*args, **kwargs)