~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Vincent Ladeuil
  • Date: 2011-10-07 14:51:42 UTC
  • mto: (6015.33.11 2.4)
  • mto: This revision was merged to the branch mainline in revision 6206.
  • Revision ID: v.ladeuil+lp@free.fr-20111007145142-oyvn7y3fhz174gs5
Here are the useful print statements used to debug the previous commit.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2011 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from bzrlib.lazy_import import lazy_import
 
18
lazy_import(globals(), """
 
19
import itertools
 
20
import time
 
21
 
 
22
from bzrlib import (
 
23
    bzrdir,
 
24
    config,
 
25
    controldir,
 
26
    debug,
 
27
    generate_ids,
 
28
    graph,
 
29
    lockable_files,
 
30
    lockdir,
 
31
    osutils,
 
32
    revision as _mod_revision,
 
33
    testament as _mod_testament,
 
34
    tsort,
 
35
    gpg,
 
36
    )
 
37
from bzrlib.bundle import serializer
 
38
""")
 
39
 
 
40
from bzrlib import (
 
41
    errors,
 
42
    registry,
 
43
    symbol_versioning,
 
44
    ui,
 
45
    )
 
46
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
47
from bzrlib.inter import InterObject
 
48
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
 
49
from bzrlib.trace import (
 
50
    log_exception_quietly, note, mutter, mutter_callsite, warning)
 
51
 
 
52
 
 
53
# Old formats display a warning, but only once
 
54
_deprecation_warning_done = False
 
55
 
 
56
 
 
57
class IsInWriteGroupError(errors.InternalBzrError):
 
58
 
 
59
    _fmt = "May not refresh_data of repo %(repo)s while in a write group."
 
60
 
 
61
    def __init__(self, repo):
 
62
        errors.InternalBzrError.__init__(self, repo=repo)
 
63
 
 
64
 
 
65
class CommitBuilder(object):
 
66
    """Provides an interface to build up a commit.
 
67
 
 
68
    This allows describing a tree to be committed without needing to
 
69
    know the internals of the format of the repository.
 
70
    """
 
71
 
 
72
    # all clients should supply tree roots.
 
73
    record_root_entry = True
 
74
    # whether this commit builder supports the record_entry_contents interface
 
75
    supports_record_entry_contents = False
 
76
 
 
77
    def __init__(self, repository, parents, config, timestamp=None,
 
78
                 timezone=None, committer=None, revprops=None,
 
79
                 revision_id=None, lossy=False):
 
80
        """Initiate a CommitBuilder.
 
81
 
 
82
        :param repository: Repository to commit to.
 
83
        :param parents: Revision ids of the parents of the new revision.
 
84
        :param timestamp: Optional timestamp recorded for commit.
 
85
        :param timezone: Optional timezone for timestamp.
 
86
        :param committer: Optional committer to set for commit.
 
87
        :param revprops: Optional dictionary of revision properties.
 
88
        :param revision_id: Optional revision id.
 
89
        :param lossy: Whether to discard data that can not be natively
 
90
            represented, when pushing to a foreign VCS 
 
91
        """
 
92
        self._config = config
 
93
        self._lossy = lossy
 
94
 
 
95
        if committer is None:
 
96
            self._committer = self._config.username()
 
97
        elif not isinstance(committer, unicode):
 
98
            self._committer = committer.decode() # throw if non-ascii
 
99
        else:
 
100
            self._committer = committer
 
101
 
 
102
        self._new_revision_id = revision_id
 
103
        self.parents = parents
 
104
        self.repository = repository
 
105
 
 
106
        self._revprops = {}
 
107
        if revprops is not None:
 
108
            self._validate_revprops(revprops)
 
109
            self._revprops.update(revprops)
 
110
 
 
111
        if timestamp is None:
 
112
            timestamp = time.time()
 
113
        # Restrict resolution to 1ms
 
114
        self._timestamp = round(timestamp, 3)
 
115
 
 
116
        if timezone is None:
 
117
            self._timezone = osutils.local_time_offset()
 
118
        else:
 
119
            self._timezone = int(timezone)
 
120
 
 
121
        self._generate_revision_if_needed()
 
122
 
 
123
    def any_changes(self):
 
124
        """Return True if any entries were changed.
 
125
 
 
126
        This includes merge-only changes. It is the core for the --unchanged
 
127
        detection in commit.
 
128
 
 
129
        :return: True if any changes have occured.
 
130
        """
 
131
        raise NotImplementedError(self.any_changes)
 
132
 
 
133
    def _validate_unicode_text(self, text, context):
 
134
        """Verify things like commit messages don't have bogus characters."""
 
135
        if '\r' in text:
 
136
            raise ValueError('Invalid value for %s: %r' % (context, text))
 
137
 
 
138
    def _validate_revprops(self, revprops):
 
139
        for key, value in revprops.iteritems():
 
140
            # We know that the XML serializers do not round trip '\r'
 
141
            # correctly, so refuse to accept them
 
142
            if not isinstance(value, basestring):
 
143
                raise ValueError('revision property (%s) is not a valid'
 
144
                                 ' (unicode) string: %r' % (key, value))
 
145
            self._validate_unicode_text(value,
 
146
                                        'revision property (%s)' % (key,))
 
147
 
 
148
    def commit(self, message):
 
149
        """Make the actual commit.
 
150
 
 
151
        :return: The revision id of the recorded revision.
 
152
        """
 
153
        raise NotImplementedError(self.commit)
 
154
 
 
155
    def abort(self):
 
156
        """Abort the commit that is being built.
 
157
        """
 
158
        raise NotImplementedError(self.abort)
 
159
 
 
160
    def revision_tree(self):
 
161
        """Return the tree that was just committed.
 
162
 
 
163
        After calling commit() this can be called to get a
 
164
        RevisionTree representing the newly committed tree. This is
 
165
        preferred to calling Repository.revision_tree() because that may
 
166
        require deserializing the inventory, while we already have a copy in
 
167
        memory.
 
168
        """
 
169
        raise NotImplementedError(self.revision_tree)
 
170
 
 
171
    def finish_inventory(self):
 
172
        """Tell the builder that the inventory is finished.
 
173
 
 
174
        :return: The inventory id in the repository, which can be used with
 
175
            repository.get_inventory.
 
176
        """
 
177
        raise NotImplementedError(self.finish_inventory)
 
178
 
 
179
    def _gen_revision_id(self):
 
180
        """Return new revision-id."""
 
181
        return generate_ids.gen_revision_id(self._committer, self._timestamp)
 
182
 
 
183
    def _generate_revision_if_needed(self):
 
184
        """Create a revision id if None was supplied.
 
185
 
 
186
        If the repository can not support user-specified revision ids
 
187
        they should override this function and raise CannotSetRevisionId
 
188
        if _new_revision_id is not None.
 
189
 
 
190
        :raises: CannotSetRevisionId
 
191
        """
 
192
        if self._new_revision_id is None:
 
193
            self._new_revision_id = self._gen_revision_id()
 
194
            self.random_revid = True
 
195
        else:
 
196
            self.random_revid = False
 
197
 
 
198
    def will_record_deletes(self):
 
199
        """Tell the commit builder that deletes are being notified.
 
200
 
 
201
        This enables the accumulation of an inventory delta; for the resulting
 
202
        commit to be valid, deletes against the basis MUST be recorded via
 
203
        builder.record_delete().
 
204
        """
 
205
        raise NotImplementedError(self.will_record_deletes)
 
206
 
 
207
    def record_iter_changes(self, tree, basis_revision_id, iter_changes):
 
208
        """Record a new tree via iter_changes.
 
209
 
 
210
        :param tree: The tree to obtain text contents from for changed objects.
 
211
        :param basis_revision_id: The revision id of the tree the iter_changes
 
212
            has been generated against. Currently assumed to be the same
 
213
            as self.parents[0] - if it is not, errors may occur.
 
214
        :param iter_changes: An iter_changes iterator with the changes to apply
 
215
            to basis_revision_id. The iterator must not include any items with
 
216
            a current kind of None - missing items must be either filtered out
 
217
            or errored-on beefore record_iter_changes sees the item.
 
218
        :return: A generator of (file_id, relpath, fs_hash) tuples for use with
 
219
            tree._observed_sha1.
 
220
        """
 
221
        raise NotImplementedError(self.record_iter_changes)
 
222
 
 
223
 
 
224
class RepositoryWriteLockResult(LogicalLockResult):
 
225
    """The result of write locking a repository.
 
226
 
 
227
    :ivar repository_token: The token obtained from the underlying lock, or
 
228
        None.
 
229
    :ivar unlock: A callable which will unlock the lock.
 
230
    """
 
231
 
 
232
    def __init__(self, unlock, repository_token):
 
233
        LogicalLockResult.__init__(self, unlock)
 
234
        self.repository_token = repository_token
 
235
 
 
236
    def __repr__(self):
 
237
        return "RepositoryWriteLockResult(%s, %s)" % (self.repository_token,
 
238
            self.unlock)
 
239
 
 
240
 
 
241
######################################################################
 
242
# Repositories
 
243
 
 
244
 
 
245
class Repository(_RelockDebugMixin, controldir.ControlComponent):
 
246
    """Repository holding history for one or more branches.
 
247
 
 
248
    The repository holds and retrieves historical information including
 
249
    revisions and file history.  It's normally accessed only by the Branch,
 
250
    which views a particular line of development through that history.
 
251
 
 
252
    See VersionedFileRepository in bzrlib.vf_repository for the
 
253
    base class for most Bazaar repositories.
 
254
    """
 
255
 
 
256
    def abort_write_group(self, suppress_errors=False):
 
257
        """Commit the contents accrued within the current write group.
 
258
 
 
259
        :param suppress_errors: if true, abort_write_group will catch and log
 
260
            unexpected errors that happen during the abort, rather than
 
261
            allowing them to propagate.  Defaults to False.
 
262
 
 
263
        :seealso: start_write_group.
 
264
        """
 
265
        if self._write_group is not self.get_transaction():
 
266
            # has an unlock or relock occured ?
 
267
            if suppress_errors:
 
