~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-05-05 08:29:29 UTC
  • mfrom: (1697.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060505082929-a037ee137f1ff240
Merge break-lock command.

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(), """
 
20
import errno
 
21
import os
 
22
import shutil
 
23
import sys
 
24
from unittest import TestSuite
22
25
from warnings import warn
23
26
 
24
27
import bzrlib
25
 
from bzrlib import (
26
 
        bzrdir,
27
 
        cache_utf8,
28
 
        config as _mod_config,
29
 
        errors,
30
 
        lockdir,
31
 
        lockable_files,
32
 
        osutils,
33
 
        revision as _mod_revision,
34
 
        transport,
35
 
        tree,
36
 
        tsort,
37
 
        ui,
38
 
        urlutils,
39
 
        )
40
 
from bzrlib.config import BranchConfig, TreeConfig
 
28
import bzrlib.bzrdir as bzrdir
 
29
from bzrlib.config import TreeConfig
 
30
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
31
from bzrlib.delta import compare_trees
 
32
import bzrlib.errors as errors
 
33
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
 
34
                           NoSuchRevision, HistoryMissing, NotBranchError,
 
35
                           DivergedBranches, LockError,
 
36
                           UninitializableFormat,
 
37
                           UnlistableStore,
 
38
                           UnlistableBranch, NoSuchFile, NotVersionedError,
 
39
                           NoWorkingTree)
 
40
import bzrlib.inventory as inventory
 
41
from bzrlib.inventory import Inventory
41
42
from bzrlib.lockable_files import LockableFiles, TransportLock
42
 
from bzrlib.tag import (
43
 
    BasicTags,
44
 
    DisabledTags,
45
 
    )
46
 
""")
47
 
 
48
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
49
 
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
50
 
                           HistoryMissing, InvalidRevisionId,
51
 
                           InvalidRevisionNumber, LockError, NoSuchFile,
52
 
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
53
 
                           NotBranchError, UninitializableFormat,
54
 
                           UnlistableStore, UnlistableBranch,
55
 
                           )
56
 
from bzrlib.hooks import Hooks
57
 
from bzrlib.symbol_versioning import (deprecated_function,
58
 
                                      deprecated_method,
59
 
                                      DEPRECATED_PARAMETER,
60
 
                                      deprecated_passed,
61
 
                                      zero_eight, zero_nine, zero_sixteen,
62
 
                                      )
 
43
from bzrlib.lockdir import LockDir
 
44
from bzrlib.osutils import (isdir, quotefn,
 
45
                            rename, splitpath, sha_file,
 
46
                            file_kind, abspath, normpath, pathjoin,
 
47
                            safe_unicode,
 
48
                            rmtree,
 
49
                            )
 
50
from bzrlib.textui import show_status
63
51
from bzrlib.trace import mutter, note
 
52
from bzrlib.tree import EmptyTree, RevisionTree
 
53
from bzrlib.repository import Repository
 
54
from bzrlib.revision import (
 
55
                             get_intervening_revisions,
 
56
                             is_ancestor,
 
57
                             NULL_REVISION,
 
58
                             Revision,
 
59
                             )
 
60
from bzrlib.store import copy_all
 
61
from bzrlib.symbol_versioning import *
 
62
import bzrlib.transactions as transactions
 
63
from bzrlib.transport import Transport, get_transport
 
64
from bzrlib.tree import EmptyTree, RevisionTree
 
65
import bzrlib.ui
 
66
import bzrlib.xml5
64
67
 
65
68
 
66
69
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
67
70
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
68
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
71
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
69
72
 
70
73
 
71
74
# TODO: Maybe include checks for common corruption of newlines, etc?
85
88
 
86
89
    base
87
90
        Base directory/url of the branch.
88
 
 
89
 
    hooks: An instance of BranchHooks.
90
91
    """
91
92
    # this is really an instance variable - FIXME move it there
92
93
    # - RBC 20060112
93
94
    base = None
94
95
 
95
 
    # override this to set the strategy for storing tags
96
 
    def _make_tags(self):
97
 
        return DisabledTags(self)
98
 
 
99
96
    def __init__(self, *ignored, **ignored_too):
100
 
        self.tags = self._make_tags()
101
 
        self._revision_history_cache = None
102
 
        self._revision_id_to_revno_cache = None
 
97
        raise NotImplementedError('The Branch class is abstract')
103
98
 
104
99
    def break_lock(self):
105
100
        """Break a lock if one is present from another instance.
123
118
        
124
119
    @staticmethod
125
120
    def open(base, _unsupported=False):
126
 
        """Open the branch rooted at base.
 
121
        """Open the repository rooted at base.
127
122
 
128
 
        For instance, if the branch is at URL/.bzr/branch,
129
 
        Branch.open(URL) -> a Branch instance.
 
123
        For instance, if the repository is at URL/.bzr/repository,
 
124
        Repository.open(URL) -> a Repository instance.
130
125
        """
131
126
        control = bzrdir.BzrDir.open(base, _unsupported)
132
127
        return control.open_branch(_unsupported)
156
151
        """
157
152
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
158
153
 
159
 
    @deprecated_function(zero_eight)
160
154
    def setup_caching(self, cache_root):
161
155
        """Subclasses that care about caching should override this, and set
162
156
        up cached stores located under cache_root.
163
 
        
164
 
        NOTE: This is unused.
165
157
        """
166
 
        pass
167
 
 
168
 
    def get_config(self):
169
 
        return BranchConfig(self)
 
158
        # seems to be unused, 2006-01-13 mbp
 
159
        warn('%s is deprecated' % self.setup_caching)
 
160
        self.cache_root = cache_root
170
161
 
171
162
    def _get_nick(self):
172
 
        return self.get_config().get_nickname()
 
163
        cfg = self.tree_config()
 
164
        return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
173
165
 
174
166
    def _set_nick(self, nick):
175
 
        self.get_config().set_user_option('nickname', nick, warn_masked=True)
 
167
        cfg = self.tree_config()
 
168
        cfg.set_option(nick, "nickname")
 
169
        assert cfg.get_option("nickname") == nick
176
170
 
177
171
    nick = property(_get_nick, _set_nick)
178
172
 
179
173
    def is_locked(self):
180
 
        raise NotImplementedError(self.is_locked)
 
174
        raise NotImplementedError('is_locked is abstract')
181
175
 
182
176
    def lock_write(self):
183
 
        raise NotImplementedError(self.lock_write)
 
177
        raise NotImplementedError('lock_write is abstract')
184
178
 
185
179
    def lock_read(self):
186
 
        raise NotImplementedError(self.lock_read)
 
180
        raise NotImplementedError('lock_read is abstract')
187
181
 
188
182
    def unlock(self):
189
 
        raise NotImplementedError(self.unlock)
 
183
        raise NotImplementedError('unlock is abstract')
190
184
 
191
185
    def peek_lock_mode(self):
192
186
        """Return lock mode for the Branch: 'r', 'w' or None"""
193
187
        raise NotImplementedError(self.peek_lock_mode)
194
188
 
195
189
    def get_physical_lock_status(self):
196
 
        raise NotImplementedError(self.get_physical_lock_status)
197
 
 
198
 
    @needs_read_lock
199
 
    def get_revision_id_to_revno_map(self):
200
 
        """Return the revision_id => dotted revno map.
201
 
 
202
 
        This will be regenerated on demand, but will be cached.
203
 
 
204
 
        :return: A dictionary mapping revision_id => dotted revno.
205
 
            This dictionary should not be modified by the caller.
206
 
        """
207
 
        if self._revision_id_to_revno_cache is not None:
208
 
            mapping = self._revision_id_to_revno_cache
209
 
        else:
210
 
            mapping = self._gen_revno_map()
211
 
            self._cache_revision_id_to_revno(mapping)
212
 
        # TODO: jam 20070417 Since this is being cached, should we be returning
213
 
        #       a copy?
214
 
        # I would rather not, and instead just declare that users should not
215
 
        # modify the return value.
216
 
        return mapping
217
 
 
218
 
    def _gen_revno_map(self):
219
 
        """Create a new mapping from revision ids to dotted revnos.
220
 
 
221
 
        Dotted revnos are generated based on the current tip in the revision
222
 
        history.
223
 
        This is the worker function for get_revision_id_to_revno_map, which
224
 
        just caches the return value.
225
 
 
226
 
        :return: A dictionary mapping revision_id => dotted revno.
227
 
        """
228
 
        last_revision = self.last_revision()
229
 
        revision_graph = self.repository.get_revision_graph(last_revision)
230
 
        merge_sorted_revisions = tsort.merge_sort(
231
 
            revision_graph,
232
 
            last_revision,
233
 
            None,
234
 
            generate_revno=True)
235
 
        revision_id_to_revno = dict((rev_id, revno)
236
 
                                    for seq_num, rev_id, depth, revno, end_of_merge
237
 
                                     in merge_sorted_revisions)
238
 
        return revision_id_to_revno
239
 
 
240
 
    def leave_lock_in_place(self):
241
 
        """Tell this branch object not to release the physical lock when this
242
 
        object is unlocked.
243
 
        
244
 
        If lock_write doesn't return a token, then this method is not supported.
245
 
        """
246
 
        self.control_files.leave_in_place()
247
 
 
248
 
    def dont_leave_lock_in_place(self):
249
 
        """Tell this branch object to release the physical lock when this
250
 
        object is unlocked, even if it didn't originally acquire it.
251
 
 
252
 
        If lock_write doesn't return a token, then this method is not supported.
253
 
        """
254
 
        self.control_files.dont_leave_in_place()
 
190
        raise NotImplementedError('get_physical_lock_status is abstract')
255
191
 
256
192
    def abspath(self, name):
257
193
        """Return absolute filename for something in the branch
259
195
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
260
196
        method and not a tree method.
261
197
        """
262
 
        raise NotImplementedError(self.abspath)
 
198
        raise NotImplementedError('abspath is abstract')
263
199
 
264
200
    def bind(self, other):
265
201
        """Bind the local branch the other branch.
284
220
        if self.base == from_branch.base:
285
221
            return (0, [])
286
222
        if pb is None:
287
 
            nested_pb = ui.ui_factory.nested_progress_bar()
 
223
            nested_pb = bzrlib.ui.ui_factory.nested_progress_bar()
288
224
            pb = nested_pb
289
225
        else:
290
226
            nested_pb = None
293
229
        try:
294
230
            if last_revision is None:
295
231
                pb.update('get source history')
296
 
                last_revision = from_branch.last_revision()
297
 
                if last_revision is None:
298
 
                    last_revision = _mod_revision.NULL_REVISION
 
232
                from_history = from_branch.revision_history()
 
233
                if from_history:
 
234
                    last_revision = from_history[-1]
 
235
                else:
 
236
                    # no history in the source branch
 
237
                    last_revision = NULL_REVISION
299
238
            return self.repository.fetch(from_branch.repository,
300
239
                                         revision_id=last_revision,
301
240
                                         pb=nested_pb)
311
250
        branch.
312
251
        """
313
252
        return None
314
 
    
315
 
    def get_old_bound_location(self):
316
 
        """Return the URL of the branch we used to be bound to
317
 
        """
318
 
        raise errors.UpgradeRequired(self.base)
319
 
 
320
 
    def get_commit_builder(self, parents, config=None, timestamp=None, 
321
 
                           timezone=None, committer=None, revprops=None, 
322
 
                           revision_id=None):
323
 
        """Obtain a CommitBuilder for this branch.
324
 
        
325
 
        :param parents: Revision ids of the parents of the new revision.
326
 
        :param config: Optional configuration to use.
327
 
        :param timestamp: Optional timestamp recorded for commit.
328
 
        :param timezone: Optional timezone for timestamp.
329
 
        :param committer: Optional committer to set for commit.
330
 
        :param revprops: Optional dictionary of revision properties.
331
 
        :param revision_id: Optional revision id.
332
 
        """
333
 
 
334
 
        if config is None:
335
 
            config = self.get_config()
