/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 repository.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
2
 
# Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
3
 
#
4
 
# This program is free software; you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation; either version 2 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
 
18
 
"""An adapter between a Git Repository and a Bazaar Branch"""
19
 
 
20
 
from bzrlib import (
21
 
    check,
22
 
    errors,
23
 
    graph as _mod_graph,
24
 
    inventory,
25
 
    repository,
26
 
    revision,
27
 
    )
28
 
try:
29
 
    from bzrlib.revisiontree import InventoryRevisionTree
30
 
except ImportError: # bzr < 2.4
31
 
    from bzrlib.revisiontree import RevisionTree as InventoryRevisionTree
32
 
from bzrlib.foreign import (
33
 
    ForeignRepository,
34
 
    )
35
 
 
36
 
from bzrlib.plugins.git.commit import (
37
 
    GitCommitBuilder,
38
 
    )
39
 
from bzrlib.plugins.git.filegraph import (
40
 
    GitFileLastChangeScanner,
41
 
    GitFileParentProvider,
42
 
    )
43
 
from bzrlib.plugins.git.mapping import (
44
 
    default_mapping,
45
 
    foreign_vcs_git,
46
 
    mapping_registry,
47
 
    )
48
 
from bzrlib.plugins.git.tree import (
49
 
    GitRevisionTree,
50
 
    )
51
 
 
52
 
 
53
 
from dulwich.objects import (
54
 
    Commit,
55
 
    Tag,
56
 
    ZERO_SHA,
57
 
    )
58
 
from dulwich.object_store import (
59
 
    tree_lookup_path,
60
 
    )
61
 
 
62
 
 
63
 
class RepoReconciler(object):
64
 
    """Reconciler that reconciles a repository.
65
 
 
66
 
    """
67
 
 
68
 
    def __init__(self, repo, other=None, thorough=False):
69
 
        """Construct a RepoReconciler.
70
 
 
71
 
        :param thorough: perform a thorough check which may take longer but
72
 
                         will correct non-data loss issues such as incorrect
73
 
                         cached data.
74
 
        """
75
 
        self.repo = repo
76
 
 
77
 
    def reconcile(self):
78
 
        """Perform reconciliation.
79
 
 
80
 
        After reconciliation the following attributes document found issues:
81
 
        inconsistent_parents: The number of revisions in the repository whose
82
 
                              ancestry was being reported incorrectly.
83
 
        garbage_inventories: The number of inventory objects without revisions
84
 
                             that were garbage collected.
85
 
        """
86
 
 
87
 
 
88
 
class GitCheck(check.Check):
89
 
 
90
 
    def __init__(self, repository, check_repo=True):
91
 
        self.repository = repository
92
 
        self.checked_rev_cnt = 0
93
 
 
94
 
    def check(self, callback_refs=None, check_repo=True):
95
 
        if callback_refs is None:
96
 
            callback_refs = {}
97
 
        self.repository.lock_read()
98
 
        self.repository.unlock()
99
 
 
100
 
    def report_results(self, verbose):
101
 
        pass
102
 
 
103
 
 
104
 
class GitRepository(ForeignRepository):
105
 
    """An adapter to git repositories for bzr."""
106
 
 
107
 
    _serializer = None
108
 
    vcs = foreign_vcs_git
109
 
    chk_bytes = None
110
 
 
111
 
    def __init__(self, gitdir, lockfiles):
112
 
        super(GitRepository, self).__init__(GitRepositoryFormat(),
113
 
            gitdir, lockfiles)
114
 
        from bzrlib.plugins.git import fetch, push
115
 
        for optimiser in [fetch.InterRemoteGitNonGitRepository,
116
 
                          fetch.InterLocalGitNonGitRepository,
117
 
                          fetch.InterGitGitRepository,
118
 
                          push.InterToLocalGitRepository,
119
 
                          push.InterToRemoteGitRepository]:
120
 
            repository.InterRepository.register_optimiser(optimiser)
121
 
 
122
 
    def add_fallback_repository(self, basis_url):
123
 
        raise errors.UnstackableRepositoryFormat(self._format,
124
 
            self.control_transport.base)
125
 
 
126
 
    def is_shared(self):
127
 
        return False
128
 
 
129
 
    def reconcile(self, other=None, thorough=False):
130
 
        """Reconcile this repository."""
131
 
        reconciler = RepoReconciler(self, thorough=thorough)
132
 
        reconciler.reconcile()
133
 
        return reconciler
134
 
 
135
 
    def supports_rich_root(self):
136
 
        return True
137
 
 
138
 
    def _warn_if_deprecated(self, branch=None): # for bzr < 2.4
139
 
        # This class isn't deprecated
140
 
        pass
141
 
 
142
 
    def get_mapping(self):
143
 
        return default_mapping