268
                mutter(
 
269
                '(suppressed) mismatched lock context and write group. %r, %r',
 
270
                self._write_group, self.get_transaction())
 
271
                return
 
272
            raise errors.BzrError(
 
273
                'mismatched lock context and write group. %r, %r' %
 
274
                (self._write_group, self.get_transaction()))
 
275
        try:
 
276
            self._abort_write_group()
 
277
        except Exception, exc:
 
278
            self._write_group = None
 
279
            if not suppress_errors:
 
280
                raise
 
281
            mutter('abort_write_group failed')
 
282
            log_exception_quietly()
 
283
            note('bzr: ERROR (ignored): %s', exc)
 
284
        self._write_group = None
 
285
 
 
286
    def _abort_write_group(self):
 
287
        """Template method for per-repository write group cleanup.
 
288
 
 
289
        This is called during abort before the write group is considered to be
 
290
        finished and should cleanup any internal state accrued during the write
 
291
        group. There is no requirement that data handed to the repository be
 
292
        *not* made available - this is not a rollback - but neither should any
 
293
        attempt be made to ensure that data added is fully commited. Abort is
 
294
        invoked when an error has occured so futher disk or network operations
 
295
        may not be possible or may error and if possible should not be
 
296
        attempted.
 
297
        """
 
298
 
 
299
    def add_fallback_repository(self, repository):
 
300
        """Add a repository to use for looking up data not held locally.
 
301
 
 
302
        :param repository: A repository.
 
303
        """
 
304
        raise NotImplementedError(self.add_fallback_repository)
 
305
 
 
306
    def _check_fallback_repository(self, repository):
 
307
        """Check that this repository can fallback to repository safely.
 
308
 
 
309
        Raise an error if not.
 
310
 
 
311
        :param repository: A repository to fallback to.
 
312
        """
 
313
        return InterRepository._assert_same_model(self, repository)
 
314
 
 
315
    def all_revision_ids(self):
 
316
        """Returns a list of all the revision ids in the repository.
 
317
 
 
318
        This is conceptually deprecated because code should generally work on
 
319
        the graph reachable from a particular revision, and ignore any other
 
320
        revisions that might be present.  There is no direct replacement
 
321
        method.
 
322
        """
 
323
        if 'evil' in debug.debug_flags:
 
324
            mutter_callsite(2, "all_revision_ids is linear with history.")
 
325
        return self._all_revision_ids()
 
326
 
 
327
    def _all_revision_ids(self):
 
328
        """Returns a list of all the revision ids in the repository.
 
329
 
 
330
        These are in as much topological order as the underlying store can
 
331
        present.
 
332
        """
 
333
        raise NotImplementedError(self._all_revision_ids)
 
334
 
 
335
    def break_lock(self):
 
336
        """Break a lock if one is present from another instance.
 
337
 
 
338
        Uses the ui factory to ask for confirmation if the lock may be from
 
339
        an active process.
 
340
        """
 
341
        self.control_files.break_lock()
 
342
 
 
343
    @needs_read_lock
 
344
    def _eliminate_revisions_not_present(self, revision_ids):
 
345
        """Check every revision id in revision_ids to see if we have it.
 
346
 
 
347
        Returns a set of the present revisions.
 
348
        """
 
349
        result = []
 
350
        graph = self.get_graph()
 
351
        parent_map = graph.get_parent_map(revision_ids)
 
352
        # The old API returned a list, should this actually be a set?
 
353
        return parent_map.keys()
 
354
 
 
355
    @staticmethod
 
356
    def create(a_bzrdir):
 
357
        """Construct the current default format repository in a_bzrdir."""
 
358
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
 
359
 
 
360
    def __init__(self, _format, a_bzrdir, control_files):
 
361
        """instantiate a Repository.
 
362
 
 
363
        :param _format: The format of the repository on disk.
 
364
        :param a_bzrdir: The BzrDir of the repository.
 
365
        :param control_files: Control files to use for locking, etc.
 
366
        """
 
367
        # In the future we will have a single api for all stores for
 
368
        # getting file texts, inventories and revisions, then
 
369
        # this construct will accept instances of those things.
 
370
        super(Repository, self).__init__()
 
371
        self._format = _format
 
372
        # the following are part of the public API for Repository:
 
373
        self.bzrdir = a_bzrdir
 
374
        self.control_files = control_files
 
375
        self._transport = control_files._transport
 
376
        self.base = self._transport.base
 
377
        # for tests
 
378
        self._write_group = None
 
379
        # Additional places to query for data.
 
380
        self._fallback_repositories = []
 
381
 
 
382
    @property
 
383
    def user_transport(self):
 
384
        return self.bzrdir.user_transport
 
385
 
 
386
    @property
 
387
    def control_transport(self):
 
388
        return self._transport
 
389
 
 
390
    def __repr__(self):
 
391
        if self._fallback_repositories:
 
392
            return '%s(%r, fallback_repositories=%r)' % (
 
393
                self.__class__.__name__,
 
394
                self.base,
 
395
                self._fallback_repositories)
 
396
        else:
 
397
            return '%s(%r)' % (self.__class__.__name__,
 
398
                               self.base)
 
399
 
 
400
    def _has_same_fallbacks(self, other_repo):
 
401
        """Returns true if the repositories have the same fallbacks."""
 
402
        my_fb = self._fallback_repositories
 
403
        other_fb = other_repo._fallback_repositories
 
404
        if len(my_fb) != len(other_fb):
 
405
            return False
 
406
        for f, g in zip(my_fb, other_fb):
 
407
            if not f.has_same_location(g):
 
408
                return False
 
409
        return True
 
410
 
 
411
    def has_same_location(self, other):
 
412
        """Returns a boolean indicating if this repository is at the same
 
413
        location as another repository.
 
414
 
 
415
        This might return False even when two repository objects are accessing
 
416
        the same physical repository via different URLs.
 
417
        """
 
418
        if self.__class__ is not other.__class__:
 
419
            return False
 
420
        return (self._transport.base == other._transport.base)
 
421
 
 
422
    def is_in_write_group(self):
 
423
        """Return True if there is an open write group.
 
424
 
 
425
        :seealso: start_write_group.
 
426
        """
 
427
        return self._write_group is not None
 
428
 
 
429
    def is_locked(self):
 
430
        return self.control_files.is_locked()
 
431
 
 
432
    def is_write_locked(self):
 
433
        """Return True if this object is write locked."""
 
434
        return self.is_locked() and self.control_files._lock_mode == 'w'
 
435
 
 
436
    def lock_write(self, token=None):
 
437
        """Lock this repository for writing.
 
438
 
 
439
        This causes caching within the repository obejct to start accumlating
 
440
        data during reads, and allows a 'write_group' to be obtained. Write
 
441
        groups must be used for actual data insertion.
 
442
 
 
443
        A token should be passed in if you know that you have locked the object
 
444
        some other way, and need to synchronise this object's state with that
 
445
        fact.
 
446
 
 
447
        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
 
448
 
 
449
        :param token: if this is already locked, then lock_write will fail
 
450
            unless the token matches the existing lock.
 
451
        :returns: a token if this instance supports tokens, otherwise None.
 
452
        :raises TokenLockingNotSupported: when a token is given but this
 
453
            instance doesn't support using token locks.
 
454
        :raises MismatchedToken: if the specified token doesn't match the token
 
455
            of the existing lock.
 
456
        :seealso: start_write_group.
 
457
        :return: A RepositoryWriteLockResult.
 
458
        """
 
459
        locked = self.is_locked()
 
460
        token = self.control_files.lock_write(token=token)
 
461
        if not locked:
 
462
            self._warn_if_deprecated()
 
463
            self._note_lock('w')
 
464
            for repo in self._fallback_repositories:
 
465
                # Writes don't affect fallback repos
 
466
                repo.lock_read()
 
467
            self._refresh_data()
 
468
        return RepositoryWriteLockResult(self.unlock, token)
 
469
 
 
470
    def lock_read(self):
 
471
        """Lock the repository for read operations.
 
472
 
 
473
        :return: An object with an unlock method which will release the lock
 
474
            obtained.
 
475
        """
 
476
        locked = self.is_locked()
 
477
        self.control_files.lock_read()
 
478
        if not locked:
 
479
            self._warn_if_deprecated()
 
480
            self._note_lock('r')
 
481
            for repo in self._fallback_repositories:
 
482
                repo.lock_read()
 
483
            self._refresh_data()
 
484
        return LogicalLockResult(self.unlock)
 
485
 
 
486
    def get_physical_lock_status(self):
 
487
        return self.control_files.get_physical_lock_status()
 
488
 
 
489
    def leave_lock_in_place(self):
 
490
        """Tell this repository not to release the physical lock when this
 
491
        object is unlocked.
 
492
 
 
493
        If lock_write doesn't return a token, then this method is not supported.
 
494
        """
 
495
        self.control_files.leave_in_place()
 
496
 
 
497
    def dont_leave_lock_in_place(self):
 
498
        """Tell this repository to release the physical lock when this
 
499
        object is unlocked, even if it didn't originally acquire it.
 
500
 
 
501
        If lock_write doesn't return a token, then this method is not supported.
 
502
        """
 
503
        self.control_files.dont_leave_in_place()
 
504
 
 
505
    @needs_read_lock
 
506
    def gather_stats(self, revid=None, committers=None):
 
507
        """Gather statistics from a revision id.
 
508
 
 
509
        :param revid: The revision id to gather statistics from, if None, then
 
510
            no revision specific statistics are gathered.
 
511
        :param committers: Optional parameter controlling whether to grab
 
512
            a count of committers from the revision specific statistics.
 
513
        :return: A dictionary of statistics. Currently this contains:
 
514
            committers: The number of committers if requested.
 
515
            firstrev: A tuple with timestamp, timezone for the penultimate left
 
516
                most ancestor of revid, if revid is not the NULL_REVISION.
 
517
            latestrev: A tuple with timestamp, timezone for revid, if revid is
 
518
                not the NULL_REVISION.
 
519
            revisions: The total revision count in the repository.
 
520
            size: An estimate disk size of the repository in bytes.
 
521
        """
 