336
 
        
337
 
        return self.repository.get_commit_builder(self, parents, config,
338
 
            timestamp, timezone, committer, revprops, revision_id)
339
253
 
340
254
    def get_master_branch(self):
341
255
        """Return the branch we are bound to.
344
258
        """
345
259
        return None
346
260
 
347
 
    def get_revision_delta(self, revno):
348
 
        """Return the delta for one revision.
349
 
 
350
 
        The delta is relative to its mainline predecessor, or the
351
 
        empty tree for revision 1.
352
 
        """
353
 
        assert isinstance(revno, int)
354
 
        rh = self.revision_history()
355
 
        if not (1 <= revno <= len(rh)):
356
 
            raise InvalidRevisionNumber(revno)
357
 
        return self.repository.get_revision_delta(rh[revno-1])
358
 
 
359
 
    @deprecated_method(zero_sixteen)
360
261
    def get_root_id(self):
361
 
        """Return the id of this branches root
362
 
 
363
 
        Deprecated: branches don't have root ids-- trees do.
364
 
        Use basis_tree().get_root_id() instead.
365
 
        """
366
 
        raise NotImplementedError(self.get_root_id)
 
262
        """Return the id of this branches root"""
 
263
        raise NotImplementedError('get_root_id is abstract')
367
264
 
368
265
    def print_file(self, file, revision_id):
369
266
        """Print `file` to stdout."""
370
 
        raise NotImplementedError(self.print_file)
 
267
        raise NotImplementedError('print_file is abstract')
371
268
 
372
269
    def append_revision(self, *revision_ids):
373
 
        raise NotImplementedError(self.append_revision)
 
270
        raise NotImplementedError('append_revision is abstract')
374
271
 
375
272
    def set_revision_history(self, rev_history):
376
 
        raise NotImplementedError(self.set_revision_history)
377
 
 
378
 
    def _cache_revision_history(self, rev_history):
379
 
        """Set the cached revision history to rev_history.
380
 
 
381
 
        The revision_history method will use this cache to avoid regenerating
382
 
        the revision history.
383
 
 
384
 
        This API is semi-public; it only for use by subclasses, all other code
385
 
        should consider it to be private.
386
 
        """
387
 
        self._revision_history_cache = rev_history
388
 
 
389
 
    def _cache_revision_id_to_revno(self, revision_id_to_revno):
390
 
        """Set the cached revision_id => revno map to revision_id_to_revno.
391
 
 
392
 
        This API is semi-public; it only for use by subclasses, all other code
393
 
        should consider it to be private.
394
 
        """
395
 
        self._revision_id_to_revno_cache = revision_id_to_revno
396
 
 
397
 
    def _clear_cached_state(self):
398
 
        """Clear any cached data on this branch, e.g. cached revision history.
399
 
 
400
 
        This means the next call to revision_history will need to call
401
 
        _gen_revision_history.
402
 
 
403
 
        This API is semi-public; it only for use by subclasses, all other code
404
 
        should consider it to be private.
405
 
        """
406
 
        self._revision_history_cache = None
407
 
        self._revision_id_to_revno_cache = None
408
 
 
409
 
    def _gen_revision_history(self):
410
 
        """Return sequence of revision hashes on to this branch.
411
 
        
412
 
        Unlike revision_history, this method always regenerates or rereads the
413
 
        revision history, i.e. it does not cache the result, so repeated calls
414
 
        may be expensive.
415
 
 
416
 
        Concrete subclasses should override this instead of revision_history so
417
 
        that subclasses do not need to deal with caching logic.
418
 
        
419
 
        This API is semi-public; it only for use by subclasses, all other code
420
 
        should consider it to be private.
421
 
        """
422
 
        raise NotImplementedError(self._gen_revision_history)
423
 
 
424
 
    @needs_read_lock
 
273
        raise NotImplementedError('set_revision_history is abstract')
 
274
 
425
275
    def revision_history(self):
426
 
        """Return sequence of revision hashes on to this branch.
427
 
        
428
 
        This method will cache the revision history for as long as it is safe to
429
 
        do so.
430
 
        """
431
 
        if self._revision_history_cache is not None:
432
 
            history = self._revision_history_cache
433
 
        else:
434
 
            history = self._gen_revision_history()
435
 
            self._cache_revision_history(history)
436
 
        return list(history)
 
276
        """Return sequence of revision hashes on to this branch."""
 
277
        raise NotImplementedError('revision_history is abstract')
437
278
 
438
279
    def revno(self):
439
280
        """Return current revision number for this branch.
447
288
        """Older format branches cannot bind or unbind."""
448
289
        raise errors.UpgradeRequired(self.base)
449
290
 
450
 
    def set_append_revisions_only(self, enabled):
451
 
        """Older format branches are never restricted to append-only"""
452
 
        raise errors.UpgradeRequired(self.base)
453
 
 
454
291
    def last_revision(self):
455
 
        """Return last revision id, or None"""
 
292
        """Return last patch hash, or None if no history."""
456
293
        ph = self.revision_history()
457
294
        if ph:
458
295
            return ph[-1]
459
296
        else:
460
297
            return None
461
298
 
462
 
    def last_revision_info(self):
463
 
        """Return information about the last revision.
464
 
 
465
 
        :return: A tuple (revno, last_revision_id).
466
 
        """
467
 
        rh = self.revision_history()
468
 
        revno = len(rh)
469
 
        if revno:
470
 
            return (revno, rh[-1])
471
 
        else:
472
 
            return (0, _mod_revision.NULL_REVISION)
473
 
 
474
299
    def missing_revisions(self, other, stop_revision=None):
475
300
        """Return a list of new revisions that would perfectly fit.
476
301
        
477
302
        If self and other have not diverged, return a list of the revisions
478
303
        present in other, but missing from self.
 
304
 
 
305
        >>> from bzrlib.workingtree import WorkingTree
 
306
        >>> bzrlib.trace.silent = True
 
307
        >>> d1 = bzrdir.ScratchDir()
 
308
        >>> br1 = d1.open_branch()
 
309
        >>> wt1 = d1.open_workingtree()
 
310
        >>> d2 = bzrdir.ScratchDir()
 
311
        >>> br2 = d2.open_branch()
 
312
        >>> wt2 = d2.open_workingtree()
 
313
        >>> br1.missing_revisions(br2)
 
314
        []
 
315
        >>> wt2.commit("lala!", rev_id="REVISION-ID-1")
 
316
        >>> br1.missing_revisions(br2)
 
317
        [u'REVISION-ID-1']
 
318
        >>> br2.missing_revisions(br1)
 
319
        []
 
320
        >>> wt1.commit("lala!", rev_id="REVISION-ID-1")
 
321
        >>> br1.missing_revisions(br2)
 
322
        []
 
323
        >>> wt2.commit("lala!", rev_id="REVISION-ID-2A")
 
324
        >>> br1.missing_revisions(br2)
 
325
        [u'REVISION-ID-2A']
 
326
        >>> wt1.commit("lala!", rev_id="REVISION-ID-2B")
 
327
        >>> br1.missing_revisions(br2)
 
328
        Traceback (most recent call last):
 
329
        DivergedBranches: These branches have diverged.  Try merge.
479
330
        """
480
331
        self_history = self.revision_history()
481
332
        self_len = len(self_history)
491
342
        else:
492
343
            assert isinstance(stop_revision, int)
493
344
            if stop_revision > other_len:
494
 
                raise errors.NoSuchRevision(self, stop_revision)
 
345
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
495
346
        return other_history[self_len:stop_revision]
496
347
 
497
348
    def update_revisions(self, other, stop_revision=None):
501
352
        :param stop_revision: Updated until the given revision
502
353
        :return: None
503
354
        """
504
 
        raise NotImplementedError(self.update_revisions)
 
355
        raise NotImplementedError('update_revisions is abstract')
505
356
 
506
357
    def revision_id_to_revno(self, revision_id):
507
358
        """Given a revision id, return its revno"""
508
 
        if _mod_revision.is_null(revision_id):
 
359
        if revision_id is None:
509
360
            return 0
510
 
        revision_id = osutils.safe_revision_id(revision_id)
511
361
        history = self.revision_history()
512
362
        try:
513
363
            return history.index(revision_id) + 1
514
364
        except ValueError:
515
 
            raise errors.NoSuchRevision(self, revision_id)
 
365
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
516
366
 
517
367
    def get_rev_id(self, revno, history=None):
518
368
        """Find the revision id of the specified revno."""
520
370
            return None
521
371
        if history is None:
522
372
            history = self.revision_history()
523
 
        if revno <= 0 or revno > len(history):
524
 
            raise errors.NoSuchRevision(self, revno)
 
373
        elif revno <= 0 or revno > len(history):
 
374
            raise bzrlib.errors.NoSuchRevision(self, revno)
525
375
        return history[revno - 1]
526
376
 
527
377
    def pull(self, source, overwrite=False, stop_revision=None):
528
 
        """Mirror source into this branch.
529
 
 
530
 
        This branch is considered to be 'local', having low latency.
531
 
 
532
 
        :returns: PullResult instance
533
 
        """
534
 
        raise NotImplementedError(self.pull)
535
 
 
536
 
    def push(self, target, overwrite=False, stop_revision=None):
537
 
        """Mirror this branch into target.
538
 
 
539
 
        This branch is considered to be 'local', having low latency.
540
 
        """
541
 
        raise NotImplementedError(self.push)
 
378
        raise NotImplementedError('pull is abstract')
542
379
 
543
380
    def basis_tree(self):
544
 
        """Return `Tree` object for last revision."""
 
381
        """Return `Tree` object for last revision.
 
382
 
 
383
        If there are no revisions yet, return an `EmptyTree`.
 
384
        """
545
385
        return self.repository.revision_tree(self.last_revision())
546
386
 
547
387
    def rename_one(self, from_rel, to_rel):
549
389
 
550
390
        This can change the directory or the filename or both.
551
391
        """
552
 
        raise NotImplementedError(self.rename_one)
 
392
        raise NotImplementedError('rename_one is abstract')
553
393
 
554
394
    def move(self, from_paths, to_name):
555
395
        """Rename files.
565
405
        This returns a list of (from_path, to_path) pairs for each
566
406
        entry that is moved.
567
407
        """
568
 
        raise NotImplementedError(self.move)
 
408
        raise NotImplementedError('move is abstract')
569
409
 
570
410
    def get_parent(self):
571
411
        """Return the parent location of the branch.
574
414
        pattern is that the user can override it by specifying a
575
415
        location.
576
416
        """
577
 
        raise NotImplementedError(self.get_parent)
578
 
 
579
 
    def _set_config_location(self, name, url, config=None,
580
 
                             make_relative=False):
581
 
        if config is None:
582
 
            config = self.get_config()
583
 
        if url is None:
584
 
            url = ''
585
 
        elif make_relative:
586
 
            url = urlutils.relative_url(self.base, url)
587
 
        config.set_user_option(name, url, warn_masked=True)
588
 
 
589
 
    def _get_config_location(self, name, config=None):
590
 
        if config is None:
591
 
            config = self.get_config()
592
 
        location = config.get_user_option(name)
593
 
        if location == '':
594
 
            location = None
595
 
        return location
596
 
 
597
 
    def get_submit_branch(self):
598
 
        """Return the submit location of the branch.
599
 
 
600
 
        This is the default location for bundle.  The usual
601
 
        pattern is that the user can override it by specifying a
602
 
        location.
603
 
        """
604
 
        return self.get_config().get_user_option('submit_branch')
605
 
 
606
 
    def set_submit_branch(self, location):
607
 
        """Return the submit location of the branch.
608
 
 
609
 
        This is the default location for bundle.  The usual
610
 
        pattern is that the user can override it by specifying a
611
 
        location.
612
 
        """
613
 
        self.get_config().set_user_option('submit_branch', location,
614
 
            warn_masked=True)
615
 
 
616
 
    def get_public_branch(self):
617
 
        """Return the public location of the branch.
618
 
 
619
 
        This is is used by merge directives.