144
 
 
145
 
    def make_working_trees(self):
146
 
        return not self._git.bare
147
 
 
148
 
    def revision_graph_can_have_wrong_parents(self):
149
 
        return False
150
 
 
151
 
    def dfetch(self, source, stop_revision):
152
 
        interrepo = repository.InterRepository.get(source, self)
153
 
        return interrepo.dfetch(stop_revision)
154
 
 
155
 
    def add_signature_text(self, revid, signature):
156
 
        raise errors.UnsupportedOperation(self.add_signature_text, self)
157
 
 
158
 
 
159
 
class LocalGitRepository(GitRepository):
160
 
    """Git repository on the file system."""
161
 
 
162
 
    def __init__(self, gitdir, lockfiles):
163
 
        GitRepository.__init__(self, gitdir, lockfiles)
164
 
        self.base = gitdir.root_transport.base
165
 
        self._git = gitdir._git
166
 
        self._file_change_scanner = GitFileLastChangeScanner(self)
167
 
 
168
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
169
 
                           timezone=None, committer=None, revprops=None,
170
 
                           revision_id=None, lossy=False):
171
 
        """Obtain a CommitBuilder for this repository.
172
 
 
173
 
        :param branch: Branch to commit to.
174
 
        :param parents: Revision ids of the parents of the new revision.
175
 
        :param config: Configuration to use.
176
 
        :param timestamp: Optional timestamp recorded for commit.
177
 
        :param timezone: Optional timezone for timestamp.
178
 
        :param committer: Optional committer to set for commit.
179
 
        :param revprops: Optional dictionary of revision properties.
180
 
        :param revision_id: Optional revision id.
181
 
        :param lossy: Whether to discard data that can not be natively
182
 
            represented, when pushing to a foreign VCS
183
 
        """
184
 
        self.start_write_group()
185
 
        return GitCommitBuilder(self, parents, config,
186
 
            timestamp, timezone, committer, revprops, revision_id,
187
 
            lossy)
188
 
 
189
 
    def get_file_graph(self):
190
 
        return _mod_graph.Graph(GitFileParentProvider(
191
 
            self._file_change_scanner))
192
 
 
193
 
    def iter_files_bytes(self, desired_files):
194
 
        """Iterate through file versions.
195
 
 
196
 
        Files will not necessarily be returned in the order they occur in
197
 
        desired_files.  No specific order is guaranteed.
198
 
 
199
 
        Yields pairs of identifier, bytes_iterator.  identifier is an opaque
200
 
        value supplied by the caller as part of desired_files.  It should
201
 
        uniquely identify the file version in the caller's context.  (Examples:
202
 
        an index number or a TreeTransform trans_id.)
203
 
 
204
 
        bytes_iterator is an iterable of bytestrings for the file.  The
205
 
        kind of iterable and length of the bytestrings are unspecified, but for
206
 
        this implementation, it is a list of bytes produced by
207
 
        VersionedFile.get_record_stream().
208
 
 
209
 
        :param desired_files: a list of (file_id, revision_id, identifier)
210
 
            triples
211
 
        """
212
 
        per_revision = {}
213
 
        for (file_id, revision_id, identifier) in desired_files:
214
 
            per_revision.setdefault(revision_id, []).append(
215
 
                (file_id, identifier))
216
 
        for revid, files in per_revision.iteritems():
217
 
            (commit_id, mapping) = self.lookup_bzr_revision_id(revid)
218
 
            try:
219
 
                commit = self._git.object_store[commit_id]
220
 
            except KeyError:
221
 
                raise errors.RevisionNotPresent(revid, self)
222
 
            root_tree = commit.tree
223
 
            for fileid, identifier in files:
224
 
                path = mapping.parse_file_id(fileid)
225
 
                try:
226
 
                    obj = tree_lookup_path(
227
 
                        self._git.object_store.__getitem__, root_tree, path)
228
 
                    if isinstance(obj, tuple):
229
 
                        (mode, item_id) = obj
230
 
                        obj = self._git.object_store[item_id]
231
 
                except KeyError:
232
 
                    raise errors.RevisionNotPresent((fileid, revid), self)
233
 
                else:
234
 
                    if obj.type_name == "tree":
235
 
                        yield (identifier, [])
236
 
                    elif obj.type_name == "blob":
237
 
                        yield (identifier, obj.chunked)
238
 
                    else:
239
 
                        raise AssertionError("file text resolved to %r" % obj)
240
 
 
241
 
    def _iter_revision_ids(self):
242
 
        mapping = self.get_mapping()
243
 
        for sha in self._git.object_store:
244
 
            o = self._git.object_store[sha]
245
 
            if not isinstance(o, Commit):
246
 
                continue
247
 
            rev, roundtrip_revid, verifiers = mapping.import_commit(o,
248
 
                mapping.revision_id_foreign_to_bzr)