522
        result = {}
 
523
        if revid and committers:
 
524
            result['committers'] = 0
 
525
        if revid and revid != _mod_revision.NULL_REVISION:
 
526
            graph = self.get_graph()
 
527
            if committers:
 
528
                all_committers = set()
 
529
            revisions = [r for (r, p) in graph.iter_ancestry([revid])
 
530
                        if r != _mod_revision.NULL_REVISION]
 
531
            last_revision = None
 
532
            if not committers:
 
533
                # ignore the revisions in the middle - just grab first and last
 
534
                revisions = revisions[0], revisions[-1]
 
535
            for revision in self.get_revisions(revisions):
 
536
                if not last_revision:
 
537
                    last_revision = revision
 
538
                if committers:
 
539
                    all_committers.add(revision.committer)
 
540
            first_revision = revision
 
541
            if committers:
 
542
                result['committers'] = len(all_committers)
 
543
            result['firstrev'] = (first_revision.timestamp,
 
544
                first_revision.timezone)
 
545
            result['latestrev'] = (last_revision.timestamp,
 
546
                last_revision.timezone)
 
547
        return result
 
548
 
 
549
    def find_branches(self, using=False):
 
550
        """Find branches underneath this repository.
 
551
 
 
552
        This will include branches inside other branches.
 
553
 
 
554
        :param using: If True, list only branches using this repository.
 
555
        """
 
556
        if using and not self.is_shared():
 
557
            return self.bzrdir.list_branches()
 
558
        class Evaluator(object):
 
559
 
 
560
            def __init__(self):
 
561
                self.first_call = True
 
562
 
 
563
            def __call__(self, bzrdir):
 
564
                # On the first call, the parameter is always the bzrdir
 
565
                # containing the current repo.
 
566
                if not self.first_call:
 
567
                    try:
 
568
                        repository = bzrdir.open_repository()
 
569
                    except errors.NoRepositoryPresent:
 
570
                        pass
 
571
                    else:
 
572
                        return False, ([], repository)
 
573
                self.first_call = False
 
574
                value = (bzrdir.list_branches(), None)
 
575
                return True, value
 
576
 
 
577
        ret = []
 
578
        for branches, repository in bzrdir.BzrDir.find_bzrdirs(
 
579
                self.user_transport, evaluate=Evaluator()):
 
580
            if branches is not None:
 
581
                ret.extend(branches)
 
582
            if not using and repository is not None:
 
583
                ret.extend(repository.find_branches())
 
584
        return ret
 
585
 
 
586
    @needs_read_lock
 
587
    def search_missing_revision_ids(self, other,
 
588
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
589
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
590
            limit=None):
 
591
        """Return the revision ids that other has that this does not.
 
592
 
 
593
        These are returned in topological order.
 
594
 
 
595
        revision_id: only return revision ids included by revision_id.
 
596
        """
 
597
        if symbol_versioning.deprecated_passed(revision_id):
 
598
            symbol_versioning.warn(
 
599
                'search_missing_revision_ids(revision_id=...) was '
 
600
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
601
                DeprecationWarning, stacklevel=3)
 
602
            if revision_ids is not None:
 
603
                raise AssertionError(
 
604
                    'revision_ids is mutually exclusive with revision_id')
 
605
            if revision_id is not None:
 
606
                revision_ids = [revision_id]
 
607
        return InterRepository.get(other, self).search_missing_revision_ids(
 
608
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
609
            if_present_ids=if_present_ids, limit=limit)
 
610
 
 
611
    @staticmethod
 
612
    def open(base):
 
613
        """Open the repository rooted at base.
 
614
 
 
615
        For instance, if the repository is at URL/.bzr/repository,
 
616
        Repository.open(URL) -> a Repository instance.
 
617
        """
 
618
        control = bzrdir.BzrDir.open(base)
 
619
        return control.open_repository()
 
620
 
 
621
    def copy_content_into(self, destination, revision_id=None):
 
622
        """Make a complete copy of the content in self into destination.
 
623
 
 
624
        This is a destructive operation! Do not use it on existing
 
625
        repositories.
 
626
        """
 
627
        return InterRepository.get(self, destination).copy_content(revision_id)
 
628
 
 
629
    def commit_write_group(self):
 
630
        """Commit the contents accrued within the current write group.
 
631
 
 
632
        :seealso: start_write_group.
 
633
        
 
634
        :return: it may return an opaque hint that can be passed to 'pack'.
 
635
        """
 
636
        if self._write_group is not self.get_transaction():
 
637
            # has an unlock or relock occured ?
 
638
            raise errors.BzrError('mismatched lock context %r and '
 
639
                'write group %r.' %
 
640
                (self.get_transaction(), self._write_group))
 
641
        result = self._commit_write_group()
 
642
        self._write_group = None
 
643
        return result
 
644
 
 
645
    def _commit_write_group(self):
 
646
        """Template method for per-repository write group cleanup.
 
647
 
 
648
        This is called before the write group is considered to be
 
649
        finished and should ensure that all data handed to the repository
 
650
        for writing during the write group is safely committed (to the
 
651
        extent possible considering file system caching etc).
 
652
        """
 
653
 
 
654
    def suspend_write_group(self):
 
655
        raise errors.UnsuspendableWriteGroup(self)
 
656
 
 
657
    def refresh_data(self):
 
658
        """Re-read any data needed to synchronise with disk.
 
659
 
 
660
        This method is intended to be called after another repository instance
 
661
        (such as one used by a smart server) has inserted data into the
 
662
        repository. On all repositories this will work outside of write groups.
 
663
        Some repository formats (pack and newer for bzrlib native formats)
 
664
        support refresh_data inside write groups. If called inside a write
 
665
        group on a repository that does not support refreshing in a write group
 
666
        IsInWriteGroupError will be raised.
 
667
        """
 
668
        self._refresh_data()
 
669
 
 
670
    def resume_write_group(self, tokens):
 
671
        if not self.is_write_locked():
 
672
            raise errors.NotWriteLocked(self)
 
673
        if self._write_group:
 
674
            raise errors.BzrError('already in a write group')
 
675
        self._resume_write_group(tokens)
 
676
        # so we can detect unlock/relock - the write group is now entered.
 
677
        self._write_group = self.get_transaction()
 
678
 
 
679
    def _resume_write_group(self, tokens):
 
680
        raise errors.UnsuspendableWriteGroup(self)
 
681
 
 
682
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
683
            fetch_spec=None):
 
684
        """Fetch the content required to construct revision_id from source.
 
685
 
 
686
        If revision_id is None and fetch_spec is None, then all content is
 
687
        copied.
 
688
 
 
689
        fetch() may not be used when the repository is in a write group -
 
690
        either finish the current write group before using fetch, or use
 
691
        fetch before starting the write group.
 
692
 
 
693
        :param find_ghosts: Find and copy revisions in the source that are
 
694
            ghosts in the target (and not reachable directly by walking out to
 
695
            the first-present revision in target from revision_id).
 
696
        :param revision_id: If specified, all the content needed for this
 
697
            revision ID will be copied to the target.  Fetch will determine for
 
698
            itself which content needs to be copied.
 
699
        :param fetch_spec: If specified, a SearchResult or
 
700
            PendingAncestryResult that describes which revisions to copy.  This
 
701
            allows copying multiple heads at once.  Mutually exclusive with
 
702
            revision_id.
 
703
        """
 
704
        if fetch_spec is not None and revision_id is not None:
 
705
            raise AssertionError(
 
706
                "fetch_spec and revision_id are mutually exclusive.")
 
707
        if self.is_in_write_group():
 
708
            raise errors.InternalBzrError(
 
709
                "May not fetch while in a write group.")
 
710
        # fast path same-url fetch operations
 
711
        # TODO: lift out to somewhere common with RemoteRepository
 
712
        # <https://bugs.launchpad.net/bzr/+bug/401646>
 
713
        if (self.has_same_location(source)
 
714
            and fetch_spec is None
 
715
            and self._has_same_fallbacks(source)):
 
716
            # check that last_revision is in 'from' and then return a
 
717
            # no-operation.
 
718
            if (revision_id is not None and
 
719
                not _mod_revision.is_null(revision_id)):
 
720
                self.get_revision(revision_id)
 
721
            return 0, []
 
722
        inter = InterRepository.get(source, self)
 
723
        return inter.fetch(revision_id=revision_id,
 
724
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
 
725
 
 
726
    def create_bundle(self, target, base, fileobj, format=None):
 
727
        return serializer.write_bundle(self, target, base, fileobj, format)
 
728
 
 
729
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
730
                           timezone=None, committer=None, revprops=None,
 
731
                           revision_id=None, lossy=False):
 
732
        """Obtain a CommitBuilder for this repository.
 
733
 
 
734
        :param branch: Branch to commit to.
 
735
        :param parents: Revision ids of the parents of the new revision.
 
736
        :param config: Configuration to use.
 
737
        :param timestamp: Optional timestamp recorded for commit.
 
738
        :param timezone: Optional timezone for timestamp.
 
739
        :param committer: Optional committer to set for commit.
 
740
        :param revprops: Optional dictionary of revision properties.
 
741
        :param revision_id: Optional revision id.
 
742
        :param lossy: Whether to discard data that can not be natively
 
743
            represented, when pushing to a foreign VCS
 
744
        """
 
745
        raise NotImplementedError(self.get_commit_builder)
 
746
 
 
747
    @only_raises(errors.LockNotHeld, errors.LockBroken)
 
748
    def unlock(self):
 
749
        if (self.control_files._lock_count == 1 and
 
750
            self.control_files._lock_mode == 'w'):
 
751
            if self._write_group is not None:
 
752
                self.abort_write_group()
 
753
                self.control_files.unlock()
 
754
                raise errors.BzrError(
 
755
                    'Must end write groups before releasing write locks.')
 
756
        self.control_files.unlock()
 
757
        if self.control_files._lock_count == 0:
 
758
            for repo in self._fallback_repositories:
 
759
                repo.unlock()
 
760
 
 
761
    @needs_read_lock
 
