/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: 2007-10-05 02:41:37 UTC
  • mto: (2592.3.166 repository)
  • mto: This revision was merged to the branch mainline in revision 2896.
  • Revision ID: robertc@robertcollins.net-20071005024137-kn7brcu07nu8cwl1
* The class ``bzrlib.repofmt.knitrepo.KnitRepository3`` has been folded into
  ``KnitRepository`` by parameters to the constructor. (Robert Collins)
* ``bzrlib.xml_serializer.Serializer`` is now responsible for checking that
  mandatory attributes are present on serialisation and deserialisation.
  This fixes some holes in API usage and allows better separation between
  physical storage and object serialisation. (Robert Collins)

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
 
import bzrlib
21
 
from bzrlib import (
22
 
    errors,
23
 
    graph,
24
 
    inventory,
25
 
    osutils,
26
 
    repository,
27
 
    revision,
28
 
    revisiontree,
29
 
    ui,
30
 
    urlutils,
31
 
    )
32
 
from bzrlib.foreign import (
33
 
    ForeignRepository,
34
 
    )
35
 
from bzrlib.trace import (
36
 
    mutter,
37
 
    )
38
 
from bzrlib.transport import (
39
 
    get_transport,
40
 
    )
41
 
 
42
 
from bzrlib.plugins.git.foreign import (
43
 
    versionedfiles,
44
 
    )
45
 
from bzrlib.plugins.git.mapping import (
46
 
    default_mapping,
47
 
    inventory_to_tree_and_blobs,
48
 
    mapping_registry,
49
 
    revision_to_commit,
50
 
    )
51
 
from bzrlib.plugins.git.versionedfiles import (
52
 
    GitTexts,
53
 
    )
54
 
 
55
 
import dulwich as git
56
 
import os
57
 
import time
58
 
 
59
 
 
60
 
class GitTags(object):
61
 
 
62
 
    def __init__(self, tags):
63
 
        self._tags = tags
64
 
 
65
 
    def __iter__(self):
66
 
        return iter(self._tags)
67
 
 
68
 
 
69
 
class GitRepository(ForeignRepository):
70
 
    """An adapter to git repositories for bzr."""
71
 
 
72
 
    _serializer = None
73
 
 
74
 
    def __init__(self, gitdir, lockfiles):
75
 
        ForeignRepository.__init__(self, GitRepositoryFormat(), gitdir, 
76
 
            lockfiles)
77
 
        from bzrlib.plugins.git import fetch, push
78
 
        for optimiser in [fetch.InterRemoteGitNonGitRepository, 
79
 
                          fetch.InterLocalGitNonGitRepository,
80
 
                          fetch.InterGitRepository,
81
 
                          push.InterToGitRepository]:
82
 
            repository.InterRepository.register_optimiser(optimiser)
83
 
 
84
 
    def is_shared(self):
85
 
        return True
86
 
 
87
 
    def supports_rich_root(self):
88
 
        return True
89
 
 
90
 
    def _warn_if_deprecated(self):
91
 
        # This class isn't deprecated
92
 
        pass
93
 
 
94
 
    def get_mapping(self):
95
 
        return default_mapping
96
 
 
97
 
    def make_working_trees(self):
98
 
        return True
99
 
 
100
 
 
101
 
class LocalGitRepository(GitRepository):
102
 
    """Git repository on the file system."""
103
 
 
104
 
    def __init__(self, gitdir, lockfiles):
105
 
        # FIXME: This also caches negatives. Need to be more careful 
106
 
        # about this once we start writing to git
107
 
        self._parents_provider = graph.CachingParentsProvider(self)
108
 
        GitRepository.__init__(self, gitdir, lockfiles)
109
 
        self.base = gitdir.root_transport.base
110
 
        self._git = gitdir._git
111
 
        self.texts = None
112
 
        self.signatures = versionedfiles.VirtualSignatureTexts(self)
113
 
        self.revisions = versionedfiles.VirtualRevisionTexts(self)
114
 
        self.inventories = versionedfiles.VirtualInventoryTexts(self)
115
 
        self.texts = GitTexts(self)
116
 
        self.tags = GitTags(self._git.get_tags())
117
 
 
118
 
    def all_revision_ids(self):
119
 
        ret = set([revision.NULL_REVISION])
120
 
        heads = self._git.heads()
121
 
        if heads == {}:
122
 
            return ret
123
 
        bzr_heads = [self.get_mapping().revision_id_foreign_to_bzr(h) for h in heads.itervalues()]
124
 
        ret = set(bzr_heads)
125
 
        graph = self.get_graph()
126
 
        for rev, parents in graph.iter_ancestry(bzr_heads):
127
 
            ret.add(rev)
128
 
        return ret
129
 
 
130
 
    #def get_revision_delta(self, revision_id):
131
 
    #    parent_revid = self.get_revision(revision_id).parent_ids[0]
132
 
    #    diff = self._git.diff(ids.convert_revision_id_bzr_to_git(parent_revid),
133
 
    #                   ids.convert_revision_id_bzr_to_git(revision_id))