620
 
        """
621
 
        return self._get_config_location('public_branch')
622
 
 
623
 
    def set_public_branch(self, location):
624
 
        """Return the submit location of the branch.
625
 
 
626
 
        This is the default location for bundle.  The usual
627
 
        pattern is that the user can override it by specifying a
628
 
        location.
629
 
        """
630
 
        self._set_config_location('public_branch', location)
 
417
        raise NotImplementedError('get_parent is abstract')
631
418
 
632
419
    def get_push_location(self):
633
420
        """Return the None or the location to push this branch to."""
634
 
        push_loc = self.get_config().get_user_option('push_location')
635
 
        return push_loc
 
421
        raise NotImplementedError('get_push_location is abstract')
636
422
 
637
423
    def set_push_location(self, location):
638
424
        """Set a new push location for this branch."""
639
 
        raise NotImplementedError(self.set_push_location)
 
425
        raise NotImplementedError('set_push_location is abstract')
640
426
 
641
427
    def set_parent(self, url):
642
 
        raise NotImplementedError(self.set_parent)
 
428
        raise NotImplementedError('set_parent is abstract')
643
429
 
644
430
    @needs_write_lock
645
431
    def update(self):
666
452
            raise InvalidRevisionNumber(revno)
667
453
 
668
454
    @needs_read_lock
669
 
    def clone(self, to_bzrdir, revision_id=None):
 
455
    def clone(self, *args, **kwargs):
670
456
        """Clone this branch into to_bzrdir preserving all semantic values.
671
457
        
672
458
        revision_id: if not None, the revision history in the new branch will
673
459
                     be truncated to end with revision_id.
674
460
        """
 
461
        # for API compatability, 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 compatability 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')
675
506
        result = self._format.initialize(to_bzrdir)
676
507
        self.copy_content_into(result, revision_id=revision_id)
677
508
        return  result
688
519
        result.set_parent(self.bzrdir.root_transport.base)
689
520
        return result
690
521
 
691
 
    def _synchronize_history(self, destination, revision_id):
692
 
        """Synchronize last revision and revision history between branches.
693
 
 
694
 
        This version is most efficient when the destination is also a
695
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
696
 
        history is the true lefthand parent history, and all of the revisions
697
 
        are in the destination's repository.  If not, set_revision_history
698
 
        will fail.
699
 
 
700
 
        :param destination: The branch to copy the history into
701
 
        :param revision_id: The revision-id to truncate history at.  May
702
 
          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.
703
528
        """
704
 
        if revision_id == _mod_revision.NULL_REVISION:
705
 
            new_history = []
706
529
        new_history = self.revision_history()
707
 
        if revision_id is not None and new_history != []:
708
 
            revision_id = osutils.safe_revision_id(revision_id)
 
530
        if revision_id is not None:
709
531
            try:
710
532
                new_history = new_history[:new_history.index(revision_id) + 1]
711
533
            except ValueError:
712
534
                rev = self.repository.get_revision(revision_id)
713
535
                new_history = rev.get_history(self.repository)[1:]
714
536
        destination.set_revision_history(new_history)
715
 
 
716
 
    @needs_read_lock
717
 
    def copy_content_into(self, destination, revision_id=None):
718
 
        """Copy the content of self into destination.
719
 
 
720
 
        revision_id: if not None, the revision history in the new branch will
721
 
                     be truncated to end with revision_id.
722
 
        """
723
 
        self._synchronize_history(destination, revision_id)
724
 
        try:
725
 
            parent = self.get_parent()
726
 
        except errors.InaccessibleParent, e:
727
 
            mutter('parent was not accessible to copy: %s', e)
728
 
        else:
729
 
            if parent:
730
 
                destination.set_parent(parent)
731
 
        self.tags.merge_to(destination.tags)
732
 
 
733
 
    @needs_read_lock
734
 
    def check(self):
735
 
        """Check consistency of the branch.
736
 
 
737
 
        In particular this checks that revisions given in the revision-history
738
 
        do actually match up in the revision graph, and that they're all 
739
 
        present in the repository.
740
 
        
741
 
        Callers will typically also want to check the repository.
742
 
 
743
 
        :return: A BranchCheckResult.
744
 
        """
745
 
        mainline_parent_id = None
746
 
        for revision_id in self.revision_history():
747
 
            try:
748
 
                revision = self.repository.get_revision(revision_id)
749
 
            except errors.NoSuchRevision, e:
750
 
                raise errors.BzrCheckError("mainline revision {%s} not in repository"
751
 
                            % revision_id)
752
 
            # In general the first entry on the revision history has no parents.
753
 
            # But it's not illegal for it to have parents listed; this can happen
754
 
            # in imports from Arch when the parents weren't reachable.
755
 
            if mainline_parent_id is not None:
756
 
                if mainline_parent_id not in revision.parent_ids:
757
 
                    raise errors.BzrCheckError("previous revision {%s} not listed among "
758
 
                                        "parents of {%s}"
759
 
                                        % (mainline_parent_id, revision_id))
760
 
            mainline_parent_id = revision_id
761
 
        return BranchCheckResult(self)
762
 
 
763
 
    def _get_checkout_format(self):
764
 
        """Return the most suitable metadir for a checkout of this branch.
765
 
        Weaves are used if this branch's repository uses weaves.
766
 
        """
767
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
768
 
            from bzrlib.repofmt import weaverepo
769
 
            format = bzrdir.BzrDirMetaFormat1()
770
 
            format.repository_format = weaverepo.RepositoryFormat7()
771
 
        else:
772
 
            format = self.repository.bzrdir.checkout_metadir()
773
 
            format.set_branch_format(self._format)
774
 
        return format
775
 
 
776
 
    def create_checkout(self, to_location, revision_id=None,
777
 
                        lightweight=False):
778
 
        """Create a checkout of a branch.
779
 
        
780
 
        :param to_location: The url to produce the checkout at
781
 
        :param revision_id: The revision to check out
782
 
        :param lightweight: If True, produce a lightweight checkout, otherwise,
783
 
        produce a bound branch (heavyweight checkout)
784
 
        :return: The tree of the created checkout
785
 
        """
786
 
        t = transport.get_transport(to_location)
787
 
        t.ensure_base()
788
 
        if lightweight:
789
 
            format = self._get_checkout_format()
790
 
            checkout = format.initialize_on_transport(t)
791
 
            BranchReferenceFormat().initialize(checkout, self)
792
 
        else:
793
 
            format = self._get_checkout_format()
794
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
795
 
                to_location, force_new_tree=False, format=format)
796
 
            checkout = checkout_branch.bzrdir
797
 
            checkout_branch.bind(self)
798
 
            # pull up to the specified revision_id to set the initial 
799
 
            # branch tip correctly, and seed it with history.
800
 
            checkout_branch.pull(self, stop_revision=revision_id)
801
 
        tree = checkout.create_workingtree(revision_id)
802
 
        basis_tree = tree.basis_tree()
803
 
        basis_tree.lock_read()
804
 
        try:
805
 
            for path, file_id in basis_tree.iter_references():
806
 
                reference_parent = self.reference_parent(file_id, path)
807
 
                reference_parent.create_checkout(tree.abspath(path),
808
 
                    basis_tree.get_reference_revision(file_id, path),
809
 
                    lightweight)
810
 
        finally:
811
 
            basis_tree.unlock()
812
 
        return tree
813
 
 
814
 
    def reference_parent(self, file_id, path):
815
 
        """Return the parent branch for a tree-reference file_id
816
 
        :param file_id: The file_id of the tree reference
817
 
        :param path: The path of the file_id in the tree
818
 
        :return: A branch associated with the file_id
819
 
        """
820
 
        # FIXME should provide multiple branches, based on config
821
 
        return Branch.open(self.bzrdir.root_transport.clone(path).base)
822
 
 
823
 
    def supports_tags(self):
824
 
        return self._format.supports_tags()
 
537
        parent = self.get_parent()
 
538
        if parent:
 
539
            destination.set_parent(parent)
825
540
 
826
541
 
827
542
class BranchFormat(object):
848
563
    _formats = {}
849
564
    """The known formats."""
850
565
 
851
 
    def __eq__(self, other):
852
 
        return self.__class__ is other.__class__
853
 
 
854
 
    def __ne__(self, other):
855
 
        return not (self == other)
856
 
 
857
566
    @classmethod
858
567
    def find_format(klass, a_bzrdir):
859
568
        """Return the format for the branch object in a_bzrdir."""
864
573
        except NoSuchFile:
865
574
            raise NotBranchError(path=transport.base)
866
575
        except KeyError:
867
 
            raise errors.UnknownFormatError(format=format_string)
 
576
            raise errors.UnknownFormatError(format_string)
868
577
 
869
578
    @classmethod
870
579
    def get_default_format(klass):
871
580
        """Return the current default format."""
872
581
        return klass._default_format
873
582
 
874
 
    def get_reference(self, a_bzrdir):
875
 
        """Get the target reference of the branch in a_bzrdir.
876
 
 
877
 
        format probing must have been completed before calling
878
 
        this method - it is assumed that the format of the branch
879
 
        in a_bzrdir is correct.
880
 
 
881
 
        :param a_bzrdir: The bzrdir to get the branch data from.
882
 
        :return: None if the branch is not a reference branch.
883
 
        """
884
 
        return None
885
 
 
886
583
    def get_format_string(self):
887
584
        """Return the ASCII format string that identifies this format."""
888
585
        raise NotImplementedError(self.get_format_string)
889
586
 
890
587
    def get_format_description(self):
891
588
        """Return the short format description for this format."""
892
 
        raise NotImplementedError(self.get_format_description)
893
 
 
894
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
895
 
                           set_format=True):
896
 
        """Initialize a branch in a bzrdir, with specified files
897
 
 
898
 
        :param a_bzrdir: The bzrdir to initialize the branch in
899
 
        :param utf8_files: The files to create as a list of
900
 
            (filename, content) tuples
901
 
        :param set_format: If True, set the format with
902
 
            self.get_format_string.  (BzrBranch4 has its format set
903
 
            elsewhere)
904
 
        :return: a branch in this format
905
 
        """
906
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
907
 
        branch_transport = a_bzrdir.get_branch_transport(self)
908
 
        lock_map = {
909
 
            'metadir': ('lock', lockdir.LockDir),
910
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
911
 
        }
912
 
        lock_name, lock_class = lock_map[lock_type]
913
 
        control_files = lockable_files.LockableFiles(branch_transport,
914
 
            lock_name, lock_class)
915
 
        control_files.create_lock()
916
 
        control_files.lock_write()
917
 
        if set_format:
918
 
            control_files.put_utf8('format', self.get_format_string())
919
 
        try:
920
 
            for file, content in utf8_files:
921
 
                control_files.put_utf8(file, content)
922
 
        finally:
923
 
            control_files.unlock()
924
 
        return self.open(a_bzrdir, _found=True)
 
589
        raise NotImplementedError(self.get_format_string)
925
590
 
926
591
    def initialize(self, a_bzrdir):
927
592
        """Create a branch of this format in a_bzrdir."""
928
 
        raise NotImplementedError(self.initialize)
 
593
        raise NotImplementedError(self.initialized)
929
594
 
930
595
    def is_supported(self):
931
596
        """Is this format supported?
960
625
    def __str__(self):
961
626
        return self.get_format_string().rstrip()
962
627
 
963
 
    def supports_tags(self):
964
 
        """True if this format supports tags stored in the branch"""
965
 
        return False  # by default
966
 
 
967
 
    # XXX: Probably doesn't really belong here -- mbp 20070212
968
 
    def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
969
 
            lock_class):
970
 
        branch_transport = a_bzrdir.get_branch_transport(self)
971
 
        control_files = lockable_files.LockableFiles(branch_transport,
972
 
            lock_filename, lock_class)
973
 
        control_files.create_lock()
974
 
        control_files.lock_write()
975
 
        try:
976
 
            for filename, content in utf8_files:
977
 
                control_files.put_utf8(filename, content)
978
 
        finally:
979
 
            control_files.unlock()