762
    def clone(self, a_bzrdir, revision_id=None):
 
763
        """Clone this repository into a_bzrdir using the current format.
 
764
 
 
765
        Currently no check is made that the format of this repository and
 
766
        the bzrdir format are compatible. FIXME RBC 20060201.
 
767
 
 
768
        :return: The newly created destination repository.
 
769
        """
 
770
        # TODO: deprecate after 0.16; cloning this with all its settings is
 
771
        # probably not very useful -- mbp 20070423
 
772
        dest_repo = self._create_sprouting_repo(a_bzrdir, shared=self.is_shared())
 
773
        self.copy_content_into(dest_repo, revision_id)
 
774
        return dest_repo
 
775
 
 
776
    def start_write_group(self):
 
777
        """Start a write group in the repository.
 
778
 
 
779
        Write groups are used by repositories which do not have a 1:1 mapping
 
780
        between file ids and backend store to manage the insertion of data from
 
781
        both fetch and commit operations.
 
782
 
 
783
        A write lock is required around the start_write_group/commit_write_group
 
784
        for the support of lock-requiring repository formats.
 
785
 
 
786
        One can only insert data into a repository inside a write group.
 
787
 
 
788
        :return: None.
 
789
        """
 
790
        if not self.is_write_locked():
 
791
            raise errors.NotWriteLocked(self)
 
792
        if self._write_group:
 
793
            raise errors.BzrError('already in a write group')
 
794
        self._start_write_group()
 
795
        # so we can detect unlock/relock - the write group is now entered.
 
796
        self._write_group = self.get_transaction()
 
797
 
 
798
    def _start_write_group(self):
 
799
        """Template method for per-repository write group startup.
 
800
 
 
801
        This is called before the write group is considered to be
 
802
        entered.
 
803
        """
 
804
 
 
805
    @needs_read_lock
 
806
    def sprout(self, to_bzrdir, revision_id=None):
 
807
        """Create a descendent repository for new development.
 
808
 
 
809
        Unlike clone, this does not copy the settings of the repository.
 
810
        """
 
811
        dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
 
812
        dest_repo.fetch(self, revision_id=revision_id)
 
813
        return dest_repo
 
814
 
 
815
    def _create_sprouting_repo(self, a_bzrdir, shared):
 
816
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
 
817
            # use target default format.
 
818
            dest_repo = a_bzrdir.create_repository()
 
819
        else:
 
820
            # Most control formats need the repository to be specifically
 
821
            # created, but on some old all-in-one formats it's not needed
 
822
            try:
 
823
                dest_repo = self._format.initialize(a_bzrdir, shared=shared)
 
824
            except errors.UninitializableFormat:
 
825
                dest_repo = a_bzrdir.open_repository()
 
826
        return dest_repo
 
827
 
 
828
    @needs_read_lock
 
829
    def has_revision(self, revision_id):
 
830
        """True if this repository has a copy of the revision."""
 
831
        return revision_id in self.has_revisions((revision_id,))
 
832
 
 
833
    @needs_read_lock
 
834
    def has_revisions(self, revision_ids):
 
835
        """Probe to find out the presence of multiple revisions.
 
836
 
 
837
        :param revision_ids: An iterable of revision_ids.
 
838
        :return: A set of the revision_ids that were present.
 
839
        """
 
840
        raise NotImplementedError(self.has_revisions)
 
841
 
 
842
    @needs_read_lock
 
843
    def get_revision(self, revision_id):
 
844
        """Return the Revision object for a named revision."""
 
845
        return self.get_revisions([revision_id])[0]
 
846
 
 
847
    def get_revision_reconcile(self, revision_id):
 
848
        """'reconcile' helper routine that allows access to a revision always.
 
849
 
 
850
        This variant of get_revision does not cross check the weave graph
 
851
        against the revision one as get_revision does: but it should only
 
852
        be used by reconcile, or reconcile-alike commands that are correcting
 
853
        or testing the revision graph.
 
854
        """
 
855
        raise NotImplementedError(self.get_revision_reconcile)
 
856
 
 
857
    def get_revisions(self, revision_ids):
 
858
        """Get many revisions at once.
 
859
        
 
860
        Repositories that need to check data on every revision read should 
 
861
        subclass this method.
 
862
        """
 
863
        raise NotImplementedError(self.get_revisions)
 
864
 
 
865
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
 
866
        """Produce a generator of revision deltas.
 
867
 
 
868
        Note that the input is a sequence of REVISIONS, not revision_ids.
 
869
        Trees will be held in memory until the generator exits.
 
870
        Each delta is relative to the revision's lefthand predecessor.
 
871
 
 
872
        :param specific_fileids: if not None, the result is filtered
 
873
          so that only those file-ids, their parents and their
 
874
          children are included.
 
875
        """
 
876
        # Get the revision-ids of interest
 
877
        required_trees = set()
 
878
        for revision in revisions:
 
879
            required_trees.add(revision.revision_id)
 
880
            required_trees.update(revision.parent_ids[:1])
 
881
 
 
882
        # Get the matching filtered trees. Note that it's more
 
883
        # efficient to pass filtered trees to changes_from() rather
 
884
        # than doing the filtering afterwards. changes_from() could
 
885
        # arguably do the filtering itself but it's path-based, not
 
886
        # file-id based, so filtering before or afterwards is
 
887
        # currently easier.
 
888
        if specific_fileids is None:
 
889
            trees = dict((t.get_revision_id(), t) for
 
890
                t in self.revision_trees(required_trees))
 
891
        else:
 
892
            trees = dict((t.get_revision_id(), t) for
 
893
                t in self._filtered_revision_trees(required_trees,
 
894
                specific_fileids))
 
895
 
 
896
        # Calculate the deltas
 
897
        for revision in revisions:
 
898
            if not revision.parent_ids:
 
899
                old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
 
900
            else:
 
901
                old_tree = trees[revision.parent_ids[0]]
 
902
            yield trees[revision.revision_id].changes_from(old_tree)
 
903
 
 
904
    @needs_read_lock
 
905
    def get_revision_delta(self, revision_id, specific_fileids=None):
 
906
        """Return the delta for one revision.
 
907
 
 
908
        The delta is relative to the left-hand predecessor of the
 
909
        revision.
 
910
 
 
911
        :param specific_fileids: if not None, the result is filtered
 
912
          so that only those file-ids, their parents and their
 
913
          children are included.
 
914
        """
 
915
        r = self.get_revision(revision_id)
 
916
        return list(self.get_deltas_for_revisions([r],
 
917
            specific_fileids=specific_fileids))[0]
 
918
 
 
919
    @needs_write_lock
 
920
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
921
        signature = gpg_strategy.sign(plaintext)
 
922
        self.add_signature_text(revision_id, signature)
 
923
 
 
924
    def add_signature_text(self, revision_id, signature):
 
925
        """Store a signature text for a revision.
 
926
 
 
927
        :param revision_id: Revision id of the revision
 
928
        :param signature: Signature text.
 
929
        """
 
930
        raise NotImplementedError(self.add_signature_text)
 
931
 
 
932
    def _find_parent_ids_of_revisions(self, revision_ids):
 
933
        """Find all parent ids that are mentioned in the revision graph.
 
934
 
 
935
        :return: set of revisions that are parents of revision_ids which are
 
936
            not part of revision_ids themselves
 
937
        """
 
938
        parent_map = self.get_parent_map(revision_ids)
 
939
        parent_ids = set()
 
940
        map(parent_ids.update, parent_map.itervalues())
 
941
        parent_ids.difference_update(revision_ids)
 
942
        parent_ids.discard(_mod_revision.NULL_REVISION)
 
943
        return parent_ids
 
944
 
 
945
    def fileids_altered_by_revision_ids(self, revision_ids):
 
946
        """Find the file ids and versions affected by revisions.
 
947
 
 
948
        :param revisions: an iterable containing revision ids.
 
949
        :return: a dictionary mapping altered file-ids to an iterable of
 
950
            revision_ids. Each altered file-ids has the exact revision_ids
 
951
            that altered it listed explicitly.
 
952
        """
 
953
        raise NotImplementedError(self.fileids_altered_by_revision_ids)
 
954
 
 
955
    def iter_files_bytes(self, desired_files):
 
956
        """Iterate through file versions.
 
957
 
 
958
        Files will not necessarily be returned in the order they occur in
 
959
        desired_files.  No specific order is guaranteed.
 
960
 
 
961
        Yields pairs of identifier, bytes_iterator.  identifier is an opaque
 
962
        value supplied by the caller as part of desired_files.  It should
 
963
        uniquely identify the file version in the caller's context.  (Examples:
 
964
        an index number or a TreeTransform trans_id.)
 
965
 
 
966
        :param desired_files: a list of (file_id, revision_id, identifier)
 
967
            triples
 
968
        """
 
969
        raise NotImplementedError(self.iter_files_bytes)
 
970
 
 
971
    def get_rev_id_for_revno(self, revno, known_pair):
 
972
        """Return the revision id of a revno, given a later (revno, revid)
 
973
        pair in the same history.
 
974
 
 
975
        :return: if found (True, revid).  If the available history ran out
 
976
            before reaching the revno, then this returns
 
977
            (False, (closest_revno, closest_revid)).
 
978
        """
 
979
        known_revno, known_revid = known_pair
 
980
        partial_history = [known_revid]
 
981
        distance_from_known = known_revno - revno
 
982
        if distance_from_known < 0:
 
983
            raise ValueError(
 
984
                'requested revno (%d) is later than given known revno (%d)'
 
985
                % (revno, known_revno))
 
986
        try:
 
987
            _iter_for_revno(
 
988
                self, partial_history, stop_index=distance_from_known)
 
989
        except errors.RevisionNotPresent, err:
 
990
            if err.revision_id == known_revid:
 
991
                # The start revision (known_revid) wasn't found.
 
992
                raise
 
993
            # This is a stacked repository with no fallbacks, or a there's a
 
994
            # left-hand ghost.  Either way, even though the revision named in
 
995
            # the error isn't in this repo, we know it's the next step in this
 
996
            # left-hand history.
 