134
 
 
135
 
    def _make_parents_provider(self):
136
 
        """See Repository._make_parents_provider()."""
137
 
        return self._parents_provider
138
 
 
139
 
    def get_parent_map(self, revids):
140
 
        parent_map = {}
141
 
        mutter("get_parent_map(%r)", revids)
142
 
        for revision_id in revids:
143
 
            assert isinstance(revision_id, str)
144
 
            if revision_id == revision.NULL_REVISION:
145
 
                parent_map[revision_id] = ()
146
 
                continue
147
 
            hexsha, mapping = self.lookup_git_revid(revision_id)
148
 
            commit  = self._git.commit(hexsha)
149
 
            if commit is None:
150
 
                continue
151
 
            else:
152
 
                parent_map[revision_id] = [mapping.revision_id_foreign_to_bzr(p) for p in commit.parents]
153
 
        return parent_map
154
 
 
155
 
    def get_ancestry(self, revision_id, topo_sorted=True):
156
 
        """See Repository.get_ancestry().
157
 
        """
158
 
        if revision_id is None:
159
 
            return [None, revision.NULL_REVISION] + self._all_revision_ids()
160
 
        assert isinstance(revision_id, str)
161
 
        ancestry = []
162
 
        graph = self.get_graph()
163
 
        for rev, parents in graph.iter_ancestry([revision_id]):
164
 
            ancestry.append(rev)
165
 
        ancestry.reverse()
166
 
        return [None] + ancestry
167
 
 
168
 
    def import_revision_gist(self, source, revid, parent_lookup):
169
 
        """Import the gist of a revision into this Git repository.
170
 
 
171
 
        """
172
 
        objects = []
173
 
        rev = source.get_revision(revid)
174
 
        for sha, object, path in inventory_to_tree_and_blobs(source, None, revid):
175
 
            if path == "":
176
 
                tree_sha = sha
177
 
            objects.append((object, path))
178
 
        commit = revision_to_commit(rev, tree_sha, parent_lookup)
179
 
        objects.append((commit, None))
180
 
        self._git.object_store.add_objects(objects)
181
 
        return commit.sha().hexdigest()
182
 
 
183
 
    def dfetch(self, source, stop_revision):
184
 
        """Import the gist of the ancestry of a particular revision."""
185
 
        if stop_revision is None:
186
 
            raise NotImplementedError
187
 
        revidmap = {}
188
 
        gitidmap = {}
189
 
        def parent_lookup(revid):
190
 
            try:
191
 
                return gitidmap[revid]
192
 
            except KeyError:
193
 
                return self.lookup_git_revid(revid)[0]
194
 
        todo = []
195
 
        source.lock_write()
196
 
        try:
197
 
            graph = source.get_graph()
198
 
            ancestry = [x for x in source.get_ancestry(stop_revision) if x is not None]
199
 
            for revid in graph.iter_topo_order(ancestry):
200
 
                if not self.has_revision(revid):
201
 
                    todo.append(revid)
202
 
            pb = ui.ui_factory.nested_progress_bar()
203
 
            try:
204
 
                for i, revid in enumerate(todo):
205
 
                    pb.update("pushing revisions", i, len(todo))
206
 
                    git_commit = self.import_revision_gist(source, revid,
207
 
                        parent_lookup)
208
 
                    gitidmap[revid] = git_commit
209
 
                    git_revid = self.get_mapping().revision_id_foreign_to_bzr(
210
 
                        git_commit)
211
 
                    revidmap[revid] = git_revid
212
 
            finally:
213
 
                pb.finished()
214
 
            source.fetch(self, revision_id=revidmap[stop_revision])
215
 
        finally:
216
 
            source.unlock()
217
 
        return revidmap
218
 
 
219
 
    def get_signature_text(self, revision_id):
220
 
        raise errors.NoSuchRevision(self, revision_id)
221
 
 
222
 
    def lookup_revision_id(self, revid):
223
 
        """Lookup a revision id.
224
 
        
225
 
        :param revid: Bazaar revision id.
226
 
        :return: Tuple with git revisionid and mapping.
227
 
        """
228
 
        # Yes, this doesn't really work, but good enough as a stub
229
 
        return osutils.sha(rev_id).hexdigest(), self.get_mapping()
230
 
 
231
 
    def has_signature_for_revision_id(self, revision_id):
232
 
        return False
233
 
 
234
 
    def lookup_git_revid(self, bzr_revid):
235
 
        try:
236
 
            return mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
237
 
        except errors.InvalidRevisionId:
238
 
            raise errors.NoSuchRevision(self, bzr_revid)
239
 
 
240
 
    def get_revision(self, revision_id):
241
 
        git_commit_id, mapping = self.lookup_git_revid(revision_id)
242
 
        try:
243
 
            commit = self._git.commit(git_commit_id)
244
 
        except KeyError:
245
 
            raise errors.NoSuchRevision(self, revision_id)
246
 
        # print "fetched revision:", git_commit_id
247
 
        revision = mapping.import_commit(commit)
248
 
        assert revision is not None
249
 
        return revision