249
 
            yield o.id, rev.revision_id, roundtrip_revid
250
 
 
251
 
    def all_revision_ids(self):
252
 
        ret = set([])
253
 
        for git_sha, revid, roundtrip_revid in self._iter_revision_ids():
254
 
            if roundtrip_revid:
255
 
                ret.add(roundtrip_revid)
256
 
            else:
257
 
                ret.add(revid)
258
 
        return ret
259
 
 
260
 
    def _get_parents(self, revid):
261
 
        if type(revid) != str:
262
 
            raise ValueError
263
 
        try:
264
 
            (hexsha, mapping) = self.lookup_bzr_revision_id(revid)
265
 
        except errors.NoSuchRevision:
266
 
            return None
267
 
        try:
268
 
            commit = self._git[hexsha]
269
 
        except KeyError:
270
 
            return None
271
 
        return [
272
 
            self.lookup_foreign_revision_id(p, mapping)
273
 
            for p in commit.parents]
274
 
 
275
 
    def get_parent_map(self, revids):
276
 
        parent_map = {}
277
 
        for revision_id in revids:
278
 
            parents = self._get_parents(revision_id)
279
 
            if revision_id == revision.NULL_REVISION:
280
 
                parent_map[revision_id] = ()
281
 
                continue
282
 
            if parents is None:
283
 
                continue
284
 
            if len(parents) == 0:
285
 
                parents = [revision.NULL_REVISION]
286
 
            parent_map[revision_id] = tuple(parents)
287
 
        return parent_map
288
 
 
289
 
    def get_known_graph_ancestry(self, revision_ids):
290
 
        """Return the known graph for a set of revision ids and their ancestors.
291
 
        """
292
 
        pending = set(revision_ids)
293
 
        parent_map = {}
294
 
        while pending:
295
 
            this_parent_map = {}
296
 
            for revid in pending:
297
 
                if revid == revision.NULL_REVISION:
298
 
                    continue
299
 
                parents = self._get_parents(revid)
300
 
                if parents is not None:
301
 
                    this_parent_map[revid] = parents
302
 
            parent_map.update(this_parent_map)
303
 
            pending = set()
304
 
            map(pending.update, this_parent_map.itervalues())
305
 
            pending = pending.difference(parent_map)
306
 
        return _mod_graph.KnownGraph(parent_map)
307
 
 
308
 
    def get_signature_text(self, revision_id):
309
 
        raise errors.NoSuchRevision(self, revision_id)
310
 
 
311
 
    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
312
 
        result = GitCheck(self, check_repo=check_repo)
313
 
        result.check(callback_refs)
314
 
        return result
315
 
 
316
 
    def pack(self, hint=None, clean_obsolete_packs=False):
317
 
        self._git.object_store.pack_loose_objects()
318
 
 
319
 
    def lookup_foreign_revision_id(self, foreign_revid, mapping=None):
320
 
        """Lookup a revision id.
321
 
 
322
 
        """
323
 
        assert type(foreign_revid) is str
324
 
        if mapping is None:
325
 
            mapping = self.get_mapping()
326
 
        if foreign_revid == ZERO_SHA:
327
 
            return revision.NULL_REVISION
328
 
        commit = self._git.object_store[foreign_revid]
329
 
        while isinstance(commit, Tag):
330
 
            commit = self._git[commit.object[1]]
331
 
        rev, roundtrip_revid, verifiers = mapping.import_commit(commit,
332
 
            mapping.revision_id_foreign_to_bzr)
333
 
        # FIXME: check testament before doing this?
334
 
        if roundtrip_revid:
335
 
            return roundtrip_revid
336
 
        else:
337
 
            return rev.revision_id
338
 
 
339
 
    def has_signature_for_revision_id(self, revision_id):
340
 
        """Check whether a GPG signature is present for this revision.
341
 
 
342
 
        This is never the case for Git repositories.
343
 
        """
344
 
        return False
345
 
 
346
 
    def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
347
 
        """Lookup a bzr revision id in a Git repository.
348
 
 
349
 
        :param bzr_revid: Bazaar revision id
350
 
        :param mapping: Optional mapping to use
351
 
        :return: Tuple with git commit id, mapping that was used and supplement
352
 
            details
353
 
        """
354
 
        try:
355
 
            (git_sha, mapping) = mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
356
 
        except errors.InvalidRevisionId:
357
 
            if mapping is None:
358
 
                mapping = self.get_mapping()
359
 
            try:
360
 
                return (self._git.refs[mapping.revid_as_refname(bzr_revid)],
361
 
                        mapping)
362
 
            except KeyError:
363
 
                # Update refs from Git commit objects
364
 
                # FIXME: Hitting this a lot will be very inefficient...