997
            partial_history.append(err.revision_id)
 
998
        if len(partial_history) <= distance_from_known:
 
999
            # Didn't find enough history to get a revid for the revno.
 
1000
            earliest_revno = known_revno - len(partial_history) + 1
 
1001
            return (False, (earliest_revno, partial_history[-1]))
 
1002
        if len(partial_history) - 1 > distance_from_known:
 
1003
            raise AssertionError('_iter_for_revno returned too much history')
 
1004
        return (True, partial_history[-1])
 
1005
 
 
1006
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1007
    def iter_reverse_revision_history(self, revision_id):
 
1008
        """Iterate backwards through revision ids in the lefthand history
 
1009
 
 
1010
        :param revision_id: The revision id to start with.  All its lefthand
 
1011
            ancestors will be traversed.
 
1012
        """
 
1013
        graph = self.get_graph()
 
1014
        stop_revisions = (None, _mod_revision.NULL_REVISION)
 
1015
        return graph.iter_lefthand_ancestry(revision_id, stop_revisions)
 
1016
 
 
1017
    def is_shared(self):
 
1018
        """Return True if this repository is flagged as a shared repository."""
 
1019
        raise NotImplementedError(self.is_shared)
 
1020
 
 
1021
    @needs_write_lock
 
1022
    def reconcile(self, other=None, thorough=False):
 
1023
        """Reconcile this repository."""
 
1024
        from bzrlib.reconcile import RepoReconciler
 
1025
        reconciler = RepoReconciler(self, thorough=thorough)
 
1026
        reconciler.reconcile()
 
1027
        return reconciler
 
1028
 
 
1029
    def _refresh_data(self):
 
1030
        """Helper called from lock_* to ensure coherency with disk.
 
1031
 
 
1032
        The default implementation does nothing; it is however possible
 
1033
        for repositories to maintain loaded indices across multiple locks
 
1034
        by checking inside their implementation of this method to see
 
1035
        whether their indices are still valid. This depends of course on
 
1036
        the disk format being validatable in this manner. This method is
 
1037
        also called by the refresh_data() public interface to cause a refresh
 
1038
        to occur while in a write lock so that data inserted by a smart server
 
1039
        push operation is visible on the client's instance of the physical
 
1040
        repository.
 
1041
        """
 
1042
 
 
1043
    @needs_read_lock
 
1044
    def revision_tree(self, revision_id):
 
1045
        """Return Tree for a revision on this branch.
 
1046
 
 
1047
        `revision_id` may be NULL_REVISION for the empty tree revision.
 
1048
        """
 
1049
        raise NotImplementedError(self.revision_tree)
 
1050
 
 
1051
    def revision_trees(self, revision_ids):
 
1052
        """Return Trees for revisions in this repository.
 
1053
 
 
1054
        :param revision_ids: a sequence of revision-ids;
 
1055
          a revision-id may not be None or 'null:'
 
1056
        """
 
1057
        raise NotImplementedError(self.revision_trees)
 
1058
 
 
1059
    @needs_read_lock
 
1060
    @symbol_versioning.deprecated_method(
 
1061
        symbol_versioning.deprecated_in((2, 4, 0)))
 
1062
    def get_ancestry(self, revision_id, topo_sorted=True):
 
1063
        """Return a list of revision-ids integrated by a revision.
 
1064
 
 
1065
        The first element of the list is always None, indicating the origin
 
1066
        revision.  This might change when we have history horizons, or
 
1067
        perhaps we should have a new API.
 
1068
 
 
1069
        This is topologically sorted.
 
1070
        """
 
1071
        if 'evil' in debug.debug_flags:
 
1072
            mutter_callsite(2, "get_ancestry is linear with history.")
 
1073
        if _mod_revision.is_null(revision_id):
 
1074
            return [None]
 
1075
        if not self.has_revision(revision_id):
 
1076
            raise errors.NoSuchRevision(self, revision_id)
 
1077
        graph = self.get_graph()
 
1078
        keys = set()
 
1079
        search = graph._make_breadth_first_searcher([revision_id])
 
1080
        while True:
 
1081
            try:
 
1082
                found, ghosts = search.next_with_ghosts()
 
1083
            except StopIteration:
 
1084
                break
 
1085
            keys.update(found)
 
1086
        if _mod_revision.NULL_REVISION in keys:
 
1087
            keys.remove(_mod_revision.NULL_REVISION)
 
1088
        if topo_sorted:
 
1089
            parent_map = graph.get_parent_map(keys)
 
1090
            keys = tsort.topo_sort(parent_map)
 
1091
        return [None] + list(keys)
 
1092
 
 
1093
    def pack(self, hint=None, clean_obsolete_packs=False):
 
1094
        """Compress the data within the repository.
 
1095
 
 
1096
        This operation only makes sense for some repository types. For other
 
1097
        types it should be a no-op that just returns.
 
1098
 
 
1099
        This stub method does not require a lock, but subclasses should use
 
1100
        @needs_write_lock as this is a long running call it's reasonable to
 
1101
        implicitly lock for the user.
 
1102
 
 
1103
        :param hint: If not supplied, the whole repository is packed.
 
1104
            If supplied, the repository may use the hint parameter as a
 
1105
            hint for the parts of the repository to pack. A hint can be
 
1106
            obtained from the result of commit_write_group(). Out of
 
1107
            date hints are simply ignored, because concurrent operations
 
1108
            can obsolete them rapidly.
 
1109
 
 
1110
        :param clean_obsolete_packs: Clean obsolete packs immediately after
 
1111
            the pack operation.
 
1112
        """
 
1113
 
 
1114
    def get_transaction(self):
 
1115
        return self.control_files.get_transaction()
 
1116
 
 
1117
    def get_parent_map(self, revision_ids):
 
1118
        """See graph.StackedParentsProvider.get_parent_map"""
 
1119
        raise NotImplementedError(self.get_parent_map)
 
1120
 
 
1121
    def _get_parent_map_no_fallbacks(self, revision_ids):
 
1122
        """Same as Repository.get_parent_map except doesn't query fallbacks."""
 
1123
        # revisions index works in keys; this just works in revisions
 
1124
        # therefore wrap and unwrap
 
1125
        query_keys = []
 
1126
        result = {}
 
1127
        for revision_id in revision_ids:
 
1128
            if revision_id == _mod_revision.NULL_REVISION:
 
1129
                result[revision_id] = ()
 
1130
            elif revision_id is None:
 
1131
                raise ValueError('get_parent_map(None) is not valid')
 
1132
            else:
 
1133
                query_keys.append((revision_id ,))
 
1134
        vf = self.revisions.without_fallbacks()
 
1135
        for ((revision_id,), parent_keys) in \
 
1136
                vf.get_parent_map(query_keys).iteritems():
 
1137
            if parent_keys:
 
1138
                result[revision_id] = tuple([parent_revid
 
1139
                    for (parent_revid,) in parent_keys])
 
1140
            else:
 
1141
                result[revision_id] = (_mod_revision.NULL_REVISION,)
 
1142
        return result
 
1143
 
 
1144
    def _make_parents_provider(self):
 
1145
        if not self._format.supports_external_lookups:
 
1146
            return self
 
1147
        return graph.StackedParentsProvider(_LazyListJoin(
 
1148
            [self._make_parents_provider_unstacked()],
 
1149
            self._fallback_repositories))
 
1150
 
 
1151
    def _make_parents_provider_unstacked(self):
 
1152
        return graph.CallableToParentsProviderAdapter(
 
1153
            self._get_parent_map_no_fallbacks)
 
1154
 
 
1155
    @needs_read_lock
 
1156
    def get_known_graph_ancestry(self, revision_ids):
 
1157
        """Return the known graph for a set of revision ids and their ancestors.
 
1158
        """
 
1159
        raise NotImplementedError(self.get_known_graph_ancestry)
 
1160
 
 
1161
    def get_file_graph(self):
 
1162
        """Return the graph walker for files."""
 
1163
        raise NotImplementedError(self.get_file_graph)
 
1164
 
 
1165
    def get_graph(self, other_repository=None):
 
1166
        """Return the graph walker for this repository format"""
 
1167
        parents_provider = self._make_parents_provider()
 
1168
        if (other_repository is not None and
 
1169
            not self.has_same_location(other_repository)):
 
1170
            parents_provider = graph.StackedParentsProvider(
 
1171
                [parents_provider, other_repository._make_parents_provider()])
 
1172
        return graph.Graph(parents_provider)
 
1173
 
 
1174
    def revision_ids_to_search_result(self, result_set):
 
1175
        """Convert a set of revision ids to a graph SearchResult."""
 
1176
        result_parents = set()
 
1177
        for parents in self.get_graph().get_parent_map(
 
1178
            result_set).itervalues():
 
1179
            result_parents.update(parents)
 
1180
        included_keys = result_set.intersection(result_parents)
 
1181
        start_keys = result_set.difference(included_keys)
 
1182
        exclude_keys = result_parents.difference(result_set)
 
1183
        result = graph.SearchResult(start_keys, exclude_keys,
 
1184
            len(result_set), result_set)
 
1185
        return result
 
1186
 
 
1187
    @needs_write_lock
 
1188
    def set_make_working_trees(self, new_value):
 
1189
        """Set the policy flag for making working trees when creating branches.
 
1190
 
 
1191
        This only applies to branches that use this repository.
 
1192
 
 
1193
        The default is 'True'.
 
1194
        :param new_value: True to restore the default, False to disable making
 
1195
                          working trees.
 
1196
        """
 
1197
        raise NotImplementedError(self.set_make_working_trees)
 
1198
 
 
1199
    def make_working_trees(self):
 
1200
        """Returns the policy for making working trees on new branches."""
 
1201
        raise NotImplementedError(self.make_working_trees)
 
1202
 
 
1203
    @needs_write_lock
 
1204
    def sign_revision(self, revision_id, gpg_strategy):
 
1205
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
1206
        plaintext = testament.as_short_text()
 
1207
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
 
1208
 
 
1209
    @needs_read_lock
 
1210
    def verify_revision(self, revision_id, gpg_strategy):
 