250
 
 
251
 
    def has_revision(self, revision_id):
252
 
        try:
253
 
            self.get_revision(revision_id)
254
 
        except errors.NoSuchRevision:
255
 
            return False
256
 
        else:
257
 
            return True
258
 
 
259
 
    def get_revisions(self, revids):
260
 
        return [self.get_revision(r) for r in revids]
261
 
 
262
 
    def revision_trees(self, revids):
263
 
        for revid in revids:
264
 
            yield self.revision_tree(revid)
265
 
 
266
 
    def revision_tree(self, revision_id):
267
 
        revision_id = revision.ensure_null(revision_id)
268
 
        if revision_id == revision.NULL_REVISION:
269
 
            inv = inventory.Inventory(root_id=None)
270
 
            inv.revision_id = revision_id
271
 
            return revisiontree.RevisionTree(self, inv, revision_id)
272
 
        return GitRevisionTree(self, revision_id)
273
 
 
274
 
    def get_inventory(self, revision_id):
275
 
        assert revision_id != None
276
 
        return self.revision_tree(revision_id).inventory
277
 
 
278
 
    def set_make_working_trees(self, trees):
279
 
        pass
280
 
 
281
 
    def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
282
 
        progress=None):
283
 
        return self._git.fetch_objects(determine_wants, graph_walker, progress)
284
 
 
285
 
 
286
 
class GitRevisionTree(revisiontree.RevisionTree):
287
 
 
288
 
    def __init__(self, repository, revision_id):
289
 
        self._repository = repository
290
 
        self._revision_id = revision_id
291
 
        assert isinstance(revision_id, str)
292
 
        git_id, self.mapping = repository.lookup_git_revid(revision_id)
293
 
        try:
294
 
            commit = repository._git.commit(git_id)
295
 
        except KeyError, r:
296
 
            raise errors.NoSuchRevision(repository, revision_id)
297
 
        self.tree = commit.tree
298
 
        self._inventory = inventory.Inventory(revision_id=revision_id)
299
 
        self._inventory.root.revision = revision_id
300
 
        self._build_inventory(self.tree, self._inventory.root, "")
301
 
 
302
 
    def get_revision_id(self):
303
 
        return self._revision_id
304
 
 
305
 
    def get_file_text(self, file_id):
306
 
        entry = self._inventory[file_id]
307
 
        if entry.kind == 'directory': return ""
308
 
        return self._repository._git.get_blob(entry.text_id).data
309
 
 
310
 
    def _build_inventory(self, tree_id, ie, path):
311
 
        assert isinstance(path, str)
312
 
        tree = self._repository._git.tree(tree_id)
313
 
        for mode, name, hexsha in tree.entries():
314
 
            basename = name.decode("utf-8")
315
 
            if path == "":
316
 
                child_path = name
317
 
            else:
318
 
                child_path = urlutils.join(path, name)
319
 
            file_id = self.mapping.generate_file_id(child_path)
320
 
            entry_kind = (mode & 0700000) / 0100000
321
 
            if entry_kind == 0:
322
 
                child_ie = inventory.InventoryDirectory(file_id, basename, ie.file_id)
323
 
            elif entry_kind == 1:
324
 
                file_kind = (mode & 070000) / 010000
325
 
                b = self._repository._git.get_blob(hexsha)
326
 
                if file_kind == 0:
327
 
                    child_ie = inventory.InventoryFile(file_id, basename, ie.file_id)
328
 
                    child_ie.text_sha1 = osutils.sha_string(b.data)
329
 
                elif file_kind == 2:
330
 
                    child_ie = inventory.InventoryLink(file_id, basename, ie.file_id)
331
 
                    child_ie.symlink_target = b.data
332
 
                    child_ie.text_sha1 = osutils.sha_string("")
333
 
                else:
334
 
                    raise AssertionError(
335
 
                        "Unknown file kind, perms=%o." % (mode,))
336
 
                child_ie.text_id = b.id
337
 
                child_ie.text_size = len(b.data)
338
 
            else:
339
 
                raise AssertionError(
340
 
                    "Unknown blob kind, perms=%r." % (mode,))
341
 
            fs_mode = mode & 0777
342
 
            child_ie.executable = bool(fs_mode & 0111)
343
 
            # TODO: This should be set to the revision id in which 
344
 
            # child_ie was last changed instead.
345
 
            child_ie.revision = self._revision_id
346
 
            self._inventory.add(child_ie)
347
 
            if entry_kind == 0:
348
 
                self._build_inventory(hexsha, child_ie, child_path)
349
 
 
350
 
 
351
 
class GitRepositoryFormat(repository.RepositoryFormat):
352
 
 
353
 
    supports_tree_reference = False
354
 
    rich_root_data = True
355
 
 
356
 
    def get_format_description(self):
357
 
        return "Git Repository"
358
 
 
359
 
    def initialize(self, url, shared=False, _internal=False):
360
 
        raise bzr_errors.UninitializableFormat(self)
361
 
 
362
 
    def check_conversion_target(self, target_repo_format):
363
 
        return target_repo_format.rich_root_data