365
 
                for git_sha, revid, roundtrip_revid in self._iter_revision_ids():
366
 
                    if not roundtrip_revid:
367
 
                        continue
368
 
                    refname = mapping.revid_as_refname(roundtrip_revid)
369
 
                    self._git.refs[refname] = git_sha
370
 
                    if roundtrip_revid == bzr_revid:
371
 
                        return git_sha, mapping
372
 
                raise errors.NoSuchRevision(self, bzr_revid)
373
 
        else:
374
 
            return (git_sha, mapping)
375
 
 
376
 
    def get_revision(self, revision_id):
377
 
        if not isinstance(revision_id, str):
378
 
            raise errors.InvalidRevisionId(revision_id, self)
379
 
        git_commit_id, mapping = self.lookup_bzr_revision_id(revision_id)
380
 
        try:
381
 
            commit = self._git[git_commit_id]
382
 
        except KeyError:
383
 
            raise errors.NoSuchRevision(self, revision_id)
384
 
        revision, roundtrip_revid, verifiers = mapping.import_commit(
385
 
            commit, self.lookup_foreign_revision_id)
386
 
        assert revision is not None
387
 
        # FIXME: check verifiers ?
388
 
        if roundtrip_revid:
389
 
            revision.revision_id = roundtrip_revid
390
 
        return revision
391
 
 
392
 
    def has_revision(self, revision_id):
393
 
        """See Repository.has_revision."""
394
 
        if revision_id == revision.NULL_REVISION:
395
 
            return True
396
 
        try:
397
 
            git_commit_id, mapping = self.lookup_bzr_revision_id(revision_id)
398
 
        except errors.NoSuchRevision:
399
 
            return False
400
 
        return (git_commit_id in self._git)
401
 
 
402
 
    def has_revisions(self, revision_ids):
403
 
        """See Repository.has_revisions."""
404
 
        return set(filter(self.has_revision, revision_ids))
405
 
 
406
 
    def get_revisions(self, revids):
407
 
        """See Repository.get_revisions."""
408
 
        return [self.get_revision(r) for r in revids]
409
 
 
410
 
    def revision_trees(self, revids):
411
 
        """See Repository.revision_trees."""
412
 
        for revid in revids:
413
 
            yield self.revision_tree(revid)
414
 
 
415
 
    def revision_tree(self, revision_id):
416
 
        """See Repository.revision_tree."""
417
 
        revision_id = revision.ensure_null(revision_id)
418
 
        if revision_id == revision.NULL_REVISION:
419
 
            inv = inventory.Inventory(root_id=None)
420
 
            inv.revision_id = revision_id
421
 
            return InventoryRevisionTree(self, inv, revision_id)
422
 
        return GitRevisionTree(self, revision_id)
423
 
 
424
 
    def get_inventory(self, revision_id):
425
 
        raise NotImplementedError(self.get_inventory)
426
 
 
427
 
    def set_make_working_trees(self, trees):
428
 
        raise errors.UnsupportedOperation(self.set_make_working_trees, self)
429
 
        # TODO: Set bare= in the configuration bug=777065
430
 
 
431
 
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
432
 
        progress=None):
433
 
        return self._git.fetch_objects(determine_wants, graph_walker, progress)
434
 
 
435
 
 
436
 
class GitRepositoryFormat(repository.RepositoryFormat):
437
 
    """Git repository format."""
438
 
 
439
 
    supports_versioned_directories = False
440
 
    supports_tree_reference = False
441
 
    rich_root_data = True
442
 
    supports_leaving_lock = False
443
 
    fast_deltas = True
444
 
    supports_funky_characters = True
445
 
    supports_external_lookups = False
446
 
    supports_full_versioned_files = False
447
 
    supports_revision_signatures = False
448
 
    revision_graph_can_have_wrong_parents = False
449
 
 
450
 
    @property
451
 
    def _matchingbzrdir(self):
452
 
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
453
 
        return LocalGitControlDirFormat()
454
 
 
455
 
    def get_format_description(self):
456
 
        return "Git Repository"
457
 
 
458
 
    def initialize(self, controldir, shared=False, _internal=False):
459
 
        from bzrlib.plugins.git.dir import GitDir
460
 
        if not isinstance(controldir, GitDir):
461
 
            raise errors.UninitializableFormat(self)
462
 
        return controldir.open_repository()
463
 
 
464
 
    def check_conversion_target(self, target_repo_format):
465
 
        return target_repo_format.rich_root_data
466
 
 
467
 
    def get_foreign_tests_repository_factory(self):
468
 
        from bzrlib.plugins.git.tests.test_repository import (
469
 
            ForeignTestsRepositoryFactory,
470
 
            )
471
 
        return ForeignTestsRepositoryFactory()
472
 
 
473
 
    def network_name(self):
474
 
        return "git"