1211
        """Verify the signature on a revision.
 
1212
        
 
1213
        :param revision_id: the revision to verify
 
1214
        :gpg_strategy: the GPGStrategy object to used
 
1215
        
 
1216
        :return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
 
1217
        """
 
1218
        if not self.has_signature_for_revision_id(revision_id):
 
1219
            return gpg.SIGNATURE_NOT_SIGNED, None
 
1220
        signature = self.get_signature_text(revision_id)
 
1221
 
 
1222
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
1223
        plaintext = testament.as_short_text()
 
1224
 
 
1225
        return gpg_strategy.verify(signature, plaintext)
 
1226
 
 
1227
    def has_signature_for_revision_id(self, revision_id):
 
1228
        """Query for a revision signature for revision_id in the repository."""
 
1229
        raise NotImplementedError(self.has_signature_for_revision_id)
 
1230
 
 
1231
    def get_signature_text(self, revision_id):
 
1232
        """Return the text for a signature."""
 
1233
        raise NotImplementedError(self.get_signature_text)
 
1234
 
 
1235
    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
 
1236
        """Check consistency of all history of given revision_ids.
 
1237
 
 
1238
        Different repository implementations should override _check().
 
1239
 
 
1240
        :param revision_ids: A non-empty list of revision_ids whose ancestry
 
1241
             will be checked.  Typically the last revision_id of a branch.
 
1242
        :param callback_refs: A dict of check-refs to resolve and callback
 
1243
            the check/_check method on the items listed as wanting the ref.
 
1244
            see bzrlib.check.
 
1245
        :param check_repo: If False do not check the repository contents, just 
 
1246
            calculate the data callback_refs requires and call them back.
 
1247
        """
 
1248
        return self._check(revision_ids=revision_ids, callback_refs=callback_refs,
 
1249
            check_repo=check_repo)
 
1250
 
 
1251
    def _check(self, revision_ids=None, callback_refs=None, check_repo=True):
 
1252
        raise NotImplementedError(self.check)
 
1253
 
 
1254
    def _warn_if_deprecated(self, branch=None):
 
1255
        if not self._format.is_deprecated():
 
1256
            return
 
1257
        global _deprecation_warning_done
 
1258
        if _deprecation_warning_done:
 
1259
            return
 
1260
        try:
 
1261
            if branch is None:
 
1262
                conf = config.GlobalConfig()
 
1263
            else:
 
1264
                conf = branch.get_config()
 
1265
            if conf.suppress_warning('format_deprecation'):
 
1266
                return
 
1267
            warning("Format %s for %s is deprecated -"
 
1268
                    " please use 'bzr upgrade' to get better performance"
 
1269
                    % (self._format, self.bzrdir.transport.base))
 
1270
        finally:
 
1271
            _deprecation_warning_done = True
 
1272
 
 
1273
    def supports_rich_root(self):
 
1274
        return self._format.rich_root_data
 
1275
 
 
1276
    def _check_ascii_revisionid(self, revision_id, method):
 
1277
        """Private helper for ascii-only repositories."""
 
1278
        # weave repositories refuse to store revisionids that are non-ascii.
 
1279
        if revision_id is not None:
 
1280
            # weaves require ascii revision ids.
 
1281
            if isinstance(revision_id, unicode):
 
1282
                try:
 
1283
                    revision_id.encode('ascii')
 
1284
                except UnicodeEncodeError:
 
1285
                    raise errors.NonAsciiRevisionId(method, self)
 
1286
            else:
 
1287
                try:
 
1288
                    revision_id.decode('ascii')
 
1289
                except UnicodeDecodeError:
 
1290
                    raise errors.NonAsciiRevisionId(method, self)
 
1291
 
 
1292
 
 
1293
class MetaDirRepository(Repository):
 
1294
    """Repositories in the new meta-dir layout.
 
1295
 
 
1296
    :ivar _transport: Transport for access to repository control files,
 
1297
        typically pointing to .bzr/repository.
 
1298
    """
 
1299
 
 
1300
    def __init__(self, _format, a_bzrdir, control_files):
 
1301
        super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
 
1302
        self._transport = control_files._transport
 
1303
 
 
1304
    def is_shared(self):
 
1305
        """Return True if this repository is flagged as a shared repository."""
 
1306
        return self._transport.has('shared-storage')
 
1307
 
 
1308
    @needs_write_lock
 
1309
    def set_make_working_trees(self, new_value):
 
1310
        """Set the policy flag for making working trees when creating branches.
 
1311
 
 
1312
        This only applies to branches that use this repository.
 
1313
 
 
1314
        The default is 'True'.
 
1315
        :param new_value: True to restore the default, False to disable making
 
1316
                          working trees.
 
1317
        """
 
1318
        if new_value:
 
1319
            try:
 
1320
                self._transport.delete('no-working-trees')
 
1321
            except errors.NoSuchFile:
 
1322
                pass
 
1323
        else:
 
1324
            self._transport.put_bytes('no-working-trees', '',
 
1325
                mode=self.bzrdir._get_file_mode())
 
1326
 
 
1327
    def make_working_trees(self):
 
1328
        """Returns the policy for making working trees on new branches."""
 
1329
        return not self._transport.has('no-working-trees')
 
1330
 
 
1331
 
 
1332
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
 
1333
    """Repository format registry."""
 
1334
 
 
1335
    def get_default(self):
 
1336
        """Return the current default format."""
 
1337
        from bzrlib import bzrdir
 
1338
        return bzrdir.format_registry.make_bzrdir('default').repository_format
 
1339
 
 
1340
 
 
1341
network_format_registry = registry.FormatRegistry()
 
1342
"""Registry of formats indexed by their network name.
 
1343
 
 
1344
The network name for a repository format is an identifier that can be used when
 
1345
referring to formats with smart server operations. See
 
1346
RepositoryFormat.network_name() for more detail.
 
1347
"""
 
1348
 
 
1349
 
 
1350
format_registry = RepositoryFormatRegistry(network_format_registry)
 
1351
"""Registry of formats, indexed by their BzrDirMetaFormat format string.
 
1352
 
 
1353
This can contain either format instances themselves, or classes/factories that
 
1354
can be called to obtain one.
 
1355
"""
 
1356
 
 
1357
 
 
1358
#####################################################################
 
1359
# Repository Formats
 
1360
 
 
1361
class RepositoryFormat(controldir.ControlComponentFormat):
 
1362
    """A repository format.
 
1363
 
 
1364
    Formats provide four things:
 
1365
     * An initialization routine to construct repository data on disk.
 
1366
     * a optional format string which is used when the BzrDir supports
 
1367
       versioned children.
 
1368
     * an open routine which returns a Repository instance.
 
1369
     * A network name for referring to the format in smart server RPC
 
1370
       methods.
 
1371
 
 
1372
    There is one and only one Format subclass for each on-disk format. But
 
1373
    there can be one Repository subclass that is used for several different
 
1374
    formats. The _format attribute on a Repository instance can be used to
 
1375
    determine the disk format.
 
1376
 
 
1377
    Formats are placed in a registry by their format string for reference
 
1378
    during opening. These should be subclasses of RepositoryFormat for
 
1379
    consistency.
 
1380
 
 
1381
    Once a format is deprecated, just deprecate the initialize and open
 
1382
    methods on the format class. Do not deprecate the object, as the
 
1383
    object may be created even when a repository instance hasn't been
 
1384
    created.
 
1385
 
 
1386
    Common instance attributes:
 
1387
    _matchingbzrdir - the bzrdir format that the repository format was
 
1388
    originally written to work with. This can be used if manually
 
1389
    constructing a bzrdir and repository, or more commonly for test suite
 
1390
    parameterization.
 
1391
    """
 
1392
 
 
1393
    # Set to True or False in derived classes. True indicates that the format
 
1394
    # supports ghosts gracefully.
 
1395
    supports_ghosts = None
 
1396
    # Can this repository be given external locations to lookup additional
 
1397
    # data. Set to True or False in derived classes.
 
1398
    supports_external_lookups = None
 
1399
    # Does this format support CHK bytestring lookups. Set to True or False in
 
1400
    # derived classes.
 
1401
    supports_chks = None
 
1402
    # Should fetch trigger a reconcile after the fetch? Only needed for
 
1403
    # some repository formats that can suffer internal inconsistencies.
 
1404
    _fetch_reconcile = False
 
1405
    # Does this format have < O(tree_size) delta generation. Used to hint what
 
1406
    # code path for commit, amongst other things.
 
1407
    fast_deltas = None
 
1408
    # Does doing a pack operation compress data? Useful for the pack UI command
 
1409
    # (so if there is one pack, the operation can still proceed because it may
 
1410
    # help), and for fetching when data won't have come from the same
 
1411
    # compressor.
 
1412
    pack_compresses = False
 
1413
    # Does the repository storage understand references to trees?
 
1414
    supports_tree_reference = None
 
1415
    # Is the format experimental ?
 
1416
    experimental = False
 
1417
    # Does this repository format escape funky characters, or does it create
 
1418
    # files with similar names as the versioned files in its contents on disk
 
1419
    # ?
 
1420
    supports_funky_characters = None
 
1421
    # Does this repository format support leaving locks?
 
1422
    supports_leaving_lock = None
 
1423
    # Does this format support the full VersionedFiles interface?
 
1424
    supports_full_versioned_files = None
 
1425
    # Does this format support signing revision signatures?
 
1426
    supports_revision_signatures = True
 
1427
    # Can the revision graph have incorrect parents?
 
1428
    revision_graph_can_have_wrong_parents = None
 
1429
    # Does this format support rich root data?
 
1430
    rich_root_data = None
 
1431
    # Does this format support explicitly versioned directories?
 
1432
    supports_versioned_directories = None
 
1433
 
 
1434
    def __repr__(self):
 
1435
        return "%s()" % self.__class__.__name__
 
1436
 
 
1437
    def __eq__(self, other):
 
1438
        # format objects are generally stateless
 
1439
        return isinstance(other, self.__class__)
 
1440
 
 
1441
    def __ne__(self, other):
 
1442
        return not self == other
 
1443
 
 
1444
    @classmethod
 