980
 
 
981
 
 
982
 
class BranchHooks(Hooks):
983
 
    """A dictionary mapping hook name to a list of callables for branch hooks.
984
 
    
985
 
    e.g. ['set_rh'] Is the list of items to be called when the
986
 
    set_revision_history function is invoked.
987
 
    """
988
 
 
989
 
    def __init__(self):
990
 
        """Create the default hooks.
991
 
 
992
 
        These are all empty initially, because by default nothing should get
993
 
        notified.
994
 
        """
995
 
        Hooks.__init__(self)
996
 
        # Introduced in 0.15:
997
 
        # invoked whenever the revision history has been set
998
 
        # with set_revision_history. The api signature is
999
 
        # (branch, revision_history), and the branch will
1000
 
        # be write-locked.
1001
 
        self['set_rh'] = []
1002
 
        # invoked after a push operation completes.
1003
 
        # the api signature is
1004
 
        # (push_result)
1005
 
        # containing the members
1006
 
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1007
 
        # where local is the local target branch or None, master is the target 
1008
 
        # master branch, and the rest should be self explanatory. The source
1009
 
        # is read locked and the target branches write locked. Source will
1010
 
        # be the local low-latency branch.
1011
 
        self['post_push'] = []
1012
 
        # invoked after a pull operation completes.
1013
 
        # the api signature is
1014
 
        # (pull_result)
1015
 
        # containing the members
1016
 
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
1017
 
        # where local is the local branch or None, master is the target 
1018
 
        # master branch, and the rest should be self explanatory. The source
1019
 
        # is read locked and the target branches write locked. The local
1020
 
        # branch is the low-latency branch.
1021
 
        self['post_pull'] = []
1022
 
        # invoked after a commit operation completes.
1023
 
        # the api signature is 
1024
 
        # (local, master, old_revno, old_revid, new_revno, new_revid)
1025
 
        # old_revid is NULL_REVISION for the first commit to a branch.
1026
 
        self['post_commit'] = []
1027
 
        # invoked after a uncommit operation completes.
1028
 
        # the api signature is
1029
 
        # (local, master, old_revno, old_revid, new_revno, new_revid) where
1030
 
        # local is the local branch or None, master is the target branch,
1031
 
        # and an empty branch recieves new_revno of 0, new_revid of None.
1032
 
        self['post_uncommit'] = []
1033
 
 
1034
 
 
1035
 
# install the default hooks into the Branch class.
1036
 
Branch.hooks = BranchHooks()
1037
 
 
1038
628
 
1039
629
class BzrBranchFormat4(BranchFormat):
1040
630
    """Bzr branch format 4.
1050
640
 
1051
641
    def initialize(self, a_bzrdir):
1052
642
        """Create a branch of this format in a_bzrdir."""
 
643
        mutter('creating branch in %s', a_bzrdir.transport.base)
 
644
        branch_transport = a_bzrdir.get_branch_transport(self)
1053
645
        utf8_files = [('revision-history', ''),
1054
646
                      ('branch-name', ''),
1055
647
                      ]
1056
 
        return self._initialize_helper(a_bzrdir, utf8_files,
1057
 
                                       lock_type='branch4', set_format=False)
 
648
        control_files = LockableFiles(branch_transport, 'branch-lock',
 
649
                                      TransportLock)
 
650
        control_files.create_lock()
 
651
        control_files.lock_write()
 
652
        try:
 
653
            for file, content in utf8_files:
 
654
                control_files.put_utf8(file, content)
 
655
        finally:
 
656
            control_files.unlock()
 
657
        return self.open(a_bzrdir, _found=True)
1058
658
 
1059
659
    def __init__(self):
1060
660
        super(BzrBranchFormat4, self).__init__()
1101
701
        
1102
702
    def initialize(self, a_bzrdir):
1103
703
        """Create a branch of this format in a_bzrdir."""
 
704
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
705
        branch_transport = a_bzrdir.get_branch_transport(self)
1104
706
        utf8_files = [('revision-history', ''),
1105
707
                      ('branch-name', ''),
1106
708
                      ]
1107
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
709
        control_files = LockableFiles(branch_transport, 'lock', LockDir)
 
710
        control_files.create_lock()
 
711
        control_files.lock_write()
 
712
        control_files.put_utf8('format', self.get_format_string())
 
713
        try:
 
714
            for file, content in utf8_files:
 
715
                control_files.put_utf8(file, content)
 
716
        finally:
 
717
            control_files.unlock()
 
718
        return self.open(a_bzrdir, _found=True, )
1108
719
 
1109
720
    def __init__(self):
1110
721
        super(BzrBranchFormat5, self).__init__()
1119
730
        if not _found:
1120
731
            format = BranchFormat.find_format(a_bzrdir)
1121
732
            assert format.__class__ == self.__class__
1122
 
        try:
1123
 
            transport = a_bzrdir.get_branch_transport(None)
1124
 
            control_files = lockable_files.LockableFiles(transport, 'lock',
1125
 
                                                         lockdir.LockDir)
1126
 
            return BzrBranch5(_format=self,
1127
 
                              _control_files=control_files,
1128
 
                              a_bzrdir=a_bzrdir,
1129
 
                              _repository=a_bzrdir.find_repository())
1130
 
        except NoSuchFile:
1131
 
            raise NotBranchError(path=transport.base)
1132
 
 
1133
 
 
1134
 
class BzrBranchFormat6(BzrBranchFormat5):
1135
 
    """Branch format with last-revision
1136
 
 
1137
 
    Unlike previous formats, this has no explicit revision history. Instead,
1138
 
    this just stores the last-revision, and the left-hand history leading
1139
 
    up to there is the history.
1140
 
 
1141
 
    This format was introduced in bzr 0.15
1142
 
    """
1143
 
 
1144
 
    def get_format_string(self):
1145
 
        """See BranchFormat.get_format_string()."""
1146
 
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
1147
 
 
1148
 
    def get_format_description(self):
1149
 
        """See BranchFormat.get_format_description()."""
1150
 
        return "Branch format 6"
1151
 
 
1152
 
    def initialize(self, a_bzrdir):
1153
 
        """Create a branch of this format in a_bzrdir."""
1154
 
        utf8_files = [('last-revision', '0 null:\n'),
1155
 
                      ('branch-name', ''),
1156
 
                      ('branch.conf', ''),
1157
 
                      ('tags', ''),
1158
 
                      ]
1159
 
        return self._initialize_helper(a_bzrdir, utf8_files)
1160
 
 
1161
 
    def open(self, a_bzrdir, _found=False):
1162
 
        """Return the branch object for a_bzrdir
1163
 
 
1164
 
        _found is a private parameter, do not use it. It is used to indicate
1165
 
               if format probing has already be done.
1166
 
        """
1167
 
        if not _found:
1168
 
            format = BranchFormat.find_format(a_bzrdir)
1169
 
            assert format.__class__ == self.__class__
1170
733
        transport = a_bzrdir.get_branch_transport(None)
1171
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1172
 
                                                     lockdir.LockDir)
1173
 
        return BzrBranch6(_format=self,
 
734
        control_files = LockableFiles(transport, 'lock', LockDir)
 
735
        return BzrBranch5(_format=self,
1174
736
                          _control_files=control_files,
1175
737
                          a_bzrdir=a_bzrdir,
1176
738
                          _repository=a_bzrdir.find_repository())
1177
739
 
1178
 
    def supports_tags(self):
1179
 
        return True
 
740
    def __str__(self):
 
741
        return "Bazaar-NG Metadir branch format 5"
1180
742
 
1181
743
 
1182
744
class BranchReferenceFormat(BranchFormat):
1198
760
        """See BranchFormat.get_format_description()."""
1199
761
        return "Checkout reference format 1"
1200
762
        
1201
 
    def get_reference(self, a_bzrdir):
1202
 
        """See BranchFormat.get_reference()."""
1203
 
        transport = a_bzrdir.get_branch_transport(None)
1204
 
        return transport.get('location').read()
1205
 
 
1206
763
    def initialize(self, a_bzrdir, target_branch=None):
1207
764
        """Create a branch of this format in a_bzrdir."""
1208
765
        if target_branch is None:
1211
768
            raise errors.UninitializableFormat(self)
1212
769
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
1213
770
        branch_transport = a_bzrdir.get_branch_transport(self)
1214
 
        branch_transport.put_bytes('location',
1215
 
            target_branch.bzrdir.root_transport.base)
1216
 
        branch_transport.put_bytes('format', self.get_format_string())
 
771
        # FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
 
772
        branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
 
773
        branch_transport.put('format', StringIO(self.get_format_string()))
1217
774
        return self.open(a_bzrdir, _found=True)
1218
775
 
1219
776
    def __init__(self):
1230
787
            # emit some sort of warning/error to the caller ?!
1231
788
        return clone
1232
789
 
1233
 
    def open(self, a_bzrdir, _found=False, location=None):
 
790
    def open(self, a_bzrdir, _found=False):
1234
791
        """Return the branch that the branch reference in a_bzrdir points at.
1235
792
 
1236
793
        _found is a private parameter, do not use it. It is used to indicate
1239
796
        if not _found:
1240
797
            format = BranchFormat.find_format(a_bzrdir)
1241
798
            assert format.__class__ == self.__class__
1242
 
        if location is None:
1243
 
            location = self.get_reference(a_bzrdir)
1244
 
        real_bzrdir = bzrdir.BzrDir.open(location)
 
799
        transport = a_bzrdir.get_branch_transport(None)
 
800
        real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
1245
801
        result = real_bzrdir.open_branch()
1246
802
        # this changes the behaviour of result.clone to create a new reference
1247
803
        # rather than a copy of the content of the branch.
1260
816
__default_format = BzrBranchFormat5()
1261
817
BranchFormat.register_format(__default_format)
1262
818
BranchFormat.register_format(BranchReferenceFormat())
1263
 
BranchFormat.register_format(BzrBranchFormat6())
1264
819
BranchFormat.set_default_format(__default_format)
1265
820
_legacy_formats = [BzrBranchFormat4(),
1266
821
                   ]
1273
828
    it's writable, and can be accessed via the normal filesystem API.
1274
829
    """
1275
830
    
1276
 
    def __init__(self, _format=None,
 
831
    def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
 
832
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
1277
833
                 _control_files=None, a_bzrdir=None, _repository=None):
1278
 
        """Create new branch object at a particular location."""
1279
 
        Branch.__init__(self)
 
834
        """Create new branch object at a particular location.
 
835
 
 
836
        transport -- A Transport object, defining how to access files.
 
837
        
 
838
        init -- If True, create new control files in a previously
 
839
             unversioned directory.  If False, the branch must already
 
840
             be versioned.
 
841
 
 
842
        relax_version_check -- If true, the usual check for the branch
 
843
            version is not applied.  This is intended only for
 
844
            upgrade/recovery type use; it's not guaranteed that
 
845
            all operations will work on old format branches.
 
