/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: Eric Anderson
  • Date: 2008-11-25 01:45:22 UTC
  • mto: (0.200.116 trunk)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: eric@pixelwareinc.com-20081125014522-leeq8vts9nv3awjm
When reading branch use active branch instead of first branch.

This allows us to interact with whatever is set as the current active 
branch instead of being limited to only the first branch (master). 
Useful for creating a branch from somewhere other than master.

Limited in its usefulness as I believe git only allows switching 
branches in a non-bare repositories and most trees being branched from 
are bare repositories. Also requiring the source repository to switch
its active branch is pretty intrusive. But something is better than
nothing.

Show diffs side-by-side

added added

removed removed

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