1445
    def find_format(klass, a_bzrdir):
 
1446
        """Return the format for the repository object in a_bzrdir.
 
1447
 
 
1448
        This is used by bzr native formats that have a "format" file in
 
1449
        the repository.  Other methods may be used by different types of
 
1450
        control directory.
 
1451
        """
 
1452
        try:
 
1453
            transport = a_bzrdir.get_repository_transport(None)
 
1454
            format_string = transport.get_bytes("format")
 
1455
            return format_registry.get(format_string)
 
1456
        except errors.NoSuchFile:
 
1457
            raise errors.NoRepositoryPresent(a_bzrdir)
 
1458
        except KeyError:
 
1459
            raise errors.UnknownFormatError(format=format_string,
 
1460
                                            kind='repository')
 
1461
 
 
1462
    @classmethod
 
1463
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1464
    def register_format(klass, format):
 
1465
        format_registry.register(format)
 
1466
 
 
1467
    @classmethod
 
1468
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1469
    def unregister_format(klass, format):
 
1470
        format_registry.remove(format)
 
1471
 
 
1472
    @classmethod
 
1473
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1474
    def get_default_format(klass):
 
1475
        """Return the current default format."""
 
1476
        return format_registry.get_default()
 
1477
 
 
1478
    def get_format_string(self):
 
1479
        """Return the ASCII format string that identifies this format.
 
1480
 
 
1481
        Note that in pre format ?? repositories the format string is
 
1482
        not permitted nor written to disk.
 
1483
        """
 
1484
        raise NotImplementedError(self.get_format_string)
 
1485
 
 
1486
    def get_format_description(self):
 
1487
        """Return the short description for this format."""
 
1488
        raise NotImplementedError(self.get_format_description)
 
1489
 
 
1490
    def initialize(self, a_bzrdir, shared=False):
 
1491
        """Initialize a repository of this format in a_bzrdir.
 
1492
 
 
1493
        :param a_bzrdir: The bzrdir to put the new repository in it.
 
1494
        :param shared: The repository should be initialized as a sharable one.
 
1495
        :returns: The new repository object.
 
1496
 
 
1497
        This may raise UninitializableFormat if shared repository are not
 
1498
        compatible the a_bzrdir.
 
1499
        """
 
1500
        raise NotImplementedError(self.initialize)
 
1501
 
 
1502
    def is_supported(self):
 
1503
        """Is this format supported?
 
1504
 
 
1505
        Supported formats must be initializable and openable.
 
1506
        Unsupported formats may not support initialization or committing or
 
1507
        some other features depending on the reason for not being supported.
 
1508
        """
 
1509
        return True
 
1510
 
 
1511
    def is_deprecated(self):
 
1512
        """Is this format deprecated?
 
1513
 
 
1514
        Deprecated formats may trigger a user-visible warning recommending
 
1515
        the user to upgrade. They are still fully supported.
 
1516
        """
 
1517
        return False
 
1518
 
 
1519
    def network_name(self):
 
1520
        """A simple byte string uniquely identifying this format for RPC calls.
 
1521
 
 
1522
        MetaDir repository formats use their disk format string to identify the
 
1523
        repository over the wire. All in one formats such as bzr < 0.8, and
 
1524
        foreign formats like svn/git and hg should use some marker which is
 
1525
        unique and immutable.
 
1526
        """
 
1527
        raise NotImplementedError(self.network_name)
 
1528
 
 
1529
    def check_conversion_target(self, target_format):
 
1530
        if self.rich_root_data and not target_format.rich_root_data:
 
1531
            raise errors.BadConversionTarget(
 
1532
                'Does not support rich root data.', target_format,
 
1533
                from_format=self)
 
1534
        if (self.supports_tree_reference and 
 
1535
            not getattr(target_format, 'supports_tree_reference', False)):
 
1536
            raise errors.BadConversionTarget(
 
1537
                'Does not support nested trees', target_format,
 
1538
                from_format=self)
 
1539
 
 
1540
    def open(self, a_bzrdir, _found=False):
 
1541
        """Return an instance of this format for the bzrdir a_bzrdir.
 
1542
 
 
1543
        _found is a private parameter, do not use it.
 
1544
        """
 
1545
        raise NotImplementedError(self.open)
 
1546
 
 
1547
    def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
 
1548
        from bzrlib.bzrdir import BzrDir, RepoInitHookParams
 
1549
        hooks = BzrDir.hooks['post_repo_init']
 
1550
        if not hooks:
 
1551
            return
 
1552
        params = RepoInitHookParams(repository, self, a_bzrdir, shared)
 
1553
        for hook in hooks:
 
1554
            hook(params)
 
1555
 
 
1556
 
 
1557
class MetaDirRepositoryFormat(RepositoryFormat):
 
1558
    """Common base class for the new repositories using the metadir layout."""
 
1559
 
 
1560
    rich_root_data = False
 
1561
    supports_tree_reference = False
 
1562
    supports_external_lookups = False
 
1563
    supports_leaving_lock = True
 
1564
 
 
1565
    @property
 
1566
    def _matchingbzrdir(self):
 
1567
        matching = bzrdir.BzrDirMetaFormat1()
 
1568
        matching.repository_format = self
 
1569
        return matching
 
1570
 
 
1571
    def __init__(self):
 
1572
        super(MetaDirRepositoryFormat, self).__init__()
 
1573
 
 
1574
    def _create_control_files(self, a_bzrdir):
 
1575
        """Create the required files and the initial control_files object."""
 
1576
        # FIXME: RBC 20060125 don't peek under the covers
 
1577
        # NB: no need to escape relative paths that are url safe.
 
1578
        repository_transport = a_bzrdir.get_repository_transport(self)
 
1579
        control_files = lockable_files.LockableFiles(repository_transport,
 
1580
                                'lock', lockdir.LockDir)
 
1581
        control_files.create_lock()
 
1582
        return control_files
 
1583
 
 
1584
    def _upload_blank_content(self, a_bzrdir, dirs, files, utf8_files, shared):
 
1585
        """Upload the initial blank content."""
 
1586
        control_files = self._create_control_files(a_bzrdir)
 
1587
        control_files.lock_write()
 
1588
        transport = control_files._transport
 
1589
        if shared == True:
 
1590
            utf8_files += [('shared-storage', '')]
 
1591
        try:
 
1592
            transport.mkdir_multi(dirs, mode=a_bzrdir._get_dir_mode())
 
1593
            for (filename, content_stream) in files:
 
1594
                transport.put_file(filename, content_stream,
 
1595
                    mode=a_bzrdir._get_file_mode())
 
1596
            for (filename, content_bytes) in utf8_files:
 
1597
                transport.put_bytes_non_atomic(filename, content_bytes,
 
1598
                    mode=a_bzrdir._get_file_mode())
 
1599
        finally:
 
1600
            control_files.unlock()
 
1601
 
 
1602
    def network_name(self):
 
1603
        """Metadir formats have matching disk and network format strings."""
 
1604
        return self.get_format_string()
 
1605
 
 
1606
 
 
1607
# formats which have no format string are not discoverable or independently
 
1608
# creatable on disk, so are not registered in format_registry.  They're
 
1609
# all in bzrlib.repofmt.knitreponow.  When an instance of one of these is
 
1610
# needed, it's constructed directly by the BzrDir.  Non-native formats where
 
1611
# the repository is not separately opened are similar.
 
1612
 
 
1613
format_registry.register_lazy(
 
1614
    'Bazaar-NG Knit Repository Format 1',
 
1615
    'bzrlib.repofmt.knitrepo',
 
1616
    'RepositoryFormatKnit1',
 
1617
    )
 
1618
 
 
1619
format_registry.register_lazy(
 
1620
    'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
 
1621
    'bzrlib.repofmt.knitrepo',
 
1622
    'RepositoryFormatKnit3',
 
1623
    )
 
1624
 
 
1625
format_registry.register_lazy(
 
1626
    'Bazaar Knit Repository Format 4 (bzr 1.0)\n',
 
1627
    'bzrlib.repofmt.knitrepo',
 
1628
    'RepositoryFormatKnit4',
 
1629
    )
 
1630
 
 
1631
# Pack-based formats. There is one format for pre-subtrees, and one for
 
1632
# post-subtrees to allow ease of testing.
 
1633
# NOTE: These are experimental in 0.92. Stable in 1.0 and above
 
1634
format_registry.register_lazy(
 
1635
    'Bazaar pack repository format 1 (needs bzr 0.92)\n',
 
1636
    'bzrlib.repofmt.knitpack_repo',
 
1637
    'RepositoryFormatKnitPack1',
 
1638
    )
 
1639
format_registry.register_lazy(
 
1640
    'Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n',
 
1641
    'bzrlib.repofmt.knitpack_repo',
 
1642
    'RepositoryFormatKnitPack3',
 
1643
    )
 
1644
format_registry.register_lazy(
 
1645
    'Bazaar pack repository format 1 with rich root (needs bzr 1.0)\n',
 
1646
    'bzrlib.repofmt.knitpack_repo',
 
1647
    'RepositoryFormatKnitPack4',
 
1648
    )
 
1649
format_registry.register_lazy(
 
1650
    'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
 
1651
    'bzrlib.repofmt.knitpack_repo',
 
1652
    'RepositoryFormatKnitPack5',
 
1653
    )
 
1654
format_registry.register_lazy(
 
1655
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
 
1656
    'bzrlib.repofmt.knitpack_repo',
 
1657
    'RepositoryFormatKnitPack5RichRoot',
 
1658
    )
 
1659
format_registry.register_lazy(
 
1660
    'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
 
1661
    'bzrlib.repofmt.knitpack_repo',
 
1662
    'RepositoryFormatKnitPack5RichRootBroken',
 
1663
    )
 
1664
format_registry.register_lazy(
 
1665
    'Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n',
 
1666
    'bzrlib.repofmt.knitpack_repo',
 
1667
    'RepositoryFormatKnitPack6',
 
1668
    )
 
1669
format_registry.register_lazy(
 
1670
    'Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n',
 
1671
    'bzrlib.repofmt.knitpack_repo',
 
1672
    'RepositoryFormatKnitPack6RichRoot',
 
1673
    )
 