846
        """
1280
847
        if a_bzrdir is None:
1281
 
            raise ValueError('a_bzrdir must be supplied')
 
848
            self.bzrdir = bzrdir.BzrDir.open(transport.base)
1282
849
        else:
1283
850
            self.bzrdir = a_bzrdir
1284
 
        # self._transport used to point to the directory containing the
1285
 
        # control directory, but was not used - now it's just the transport
1286
 
        # for the branch control files.  mbp 20070212
1287
 
        self._base = self.bzrdir.transport.clone('..').base
 
851
        self._transport = self.bzrdir.transport.clone('..')
 
852
        self._base = self._transport.base
1288
853
        self._format = _format
1289
854
        if _control_files is None:
1290
 
            raise ValueError('BzrBranch _control_files is None')
 
855
            raise BzrBadParameterMissing('_control_files')
1291
856
        self.control_files = _control_files
1292
 
        self._transport = _control_files._transport
 
857
        if deprecated_passed(init):
 
858
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
859
                 "deprecated as of bzr 0.8. Please use Branch.create().",
 
860
                 DeprecationWarning,
 
861
                 stacklevel=2)
 
862
            if init:
 
863
                # this is slower than before deprecation, oh well never mind.
 
864
                # -> its deprecated.
 
865
                self._initialize(transport.base)
 
866
        self._check_format(_format)
 
867
        if deprecated_passed(relax_version_check):
 
868
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
869
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
870
                 "Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
 
871
                 "open() method.",
 
872
                 DeprecationWarning,
 
873
                 stacklevel=2)
 
874
            if (not relax_version_check
 
875
                and not self._format.is_supported()):
 
876
                raise errors.UnsupportedFormatError(
 
877
                        'sorry, branch format %r not supported' % fmt,
 
878
                        ['use a different bzr version',
 
879
                         'or remove the .bzr directory'
 
880
                         ' and "bzr init" again'])
 
881
        if deprecated_passed(transport):
 
882
            warn("BzrBranch.__init__(transport=XXX...): The transport "
 
883
                 "parameter is deprecated as of bzr 0.8. "
 
884
                 "Please use Branch.open, or bzrdir.open_branch().",
 
885
                 DeprecationWarning,
 
886
                 stacklevel=2)
1293
887
        self.repository = _repository
1294
888
 
1295
889
    def __str__(self):
1297
891
 
1298
892
    __repr__ = __str__
1299
893
 
 
894
    def __del__(self):
 
895
        # TODO: It might be best to do this somewhere else,
 
896
        # but it is nice for a Branch object to automatically
 
897
        # cache it's information.
 
898
        # Alternatively, we could have the Transport objects cache requests
 
899
        # See the earlier discussion about how major objects (like Branch)
 
900
        # should never expect their __del__ function to run.
 
901
        # XXX: cache_root seems to be unused, 2006-01-13 mbp
 
902
        if hasattr(self, 'cache_root') and self.cache_root is not None:
 
903
            try:
 
904
                rmtree(self.cache_root)
 
905
            except:
 
906
                pass
 
907
            self.cache_root = None
 
908
 
1300
909
    def _get_base(self):
1301
 
        """Returns the directory containing the control directory."""
1302
910
        return self._base
1303
911
 
1304
912
    base = property(_get_base, doc="The URL for the root of this branch.")
1305
913
 
 
914
    def _finish_transaction(self):
 
915
        """Exit the current transaction."""
 
916
        return self.control_files._finish_transaction()
 
917
 
 
918
    def get_transaction(self):
 
919
        """Return the current active transaction.
 
920
 
 
921
        If no transaction is active, this returns a passthrough object
 
922
        for which all data is immediately flushed and no caching happens.
 
923
        """
 
924
        # this is an explicit function so that we can do tricky stuff
 
925
        # when the storage in rev_storage is elsewhere.
 
926
        # we probably need to hook the two 'lock a location' and 
 
927
        # 'have a transaction' together more delicately, so that
 
928
        # we can have two locks (branch and storage) and one transaction
 
929
        # ... and finishing the transaction unlocks both, but unlocking
 
930
        # does not. - RBC 20051121
 
931
        return self.control_files.get_transaction()
 
932
 
 
933
    def _set_transaction(self, transaction):
 
934
        """Set a new active transaction."""
 
935
        return self.control_files._set_transaction(transaction)
 
936
 
1306
937
    def abspath(self, name):
1307
938
        """See Branch.abspath."""
1308
939
        return self.control_files._transport.abspath(name)
1309
940
 
1310
 
 
1311
 
    @deprecated_method(zero_sixteen)
 
941
    def _check_format(self, format):
 
942
        """Identify the branch format if needed.
 
943
 
 
944
        The format is stored as a reference to the format object in
 
945
        self._format for code that needs to check it later.
 
946
 
 
947
        The format parameter is either None or the branch format class
 
948
        used to open this branch.
 
949
 
 
950
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
 
951
        """
 
952
        if format is None:
 
953
            format = BzrBranchFormat.find_format(self.bzrdir)
 
954
        self._format = format
 
955
        mutter("got branch format %s", self._format)
 
956
 
1312
957
    @needs_read_lock
1313
958
    def get_root_id(self):
1314
959
        """See Branch.get_root_id."""
1318
963
    def is_locked(self):
1319
964
        return self.control_files.is_locked()
1320
965
 
1321
 
    def lock_write(self, token=None):
1322
 
        repo_token = self.repository.lock_write()
1323
 
        try:
1324
 
            token = self.control_files.lock_write(token=token)
1325
 
        except:
1326
 
            self.repository.unlock()
1327
 
            raise
1328
 
        return token
 
966
    def lock_write(self):
 
967
        # TODO: test for failed two phase locks. This is known broken.
 
968
        self.control_files.lock_write()
 
969
        self.repository.lock_write()
1329
970
 
1330
971
    def lock_read(self):
 
972
        # TODO: test for failed two phase locks. This is known broken.
 
973
        self.control_files.lock_read()
1331
974
        self.repository.lock_read()
1332
 
        try:
1333
 
            self.control_files.lock_read()
1334
 
        except:
1335
 
            self.repository.unlock()
1336
 
            raise
1337
975
 
1338
976
    def unlock(self):
1339
977
        # TODO: test for failed two phase locks. This is known broken.
1340
978
        try:
 
979
            self.repository.unlock()
 
980
        finally:
1341
981
            self.control_files.unlock()
1342
 
        finally:
1343
 
            self.repository.unlock()
1344
 
        if not self.control_files.is_locked():
1345
 
            # we just released the lock
1346
 
            self._clear_cached_state()
1347
982
        
1348
983
    def peek_lock_mode(self):
1349
984
        if self.control_files._lock_count == 0:
1362
997
    @needs_write_lock
1363
998
    def append_revision(self, *revision_ids):
1364
999
        """See Branch.append_revision."""
1365
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1366
1000
        for revision_id in revision_ids:
1367
 
            _mod_revision.check_not_reserved_id(revision_id)
1368
1001
            mutter("add {%s} to revision-history" % revision_id)
1369
1002
        rev_history = self.revision_history()
1370
1003
        rev_history.extend(revision_ids)
1371
1004
        self.set_revision_history(rev_history)
1372
1005
 
1373
 
    def _write_revision_history(self, history):
1374
 
        """Factored out of set_revision_history.
1375
 
 
1376
 
        This performs the actual writing to disk.
1377
 
        It is intended to be called by BzrBranch5.set_revision_history."""
1378
 
        self.control_files.put_bytes(
1379
 
            'revision-history', '\n'.join(history))
1380
 
 
1381
1006
    @needs_write_lock
1382
1007
    def set_revision_history(self, rev_history):
1383
1008
        """See Branch.set_revision_history."""
1384
 
        rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1385
 
        self._clear_cached_state()
1386
 
        self._write_revision_history(rev_history)
1387
 
        self._cache_revision_history(rev_history)
1388
 
        for hook in Branch.hooks['set_rh']:
1389
 
            hook(self, rev_history)
1390
 
 
1391
 
    @needs_write_lock
1392
 
    def set_last_revision_info(self, revno, revision_id):
1393
 
        revision_id = osutils.safe_revision_id(revision_id)
1394
 
        history = self._lefthand_history(revision_id)
1395
 
        assert len(history) == revno, '%d != %d' % (len(history), revno)
1396
 
        self.set_revision_history(history)
1397
 
 
1398
 
    def _gen_revision_history(self):
1399
 
        history = self.control_files.get('revision-history').read().split('\n')
1400
 
        if history[-1:] == ['']:
1401
 
            # There shouldn't be a trailing newline, but just in case.
1402
 
            history.pop()
1403
 
        return history
1404
 
 
1405
 
    def _lefthand_history(self, revision_id, last_rev=None,
1406
 
                          other_branch=None):
1407
 
        # stop_revision must be a descendant of last_revision
1408
 
        stop_graph = self.repository.get_revision_graph(revision_id)
1409
 
        if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1410
 
            and last_rev not in stop_graph):
1411
 
            # our previous tip is not merged into stop_revision
1412
 
            raise errors.DivergedBranches(self, other_branch)
1413
 
        # make a new revision history from the graph
1414
 
        current_rev_id = revision_id
1415
 
        new_history = []
1416
 
        while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1417
 
            new_history.append(current_rev_id)
1418
 
            current_rev_id_parents = stop_graph[current_rev_id]
1419
 
            try:
1420
 
                current_rev_id = current_rev_id_parents[0]
1421
 
            except IndexError:
1422
 
                current_rev_id = None
1423
 
        new_history.reverse()
1424
 
        return new_history
1425
 
 
1426
 
    @needs_write_lock
1427
 
    def generate_revision_history(self, revision_id, last_rev=None,
1428
 
        other_branch=None):
1429
 
        """Create a new revision history that will finish with revision_id.
1430
 
 
1431
 
        :param revision_id: the new tip to use.
1432
 
        :param last_rev: The previous last_revision. If not None, then this
1433
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
1434
 
        :param other_branch: The other branch that DivergedBranches should
1435
 
            raise with respect to.
 
1009
        self.control_files.put_utf8(
 
1010
            'revision-history', '\n'.join(rev_history))
 
1011
        transaction = self.get_transaction()
 
1012
        history = transaction.map.find_revision_history()
 
1013
        if history is not None:
 
1014
            # update the revision history in the identity map.
 
1015
            history[:] = list(rev_history)
 
1016
            # this call is disabled because revision_history is 
 
1017
            # not really an object yet, and the transaction is for objects.
 
1018
            # transaction.register_dirty(history)
 
1019
        else:
 
1020
            transaction.map.add_revision_history(rev_history)
 
1021
            # this call is disabled because revision_history is 
 
1022
            # not really an object yet, and the transaction is for objects.
 
1023
            # transaction.register_clean(history)
 
1024
 
 
1025
    def get_revision_delta(self, revno):
 
1026
        """Return the delta for one revision.
 
1027
 
 
1028
        The delta is relative to its mainline predecessor, or the
 
1029
        empty tree for revision 1.
1436
1030
        """
1437
 
        revision_id = osutils.safe_revision_id(revision_id)
1438
 
        self.set_revision_history(self._lefthand_history(revision_id,
1439
 
            last_rev, other_branch))
 
1031
        assert isinstance(revno, int)
 
1032
        rh = self.revision_history()
 
1033
        if not (1 <= revno <= len(rh)):
 
1034
            raise InvalidRevisionNumber(revno)
 
1035
 
 
1036
        # revno is 1-based; list is 0-based
 
1037
 
 
1038
        new_tree = self.repository.revision_tree(rh[revno-1])
 
1039
        if revno == 1:
 
1040
            old_tree = EmptyTree()
 
1041
        else:
 
1042
            old_tree = self.repository.revision_tree(rh[revno-2])
 
1043
        return compare_trees(old_tree, new_tree)
 
1044
 
 
1045
    @needs_read_lock
 
1046
    def revision_history(self):
 
1047
        """See Branch.revision_history."""
 
1048
        transaction = self.get_transaction()
 
1049
        history = transaction.map.find_revision_history()
 
1050
        if history is not None:
 
1051
            mutter("cache hit for revision-history in %s", self)
 
1052
            return list(history)
 
1053
        history = [l.rstrip('\r\n') for l in
 
1054
                self.control_files.get_utf8('revision-history').readlines()]
 
1055
        transaction.map.add_revision_history(history)
 
1056
        # this call is disabled because revision_history is 
 
1057
        # not really an object yet, and the transaction is for objects.
 
1058
        # transaction.register_clean(history, precious=True)
 
1059
        return list(history)
1440
1060
 
1441
1061
    @needs_write_lock
1442
1062
    def update_revisions(self, other, stop_revision=None):
1448
1068
                if stop_revision is None:
1449
1069
                    # if there are no commits, we're done.
1450
1070
                    return
1451
 
            else:
1452
 
                stop_revision = osutils.safe_revision_id(stop_revision)
1453
1071
            # whats the current last revision, before we fetch [and change it
1454
1072
            # possibly]
1455
 
            last_rev = _mod_revision.ensure_null(self.last_revision())
 
