/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/repository.py

  • Committer: Jelmer Vernooij
  • Date: 2019-05-29 03:22:34 UTC
  • mfrom: (7303 work)
  • mto: This revision was merged to the branch mainline in revision 7306.
  • Revision ID: jelmer@jelmer.uk-20190529032234-mt3fuws8gq03tapi
Merge trunk.

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