1674
format_registry.register_lazy(
 
1675
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
1676
    'bzrlib.repofmt.groupcompress_repo',
 
1677
    'RepositoryFormat2a',
 
1678
    )
 
1679
 
 
1680
# Development formats.
 
1681
# Check their docstrings to see if/when they are obsolete.
 
1682
format_registry.register_lazy(
 
1683
    ("Bazaar development format 2 with subtree support "
 
1684
        "(needs bzr.dev from before 1.8)\n"),
 
1685
    'bzrlib.repofmt.knitpack_repo',
 
1686
    'RepositoryFormatPackDevelopment2Subtree',
 
1687
    )
 
1688
format_registry.register_lazy(
 
1689
    'Bazaar development format 8\n',
 
1690
    'bzrlib.repofmt.groupcompress_repo',
 
1691
    'RepositoryFormat2aSubtree',
 
1692
    )
 
1693
 
 
1694
 
 
1695
class InterRepository(InterObject):
 
1696
    """This class represents operations taking place between two repositories.
 
1697
 
 
1698
    Its instances have methods like copy_content and fetch, and contain
 
1699
    references to the source and target repositories these operations can be
 
1700
    carried out on.
 
1701
 
 
1702
    Often we will provide convenience methods on 'repository' which carry out
 
1703
    operations with another repository - they will always forward to
 
1704
    InterRepository.get(other).method_name(parameters).
 
1705
    """
 
1706
 
 
1707
    _optimisers = []
 
1708
    """The available optimised InterRepository types."""
 
1709
 
 
1710
    @needs_write_lock
 
1711
    def copy_content(self, revision_id=None):
 
1712
        """Make a complete copy of the content in self into destination.
 
1713
 
 
1714
        This is a destructive operation! Do not use it on existing
 
1715
        repositories.
 
1716
 
 
1717
        :param revision_id: Only copy the content needed to construct
 
1718
                            revision_id and its parents.
 
1719
        """
 
1720
        try:
 
1721
            self.target.set_make_working_trees(self.source.make_working_trees())
 
1722
        except NotImplementedError:
 
1723
            pass
 
1724
        self.target.fetch(self.source, revision_id=revision_id)
 
1725
 
 
1726
    @needs_write_lock
 
1727
    def fetch(self, revision_id=None, find_ghosts=False,
 
1728
            fetch_spec=None):
 
1729
        """Fetch the content required to construct revision_id.
 
1730
 
 
1731
        The content is copied from self.source to self.target.
 
1732
 
 
1733
        :param revision_id: if None all content is copied, if NULL_REVISION no
 
1734
                            content is copied.
 
1735
        :return: None.
 
1736
        """
 
1737
        raise NotImplementedError(self.fetch)
 
1738
 
 
1739
    @needs_read_lock
 
1740
    def search_missing_revision_ids(self,
 
1741
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1742
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
1743
            limit=None):
 
1744
        """Return the revision ids that source has that target does not.
 
1745
 
 
1746
        :param revision_id: only return revision ids included by this
 
1747
            revision_id.
 
1748
        :param revision_ids: return revision ids included by these
 
1749
            revision_ids.  NoSuchRevision will be raised if any of these
 
1750
            revisions are not present.
 
1751
        :param if_present_ids: like revision_ids, but will not cause
 
1752
            NoSuchRevision if any of these are absent, instead they will simply
 
1753
            not be in the result.  This is useful for e.g. finding revisions
 
1754
            to fetch for tags, which may reference absent revisions.
 
1755
        :param find_ghosts: If True find missing revisions in deep history
 
1756
            rather than just finding the surface difference.
 
1757
        :param limit: Maximum number of revisions to return, topologically
 
1758
            ordered
 
1759
        :return: A bzrlib.graph.SearchResult.
 
1760
        """
 
1761
        raise NotImplementedError(self.search_missing_revision_ids)
 
1762
 
 
1763
    @staticmethod
 
1764
    def _same_model(source, target):
 
1765
        """True if source and target have the same data representation.
 
1766
 
 
1767
        Note: this is always called on the base class; overriding it in a
 
1768
        subclass will have no effect.
 
1769
        """
 
1770
        try:
 
1771
            InterRepository._assert_same_model(source, target)
 
1772
            return True
 
1773
        except errors.IncompatibleRepositories, e:
 
1774
            return False
 
1775
 
 
1776
    @staticmethod
 
1777
    def _assert_same_model(source, target):
 
1778
        """Raise an exception if two repositories do not use the same model.
 
1779
        """
 
1780
        if source.supports_rich_root() != target.supports_rich_root():
 
1781
            raise errors.IncompatibleRepositories(source, target,
 
1782
                "different rich-root support")
 
1783
        if source._serializer != target._serializer:
 
1784
            raise errors.IncompatibleRepositories(source, target,
 
1785
                "different serializers")
 
1786
 
 
1787
 
 
1788
class CopyConverter(object):
 
1789
    """A repository conversion tool which just performs a copy of the content.
 
1790
 
 
1791
    This is slow but quite reliable.
 
1792
    """
 
1793
 
 
1794
    def __init__(self, target_format):
 
1795
        """Create a CopyConverter.
 
1796
 
 
1797
        :param target_format: The format the resulting repository should be.
 
1798
        """
 
1799
        self.target_format = target_format
 
1800
 
 
1801
    def convert(self, repo, pb):
 
1802
        """Perform the conversion of to_convert, giving feedback via pb.
 
1803
 
 
1804
        :param to_convert: The disk object to convert.
 
1805
        :param pb: a progress bar to use for progress information.
 
1806
        """
 
1807
        pb = ui.ui_factory.nested_progress_bar()
 
1808
        self.count = 0
 
1809
        self.total = 4
 
1810
        # this is only useful with metadir layouts - separated repo content.
 
1811
        # trigger an assertion if not such
 
1812
        repo._format.get_format_string()
 
1813
        self.repo_dir = repo.bzrdir
 
1814
        pb.update('Moving repository to repository.backup')
 
1815
        self.repo_dir.transport.move('repository', 'repository.backup')
 
1816
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
 
1817
        repo._format.check_conversion_target(self.target_format)
 
1818
        self.source_repo = repo._format.open(self.repo_dir,
 
1819
            _found=True,
 
1820
            _override_transport=backup_transport)
 
1821
        pb.update('Creating new repository')
 
1822
        converted = self.target_format.initialize(self.repo_dir,
 
1823
                                                  self.source_repo.is_shared())
 
1824
        converted.lock_write()
 
1825
        try:
 
1826
            pb.update('Copying content')
 
1827
            self.source_repo.copy_content_into(converted)
 
1828
        finally:
 
1829
            converted.unlock()
 
1830
        pb.update('Deleting old repository content')
 
1831
        self.repo_dir.transport.delete_tree('repository.backup')
 
1832
        ui.ui_factory.note('repository converted')
 
1833
        pb.finished()
 
1834
 
 
1835
 
 
1836
def _strip_NULL_ghosts(revision_graph):
 
1837
    """Also don't use this. more compatibility code for unmigrated clients."""
 
1838
    # Filter ghosts, and null:
 
1839
    if _mod_revision.NULL_REVISION in revision_graph:
 
1840
        del revision_graph[_mod_revision.NULL_REVISION]
 
1841
    for key, parents in revision_graph.items():
 
1842
        revision_graph[key] = tuple(parent for parent in parents if parent
 
1843
            in revision_graph)
 
1844
    return revision_graph
 
1845
 
 
1846
 
 
1847
def _iter_for_revno(repo, partial_history_cache, stop_index=None,
 
1848
                    stop_revision=None):
 
1849
    """Extend the partial history to include a given index
 
1850
 
 
1851
    If a stop_index is supplied, stop when that index has been reached.
 
1852
    If a stop_revision is supplied, stop when that revision is
 
1853
    encountered.  Otherwise, stop when the beginning of history is
 
1854
    reached.
 
1855
 
 
1856
    :param stop_index: The index which should be present.  When it is
 
1857
        present, history extension will stop.
 
1858
    :param stop_revision: The revision id which should be present.  When
 
1859
        it is encountered, history extension will stop.
 
1860
    """
 
1861
    start_revision = partial_history_cache[-1]
 
1862
    graph = repo.get_graph()
 
1863
    iterator = graph.iter_lefthand_ancestry(start_revision,
 
1864
        (_mod_revision.NULL_REVISION,))
 
1865
    try:
 
1866
        # skip the last revision in the list
 
1867
        iterator.next()
 
1868
        while True:
 
1869
            if (stop_index is not None and
 
1870
                len(partial_history_cache) > stop_index):
 
1871
                break
 
1872
            if partial_history_cache[-1] == stop_revision:
 
1873
                break
 
1874
            revision_id = iterator.next()
 
1875
            partial_history_cache.append(revision_id)
 
1876
    except StopIteration:
 
1877
        # No more history
 
1878
        return
 
1879
 
 
1880
 
 
1881
class _LazyListJoin(object):
 
1882
    """An iterable yielding the contents of many lists as one list.
 
1883
 
 
1884
    Each iterator made from this will reflect the current contents of the lists
 
1885
    at the time the iterator is made.
 
1886
    
 
1887
    This is used by Repository's _make_parents_provider implementation so that
 
1888
    it is safe to do::
 
1889
 
 
1890
      pp = repo._make_parents_provider()      # uses a list of fallback repos
 
1891
      pp.add_fallback_repository(other_repo)  # appends to that list
 
1892
      result = pp.get_parent_map(...)
 
1893
      # The result will include revs from other_repo
 
1894
    """
 
1895
 
 
1896
    def __init__(self, *list_parts):
 
1897
        self.list_parts = list_parts
 
1898
 
 
1899
    def __iter__(self):
 
1900
        full_list = []
 
1901
        for list_part in self.list_parts:
 
1902
            full_list.extend(list_part)
 
1903
        return iter(full_list)
 
1904
 
 
1905
    def __repr__(self):
 
1906
        return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
 
1907
                              self.list_parts)