1073
            last_rev = self.last_revision()
1456
1074
            # we fetch here regardless of whether we need to so that we pickup
1457
1075
            # filled in ghosts.
1458
1076
            self.fetch(other, stop_revision)
1459
 
            my_ancestry = self.repository.get_ancestry(last_rev,
1460
 
                                                       topo_sorted=False)
 
1077
            my_ancestry = self.repository.get_ancestry(last_rev)
1461
1078
            if stop_revision in my_ancestry:
1462
1079
                # last_revision is a descendant of stop_revision
1463
1080
                return
1464
 
            self.generate_revision_history(stop_revision, last_rev=last_rev,
1465
 
                other_branch=other)
 
1081
            # stop_revision must be a descendant of last_revision
 
1082
            stop_graph = self.repository.get_revision_graph(stop_revision)
 
1083
            if last_rev is not None and last_rev not in stop_graph:
 
1084
                # our previous tip is not merged into stop_revision
 
1085
                raise errors.DivergedBranches(self, other)
 
1086
            # make a new revision history from the graph
 
1087
            current_rev_id = stop_revision
 
1088
            new_history = []
 
1089
            while current_rev_id not in (None, NULL_REVISION):
 
1090
                new_history.append(current_rev_id)
 
1091
                current_rev_id_parents = stop_graph[current_rev_id]
 
1092
                try:
 
1093
                    current_rev_id = current_rev_id_parents[0]
 
1094
                except IndexError:
 
1095
                    current_rev_id = None
 
1096
            new_history.reverse()
 
1097
            self.set_revision_history(new_history)
1466
1098
        finally:
1467
1099
            other.unlock()
1468
1100
 
 
1101
    @deprecated_method(zero_eight)
 
1102
    def pullable_revisions(self, other, stop_revision):
 
1103
        """Please use bzrlib.missing instead."""
 
1104
        other_revno = other.revision_id_to_revno(stop_revision)
 
1105
        try:
 
1106
            return self.missing_revisions(other, other_revno)
 
1107
        except DivergedBranches, e:
 
1108
            try:
 
1109
                pullable_revs = get_intervening_revisions(self.last_revision(),
 
1110
                                                          stop_revision, 
 
1111
                                                          self.repository)
 
1112
                assert self.last_revision() not in pullable_revs
 
1113
                return pullable_revs
 
1114
            except bzrlib.errors.NotAncestor:
 
1115
                if is_ancestor(self.last_revision(), stop_revision, self):
 
1116
                    return []
 
1117
                else:
 
1118
                    raise e
 
1119
        
1469
1120
    def basis_tree(self):
1470
1121
        """See Branch.basis_tree."""
1471
1122
        return self.repository.revision_tree(self.last_revision())
1473
1124
    @deprecated_method(zero_eight)
1474
1125
    def working_tree(self):
1475
1126
        """Create a Working tree object for this branch."""
1476
 
 
 
1127
        from bzrlib.workingtree import WorkingTree
1477
1128
        from bzrlib.transport.local import LocalTransport
1478
1129
        if (self.base.find('://') != -1 or 
1479
1130
            not isinstance(self._transport, LocalTransport)):
1481
1132
        return self.bzrdir.open_workingtree()
1482
1133
 
1483
1134
    @needs_write_lock
1484
 
    def pull(self, source, overwrite=False, stop_revision=None,
1485
 
             _hook_master=None, run_hooks=True):
1486
 
        """See Branch.pull.
1487
 
 
1488
 
        :param _hook_master: Private parameter - set the branch to 
1489
 
            be supplied as the master to push hooks.
1490
 
        :param run_hooks: Private parameter - if false, this branch
1491
 
            is being called because it's the master of the primary branch,
1492
 
            so it should not run its hooks.
1493
 
        """
1494
 
        result = PullResult()
1495
 
        result.source_branch = source
1496
 
        result.target_branch = self
 
1135
    def pull(self, source, overwrite=False, stop_revision=None):
 
1136
        """See Branch.pull."""
1497
1137
        source.lock_read()
1498
1138
        try:
1499
 
            result.old_revno, result.old_revid = self.last_revision_info()
 
1139
            old_count = len(self.revision_history())
1500
1140
            try:
1501
 
                self.update_revisions(source, stop_revision)
 
1141
                self.update_revisions(source,stop_revision)
1502
1142
            except DivergedBranches:
1503
1143
                if not overwrite:
1504
1144
                    raise
1505
1145
            if overwrite:
1506
 
                if stop_revision is None:
1507
 
                    stop_revision = source.last_revision()
1508
 
                self.generate_revision_history(stop_revision)
1509
 
            result.tag_conflicts = source.tags.merge_to(self.tags)
1510
 
            result.new_revno, result.new_revid = self.last_revision_info()
1511
 
            if _hook_master:
1512
 
                result.master_branch = _hook_master
1513
 
                result.local_branch = self
1514
 
            else:
1515
 
                result.master_branch = self
1516
 
                result.local_branch = None
1517
 
            if run_hooks:
1518
 
                for hook in Branch.hooks['post_pull']:
1519
 
                    hook(result)
 
1146
                self.set_revision_history(source.revision_history())
 
1147
            new_count = len(self.revision_history())
 
1148
            return new_count - old_count
1520
1149
        finally:
1521
1150
            source.unlock()
1522
 
        return result
1523
1151
 
1524
 
    def _get_parent_location(self):
 
1152
    def get_parent(self):
 
1153
        """See Branch.get_parent."""
 
1154
        import errno
1525
1155
        _locs = ['parent', 'pull', 'x-pull']
1526
1156
        for l in _locs:
1527
1157
            try:
1528
 
                return self.control_files.get(l).read().strip('\n')
 
1158
                return self.control_files.get_utf8(l).read().strip('\n')
1529
1159
            except NoSuchFile:
1530
1160
                pass
1531
1161
        return None
1532
1162
 
1533
 
    @needs_read_lock
1534
 
    def push(self, target, overwrite=False, stop_revision=None,
1535
 
             _override_hook_source_branch=None):
1536
 
        """See Branch.push.
1537
 
 
1538
 
        This is the basic concrete implementation of push()
1539
 
 
1540
 
        :param _override_hook_source_branch: If specified, run
1541
 
        the hooks passing this Branch as the source, rather than self.  
1542
 
        This is for use of RemoteBranch, where push is delegated to the
1543
 
        underlying vfs-based Branch. 
1544
 
        """
1545
 
        # TODO: Public option to disable running hooks - should be trivial but
1546
 
        # needs tests.
1547
 
        target.lock_write()
1548
 
        try:
1549
 
            result = self._push_with_bound_branches(target, overwrite,
1550
 
                    stop_revision,
1551
 
                    _override_hook_source_branch=_override_hook_source_branch)
1552
 
            return result
1553
 
        finally:
1554
 
            target.unlock()
1555
 
 
1556
 
    def _push_with_bound_branches(self, target, overwrite,
1557
 
            stop_revision,
1558
 
            _override_hook_source_branch=None):
1559
 
        """Push from self into target, and into target's master if any.
1560
 
        
1561
 
        This is on the base BzrBranch class even though it doesn't support 
1562
 
        bound branches because the *target* might be bound.
1563
 
        """
1564
 
        def _run_hooks():
1565
 
            if _override_hook_source_branch:
1566
 
                result.source_branch = _override_hook_source_branch
1567
 
            for hook in Branch.hooks['post_push']:
1568
 
                hook(result)
1569
 
 
1570
 
        bound_location = target.get_bound_location()
1571
 
        if bound_location and target.base != bound_location:
1572
 
            # there is a master branch.
1573
 
            #
1574
 
            # XXX: Why the second check?  Is it even supported for a branch to
1575
 
            # be bound to itself? -- mbp 20070507
1576
 
            master_branch = target.get_master_branch()
1577
 
            master_branch.lock_write()
1578
 
            try:
1579
 
                # push into the master from this branch.
1580
 
                self._basic_push(master_branch, overwrite, stop_revision)
1581
 
                # and push into the target branch from this. Note that we push from
1582
 
                # this branch again, because its considered the highest bandwidth
1583
 
                # repository.
1584
 
                result = self._basic_push(target, overwrite, stop_revision)
1585
 
                result.master_branch = master_branch
1586
 
                result.local_branch = target
1587
 
                _run_hooks()
1588
 
                return result
1589
 
            finally:
1590
 
                master_branch.unlock()
1591
 
        else:
1592
 
            # no master branch
1593
 
            result = self._basic_push(target, overwrite, stop_revision)
1594
 
            # TODO: Why set master_branch and local_branch if there's no
1595
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
1596
 
            # 20070504
1597
 
            result.master_branch = target
1598
 
            result.local_branch = None
1599
 
            _run_hooks()
1600
 
            return result
1601
 
 
1602
 
    def _basic_push(self, target, overwrite, stop_revision):
1603
 
        """Basic implementation of push without bound branches or hooks.
1604
 
 
1605
 
        Must be called with self read locked and target write locked.
1606
 
        """
1607
 
        result = PushResult()
1608
 
        result.source_branch = self
1609
 
        result.target_branch = target
1610
 
        result.old_revno, result.old_revid = target.last_revision_info()
1611
 
        try:
1612
 
            target.update_revisions(self, stop_revision)
1613
 
        except DivergedBranches:
1614
 
            if not overwrite:
1615
 
                raise
1616
 
        if overwrite:
1617
 
            target.set_revision_history(self.revision_history())
1618
 
        result.tag_conflicts = self.tags.merge_to(target.tags)
1619
 
        result.new_revno, result.new_revid = target.last_revision_info()
1620
 
        return result
1621
 
 
1622
 
    def get_parent(self):
1623
 
        """See Branch.get_parent."""
1624
 
 
1625
 
        assert self.base[-1] == '/'
1626
 
        parent = self._get_parent_location()
1627
 
        if parent is None:
1628
 
            return parent
1629
 
        # This is an old-format absolute path to a local branch
1630
 
        # turn it into a url
1631
 
        if parent.startswith('/'):
1632
 
            parent = urlutils.local_path_to_url(parent.decode('utf8'))
1633
 
        try:
1634
 
            return urlutils.join(self.base[:-1], parent)
1635
 
        except errors.InvalidURLJoin, e:
1636
 
            raise errors.InaccessibleParent(parent, self.base)
 
1163
    def get_push_location(self):
 
1164
        """See Branch.get_push_location."""
 
1165
        config = bzrlib.config.BranchConfig(self)
 
1166
        push_loc = config.get_user_option('push_location')
 
1167
        return push_loc
1637
1168
 
1638
1169
    def set_push_location(self, location):
1639
1170
        """See Branch.set_push_location."""
1640
 
        self.get_config().set_user_option(
1641
 
            'push_location', location,
1642
 
            store=_mod_config.STORE_LOCATION_NORECURSE)
 
1171
        config = bzrlib.config.LocationConfig(self.base)
 
1172
        config.set_user_option('push_location', location)
1643
1173
 
1644
1174
    @needs_write_lock
1645
1175
    def set_parent(self, url):
1649
1179
        # FIXUP this and get_parent in a future branch format bump:
1650
1180
        # read and rewrite the file, and have the new format code read
1651
1181
        # using .get not .get_utf8. RBC 20060125
1652
 
        if url is not None:
1653
 
            if isinstance(url, unicode):
1654
 
                try: 
1655
 
                    url = url.encode('ascii')
1656
 
                except UnicodeEncodeError:
1657
 
                    raise errors.InvalidURL(url,
1658
 
                        "Urls must be 7-bit ascii, "
1659
 
                        "use bzrlib.urlutils.escape")
1660
 
            url = urlutils.relative_url(self.base, url)
1661
 
        self._set_parent_location(url)
1662
 
 
1663
 
    def _set_parent_location(self, url):
1664
1182
        if url is None:
1665
1183
            self.control_files._transport.delete('parent')
1666
1184
        else:
1667
 
            assert isinstance(url, str)
1668
 
            self.control_files.put_bytes('parent', url + '\n')
 
