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

Ignore .plugins dir.

Show diffs side-by-side

added added

removed removed

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