1185
            self.control_files.put_utf8('parent', url + '\n')
1669
1186
 
1670
 
    @deprecated_function(zero_nine)
1671
1187
    def tree_config(self):
1672
 
        """DEPRECATED; call get_config instead.  
1673
 
        TreeConfig has become part of BranchConfig."""
1674
1188
        return TreeConfig(self)
1675
1189
 
1676
1190
 
1691
1205
                                         _repository=_repository)
1692
1206
        
1693
1207
    @needs_write_lock
1694
 
    def pull(self, source, overwrite=False, stop_revision=None,
1695
 
             run_hooks=True):
1696
 
        """Pull from source into self, updating my master if any.
1697
 
        
1698
 
        :param run_hooks: Private parameter - if false, this branch
1699
 
            is being called because it's the master of the primary branch,
1700
 
            so it should not run its hooks.
1701
 
        """
 
1208
    def pull(self, source, overwrite=False, stop_revision=None):
 
1209
        """Updates branch.pull to be bound branch aware."""
1702
1210
        bound_location = self.get_bound_location()
1703
 
        master_branch = None
1704
 
        if bound_location and source.base != bound_location:
 
1211
        if source.base != bound_location:
1705
1212
            # not pulling from master, so we need to update master.
1706
1213
            master_branch = self.get_master_branch()
1707
 
            master_branch.lock_write()
1708
 
        try:
1709
 
            if master_branch:
1710
 
                # pull from source into master.
1711
 
                master_branch.pull(source, overwrite, stop_revision,
1712
 
                    run_hooks=False)
1713
 
            return super(BzrBranch5, self).pull(source, overwrite,
1714
 
                stop_revision, _hook_master=master_branch,
1715
 
                run_hooks=run_hooks)
1716
 
        finally:
1717
 
            if master_branch:
1718
 
                master_branch.unlock()
 
1214
            if master_branch:
 
1215
                master_branch.pull(source)
 
1216
                source = master_branch
 
1217
        return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
1719
1218
 
1720
1219
    def get_bound_location(self):
1721
1220
        try:
1731
1230
 
1732
1231
        This could memoise the branch, but if thats done
1733
1232
        it must be revalidated on each new lock.
1734
 
        So for now we just don't memoise it.
 
1233
        So for now we just dont memoise it.
1735
1234
        # RBC 20060304 review this decision.
1736
1235
        """
1737
1236
        bound_loc = self.get_bound_location()
1760
1259
 
1761
1260
    @needs_write_lock
1762
1261
    def bind(self, other):
1763
 
        """Bind this branch to the branch other.
 
1262
        """Bind the local branch the other branch.
1764
1263
 
1765
 
        This does not push or pull data between the branches, though it does
1766
 
        check for divergence to raise an error when the branches are not
1767
 
        either the same, or one a prefix of the other. That behaviour may not
1768
 
        be useful, so that check may be removed in future.
1769
 
        
1770
1264
        :param other: The branch to bind to
1771
1265
        :type other: Branch
1772
1266
        """
1777
1271
        #       but binding itself may not be.
1778
1272
        #       Since we *have* to check at commit time, we don't
1779
1273
        #       *need* to check here
1780
 
 
1781
 
        # we want to raise diverged if:
1782
 
        # last_rev is not in the other_last_rev history, AND
1783
 
        # other_last_rev is not in our history, and do it without pulling
1784
 
        # history around
1785
 
        last_rev = _mod_revision.ensure_null(self.last_revision())
1786
 
        if last_rev != _mod_revision.NULL_REVISION:
1787
 
            other.lock_read()
1788
 
            try:
1789
 
                other_last_rev = other.last_revision()
1790
 
                if not _mod_revision.is_null(other_last_rev):
1791
 
                    # neither branch is new, we have to do some work to
1792
 
                    # ascertain diversion.
1793
 
                    remote_graph = other.repository.get_revision_graph(
1794
 
                        other_last_rev)
1795
 
                    local_graph = self.repository.get_revision_graph(last_rev)
1796
 
                    if (last_rev not in remote_graph and
1797
 
                        other_last_rev not in local_graph):
1798
 
                        raise errors.DivergedBranches(self, other)
1799
 
            finally:
1800
 
                other.unlock()
 
1274
        self.pull(other)
 
1275
 
 
1276
        # we are now equal to or a suffix of other.
 
1277
 
 
1278
        # Since we have 'pulled' from the remote location,
 
1279
        # now we should try to pull in the opposite direction
 
1280
        # in case the local tree has more revisions than the
 
1281
        # remote one.
 
1282
        # There may be a different check you could do here
 
1283
        # rather than actually trying to install revisions remotely.
 
1284
        # TODO: capture an exception which indicates the remote branch
 
1285
        #       is not writeable. 
 
1286
        #       If it is up-to-date, this probably should not be a failure
 
1287
        
 
1288
        # lock other for write so the revision-history syncing cannot race
 
1289
        other.lock_write()
 
1290
        try:
 
1291
            other.pull(self)
 
1292
            # if this does not error, other now has the same last rev we do
 
1293
            # it can only error if the pull from other was concurrent with
 
1294
            # a commit to other from someone else.
 
1295
 
 
1296
            # until we ditch revision-history, we need to sync them up:
 
1297
            self.set_revision_history(other.revision_history())
 
1298
            # now other and self are up to date with each other and have the
 
1299
            # same revision-history.
 
1300
        finally:
 
1301
            other.unlock()
 
1302
 
1801
1303
        self.set_bound_location(other.base)
1802
1304
 
1803
1305
    @needs_write_lock
1816
1318
        if master is not None:
1817
1319
            old_tip = self.last_revision()
1818
1320
            self.pull(master, overwrite=True)
1819
 
            if old_tip in self.repository.get_ancestry(
1820
 
                _mod_revision.ensure_null(self.last_revision()),
1821
 
                topo_sorted=False):
 
1321
            if old_tip in self.repository.get_ancestry(self.last_revision()):
1822
1322
                return None
1823
1323
            return old_tip
1824
1324
        return None
1825
1325
 
1826
1326
 
1827
 
class BzrBranchExperimental(BzrBranch5):
1828
 
    """Bzr experimental branch format
1829
 
 
1830
 
    This format has:
1831
 
     - a revision-history file.
1832
 
     - a format string
1833
 
     - a lock dir guarding the branch itself
1834
 
     - all of this stored in a branch/ subdirectory
1835
 
     - works with shared repositories.
1836
 
     - a tag dictionary in the branch
1837
 
 
1838
 
    This format is new in bzr 0.15, but shouldn't be used for real data, 
1839
 
    only for testing.
1840
 
 
1841
 
    This class acts as it's own BranchFormat.
 
1327
class BranchTestProviderAdapter(object):
 
1328
    """A tool to generate a suite testing multiple branch formats at once.
 
1329
 
 
1330
    This is done by copying the test once for each transport and injecting
 
1331
    the transport_server, transport_readonly_server, and branch_format
 
1332
    classes into each copy. Each copy is also given a new id() to make it
 
1333
    easy to identify.
1842
1334
    """
1843
1335
 
1844
 
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1845
 
 
1846
 
    @classmethod
1847
 
    def get_format_string(cls):
1848
 
        """See BranchFormat.get_format_string()."""
1849
 
        return "Bazaar-NG branch format experimental\n"
1850
 
 
1851
 
    @classmethod
1852
 
    def get_format_description(cls):
1853
 
        """See BranchFormat.get_format_description()."""
1854
 
        return "Experimental branch format"
1855
 
 
1856
 
    @classmethod
1857
 
    def get_reference(cls, a_bzrdir):
1858
 
        """Get the target reference of the branch in a_bzrdir.
1859
 
 
1860
 
        format probing must have been completed before calling
1861
 
        this method - it is assumed that the format of the branch
1862
 
        in a_bzrdir is correct.
1863
 
 
1864
 
        :param a_bzrdir: The bzrdir to get the branch data from.
1865
 
        :return: None if the branch is not a reference branch.
1866
 
        """
1867
 
        return None
1868
 
 
1869
 
    @classmethod
1870
 
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1871
 
            lock_class):
1872
 
        branch_transport = a_bzrdir.get_branch_transport(cls)
1873
 
        control_files = lockable_files.LockableFiles(branch_transport,
1874
 
            lock_filename, lock_class)
1875
 
        control_files.create_lock()
1876
 
        control_files.lock_write()
1877
 
        try:
1878
 
            for filename, content in utf8_files:
1879
 
                control_files.put_utf8(filename, content)
1880
 
        finally:
1881
 
            control_files.unlock()
1882
 
        
1883
 
    @classmethod
1884
 
    def initialize(cls, a_bzrdir):
1885
 
        """Create a branch of this format in a_bzrdir."""
1886
 
        utf8_files = [('format', cls.get_format_string()),
1887
 
                      ('revision-history', ''),
1888
 
                      ('branch-name', ''),
1889
 
                      ('tags', ''),
1890
 
                      ]
1891
 
        cls._initialize_control_files(a_bzrdir, utf8_files,
1892
 
            'lock', lockdir.LockDir)
1893
 
        return cls.open(a_bzrdir, _found=True)
1894
 
 
1895
 
    @classmethod
1896
 
    def open(cls, a_bzrdir, _found=False):
1897
 
        """Return the branch object for a_bzrdir
1898
 
 
1899
 
        _found is a private parameter, do not use it. It is used to indicate
1900
 
               if format probing has already be done.
1901
 
        """
1902
 
        if not _found:
1903
 
            format = BranchFormat.find_format(a_bzrdir)
1904
 
            assert format.__class__ == cls
1905
 
        transport = a_bzrdir.get_branch_transport(None)
1906
 
        control_files = lockable_files.LockableFiles(transport, 'lock',
1907
 
                                                     lockdir.LockDir)
1908
 
        return cls(_format=cls,
1909
 
            _control_files=control_files,
1910
 
            a_bzrdir=a_bzrdir,
1911
 
            _repository=a_bzrdir.find_repository())
1912
 
 
1913
 
    @classmethod
1914
 
    def is_supported(cls):
1915
 
        return True
1916
 
 
1917
 
    def _make_tags(self):
1918
 
        return BasicTags(self)
1919
 
 
1920
 
    @classmethod
1921
 
    def supports_tags(cls):
1922
 
        return True
1923
 
 
1924
 
 
1925
 
BranchFormat.register_format(BzrBranchExperimental)
1926
 
 
1927
 
 
1928
 
class BzrBranch6(BzrBranch5):
1929
 
 
1930
 
    @needs_read_lock
1931
 
    def last_revision_info(self):
1932
 
        revision_string = self.control_files.get('last-revision').read()
1933
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1934
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
1935
 
        revno = int(revno)
1936
 
        return revno, revision_id
1937
 
 
1938
 
    def last_revision(self):
1939
 
        """Return last revision id, or None"""
1940
 
        revision_id = self.last_revision_info()[1]
1941
 
        if revision_id == _mod_revision.NULL_REVISION:
1942
 
            revision_id = None
1943
 
        return revision_id
1944
 
 
1945
 
    def _write_last_revision_info(self, revno, revision_id):
1946
 
        """Simply write out the revision id, with no checks.
1947
 
 
1948
 
        Use set_last_revision_info to perform this safely.
1949
 
 
1950
 
        Does not update the revision_history cache.
1951
 
        Intended to be called by set_last_revision_info and
1952
 
        _write_revision_history.
1953
 
        """
1954
 
        if revision_id is None:
1955
 
            revision_id = 'null:'
1956
 
        out_string = '%d %s\n' % (revno, revision_id)
1957
 
        self.control_files.put_bytes('last-revision', out_string)
1958
 
 
1959
 
    @needs_write_lock
1960
 
    def set_last_revision_info(self, revno, revision_id):
1961
 
        revision_id = osutils.safe_revision_id(revision_id)
1962
 
        if self._get_append_revisions_only():
1963
 
            self._check_history_violation(revision_id)
1964
 
        self._write_last_revision_info(revno, revision_id)
1965
 
        self._clear_cached_state()
1966
 
 
1967
 
    def _check_history_violation(self, revision_id):
1968
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
1969
 
        if _mod_revision.is_null(last_revision):
1970
 
            return
1971
 
        if last_revision not in self._lefthand_history(revision_id):
1972
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
1973
 
 
1974
 
    def _gen_revision_history(self):
1975
 
        """Generate the revision history from last revision
1976
 
        """
1977
 
        history = list(self.repository.iter_reverse_revision_history(
1978
 
            self.last_revision()))
1979
 
        history.reverse()
1980
 
        return history
1981
 
 
1982
 
    def _write_revision_history(self, history):
1983
 
        """Factored out of set_revision_history.
1984
 
 
1985
 
        This performs the actual writing to disk, with format-specific checks.
1986
 
        It is intended to be called by BzrBranch5.set_revision_history.
1987
 
        """
1988
 
        if len(history) == 0:
1989
 
            last_revision = 'null:'
1990
 
        else:
1991
 
            if history != self._lefthand_history(history[-1]):
1992
 
                raise errors.NotLefthandHistory(history)
1993
 
            last_revision = history[-1]
1994
 
        if self._get_append_revisions_only():
1995
 
            self._check_history_violation(last_revision)
1996
 
        self._write_last_revision_info(len(history), last_revision)
1997
 
 
1998
 
    @needs_write_lock
1999
 
    def append_revision(self, *revision_ids):
2000
 
        revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
2001
 
        if len(revision_ids) == 0:
2002
 
            return
2003
 
        prev_revno, prev_revision = self.last_revision_info()
2004
 
        for revision in self.repository.get_revisions(revision_ids):
2005
 
            if prev_revision == _mod_revision.NULL_REVISION:
2006
 
                if revision.parent_ids != []:
2007
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
2008
 
                                                         revision.revision_id)
2009
 
            else:
2010
 
                if revision.parent_ids[0] != prev_revision:
2011
 
                    raise errors.NotLeftParentDescendant(self, prev_revision,
2012
 
                                                         revision.revision_id)
2013
 
            prev_revision = revision.revision_id
2014
 
        self.set_last_revision_info(prev_revno + len(revision_ids),
2015
 
                                    revision_ids[-1])
2016
 
 
2017
 
    @needs_write_lock
2018
 
    def _set_parent_location(self, url):
2019
 
        """Set the parent branch"""
2020
 
        self._set_config_location('parent_location', url, make_relative=True)
2021
 
 
2022
 
    @needs_read_lock
2023
 
    def _get_parent_location(self):
2024
 
        """Set the parent branch"""
2025
 
        return self._get_config_location('parent_location')
2026
 
 
2027
 
    def set_push_location(self, location):
2028
 
        """See Branch.set_push_location."""
2029
 
        self._set_config_location('push_location', location)
2030
 
 
2031
 
    def set_bound_location(self, location):
2032
 
        """See Branch.set_push_location."""
2033
 
        result = None
2034
 
        config = self.get_config()
2035
 
        if location is None:
2036
 
            if config.get_user_option('bound') != 'True':
2037
 
                return False
2038
 
            else:
2039
 
                config.set_user_option('bound', 'False', warn_masked=True)
2040
 
                return True
2041
 
        else:
2042
 
            self._set_config_location('bound_location', location,
2043
 
                                      config=config)
2044
 
            config.set_user_option('bound', 'True', warn_masked=True)
2045
 
        return True
2046
 
 
2047
 
    def _get_bound_location(self, bound):
2048
 
        """Return the bound location in the config file.
2049
 
 
2050
 
        Return None if the bound parameter does not match"""
2051
 
        config = self.get_config()
2052
 
        config_bound = (config.get_user_option('bound') == 'True')
2053
 
        if config_bound != bound:
2054
 
            return None
2055
 
        return self._get_config_location('bound_location', config=config)
2056
 
 
2057
 
    def get_bound_location(self):
2058
 
        """See Branch.set_push_location."""
2059
 
        return self._get_bound_location(True)
2060
 
 
2061
 
    def get_old_bound_location(self):
2062
 
        """See Branch.get_old_bound_location"""
2063
 
        return self._get_bound_location(False)
2064
 
 
2065
 
    def set_append_revisions_only(self, enabled):
2066
 
        if enabled:
2067
 
            value = 'True'
2068
 
        else:
2069
 
            value = 'False'
2070
 
        self.get_config().set_user_option('append_revisions_only', value,
2071
 
            warn_masked=True)
2072
 
 
2073
 
    def _get_append_revisions_only(self):
2074
 
        value = self.get_config().get_user_option('append_revisions_only')
2075
 
        return value == 'True'
2076
 
 
2077
 
    def _synchronize_history(self, destination, revision_id):
2078
 
        """Synchronize last revision and revision history between branches.
2079
 
 
2080
 
        This version is most efficient when the destination is also a
2081
 
        BzrBranch6, but works for BzrBranch5, as long as the destination's
2082
 
        repository contains all the lefthand ancestors of the intended
2083
 
        last_revision.  If not, set_last_revision_info will fail.
2084
 
 
2085
 
        :param destination: The branch to copy the history into
2086
 
        :param revision_id: The revision-id to truncate history at.  May
2087
 
          be None to copy complete history.
2088
 
        """
2089
 
        if revision_id is None:
2090
 
            revno, revision_id = self.last_revision_info()
2091
 
        else:
2092
 
            # To figure out the revno for a random revision, we need to build
2093
 
            # the revision history, and count its length.
2094
 
            # We don't care about the order, just how long it is.
2095
 
            # Alternatively, we could start at the current location, and count
2096
 
            # backwards. But there is no guarantee that we will find it since
2097
 
            # it may be a merged revision.
2098
 
            revno = len(list(self.repository.iter_reverse_revision_history(
2099
 
                                                                revision_id)))
2100
 
        destination.set_last_revision_info(revno, revision_id)
2101
 
 
2102
 
    def _make_tags(self):
2103
 
        return BasicTags(self)
 
1336
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1337
        self._transport_server = transport_server
 
1338
        self._transport_readonly_server = transport_readonly_server
 
1339
        self._formats = formats
 
1340
    
 
1341
    def adapt(self, test):
 
1342
        result = TestSuite()
 
1343
        for branch_format, bzrdir_format in self._formats:
 
1344
            new_test = deepcopy(test)
 
1345
            new_test.transport_server = self._transport_server
 
1346
            new_test.transport_readonly_server = self._transport_readonly_server
 
1347
            new_test.bzrdir_format = bzrdir_format
 
1348
            new_test.branch_format = branch_format
 
1349
            def make_new_test_id():
 
1350
                new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
 
1351
                return lambda: new_id
 
1352
            new_test.id = make_new_test_id()
 
1353
            result.addTest(new_test)
 
1354
        return result
2104
1355
 
2105
1356
 
2106
1357
######################################################################
2107
 
# results of operations
2108
 
 
2109
 
 
2110
 
class _Result(object):
2111
 
 
2112
 
    def _show_tag_conficts(self, to_file):
2113
 
        if not getattr(self, 'tag_conflicts', None):
2114
 
            return
2115
 
        to_file.write('Conflicting tags:\n')
2116
 
        for name, value1, value2 in self.tag_conflicts:
2117
 
            to_file.write('    %s\n' % (name, ))
2118
 
 
2119
 
 
2120
 
class PullResult(_Result):
2121
 
    """Result of a Branch.pull operation.
2122
 
 
2123
 
    :ivar old_revno: Revision number before pull.
2124
 
    :ivar new_revno: Revision number after pull.
2125
 
    :ivar old_revid: Tip revision id before pull.
2126
 
    :ivar new_revid: Tip revision id after pull.
2127
 
    :ivar source_branch: Source (local) branch object.
2128
 
    :ivar master_branch: Master branch of the target, or None.
2129
 
    :ivar target_branch: Target/destination branch object.
2130
 
    """
2131
 
 
2132
 
    def __int__(self):
2133
 
        # DEPRECATED: pull used to return the change in revno
2134
 
        return self.new_revno - self.old_revno
2135
 
 
2136
 
    def report(self, to_file):
2137
 
        if self.old_revid == self.new_revid:
2138
 
            to_file.write('No revisions to pull.\n')
2139
 
        else:
2140
 
            to_file.write('Now on revision %d.\n' % self.new_revno)
2141
 
        self._show_tag_conficts(to_file)
2142
 
 
2143
 
 
2144
 
class PushResult(_Result):
2145
 
    """Result of a Branch.push operation.
2146
 
 
2147
 
    :ivar old_revno: Revision number before push.
2148
 
    :ivar new_revno: Revision number after push.
2149
 
    :ivar old_revid: Tip revision id before push.
2150
 
    :ivar new_revid: Tip revision id after push.
2151
 
    :ivar source_branch: Source branch object.
2152
 
    :ivar master_branch: Master branch of the target, or None.
2153
 
    :ivar target_branch: Target/destination branch object.
2154
 
    """
2155
 
 
2156
 
    def __int__(self):
2157
 
        # DEPRECATED: push used to return the change in revno
2158
 
        return self.new_revno - self.old_revno
2159
 
 
2160
 
    def report(self, to_file):
2161
 
        """Write a human-readable description of the result."""
2162
 
        if self.old_revid == self.new_revid:
2163
 
            to_file.write('No new revisions to push.\n')
2164
 
        else:
2165
 
            to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2166
 
        self._show_tag_conficts(to_file)
2167
 
 
2168
 
 
2169
 
class BranchCheckResult(object):
2170
 
    """Results of checking branch consistency.
2171
 
 
2172
 
    :see: Branch.check
2173
 
    """
2174
 
 
2175
 
    def __init__(self, branch):
2176
 
        self.branch = branch
2177
 
 
2178
 
    def report_results(self, verbose):
2179
 
        """Report the check results via trace.note.
2180
 
        
2181
 
        :param verbose: Requests more detailed display of what was checked,
2182
 
            if any.
2183
 
        """
2184
 
        note('checked branch %s format %s',
2185
 
             self.branch.base,
2186
 
             self.branch._format)
2187
 
 
2188
 
 
2189
 
class Converter5to6(object):
2190
 
    """Perform an in-place upgrade of format 5 to format 6"""
2191
 
 
2192
 
    def convert(self, branch):
2193
 
        # Data for 5 and 6 can peacefully coexist.
2194
 
        format = BzrBranchFormat6()
2195
 
        new_branch = format.open(branch.bzrdir, _found=True)
2196
 
 
2197
 
        # Copy source data into target
2198
 
        new_branch.set_last_revision_info(*branch.last_revision_info())
2199
 
        new_branch.set_parent(branch.get_parent())
2200
 
        new_branch.set_bound_location(branch.get_bound_location())
2201
 
        new_branch.set_push_location(branch.get_push_location())
2202
 
 
2203
 
        # New branch has no tags by default
2204
 
        new_branch.tags._set_tag_dict({})
2205
 
 
2206
 
        # Copying done; now update target format
2207
 
        new_branch.control_files.put_utf8('format',
2208
 
            format.get_format_string())
2209
 
 
2210
 
        # Clean up old files
2211
 
        new_branch.control_files._transport.delete('revision-history')
2212
 
        try:
2213
 
            branch.set_parent(None)
2214
 
        except NoSuchFile:
2215
 
            pass
2216
 
        branch.set_bound_location(None)
 
1358
# predicates
 
1359
 
 
1360
 
 
1361
@deprecated_function(zero_eight)
 
1362
def ScratchBranch(*args, **kwargs):
 
1363
    """See bzrlib.bzrdir.ScratchDir."""
 
1364
    d = ScratchDir(*args, **kwargs)
 
1365
    return d.open_branch()
 
1366
 
 
1367
 
 
1368
@deprecated_function(zero_eight)
 
1369
def is_control_file(*args, **kwargs):
 
1370
    """See bzrlib.workingtree.is_control_file."""
 
1371
    return bzrlib.workingtree.is_control_file(*args, **kwargs)