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

Share sha map cache connections inside threads.

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
# Copyright (C) 2008 John Carr
2
4
#
3
5
# This program is free software; you can redistribute it and/or modify
4
6
# it under the terms of the GNU General Public License as published by
16
18
 
17
19
"""Converters, etc for going between Bazaar and Git ids."""
18
20
 
19
 
from bzrlib import errors, foreign
20
 
from bzrlib.inventory import ROOT_ID
 
21
import stat
 
22
 
 
23
from bzrlib import (
 
24
    errors,
 
25
    foreign,
 
26
    osutils,
 
27
    urlutils,
 
28
    )
 
29
from bzrlib.inventory import (
 
30
    ROOT_ID,
 
31
    )
21
32
from bzrlib.foreign import (
22
 
        ForeignRevision,
23
 
        )
 
33
    ForeignVcs, 
 
34
    VcsMappingRegistry, 
 
35
    ForeignRevision,
 
36
    )
 
37
from bzrlib.xml_serializer import (
 
38
    escape_invalid_chars,
 
39
    )
 
40
 
 
41
DEFAULT_FILE_MODE = stat.S_IFREG | 0644
24
42
 
25
43
 
26
44
def escape_file_id(file_id):
35
53
    """Class that maps between Git and Bazaar semantics."""
36
54
    experimental = False
37
55
 
38
 
    def revision_id_foreign_to_bzr(self, git_rev_id):
 
56
    def __init__(self):
 
57
        super(BzrGitMapping, self).__init__(foreign_git)
 
58
 
 
59
    def __eq__(self, other):
 
60
        return type(self) == type(other) and self.revid_prefix == other.revid_prefix
 
61
 
 
62
    @classmethod
 
63
    def revision_id_foreign_to_bzr(cls, git_rev_id):
39
64
        """Convert a git revision id handle to a Bazaar revision id."""
40
 
        return "%s:%s" % (self.revid_prefix, git_rev_id)
 
65
        return "%s:%s" % (cls.revid_prefix, git_rev_id)
41
66
 
42
 
    def revision_id_bzr_to_foreign(self, bzr_rev_id):
 
67
    @classmethod
 
68
    def revision_id_bzr_to_foreign(cls, bzr_rev_id):
43
69
        """Convert a Bazaar revision id to a git revision id handle."""
44
 
        if not bzr_rev_id.startswith("%s:" % self.revid_prefix):
45
 
            raise errors.InvalidRevisionId(bzr_rev_id, self)
46
 
        return bzr_rev_id[len(self.revid_prefix)+1:]
47
 
 
48
 
    def show_foreign_revid(self, foreign_revid):
49
 
        return { "git commit": foreign_revid }
 
70
        if not bzr_rev_id.startswith("%s:" % cls.revid_prefix):
 
71
            raise errors.InvalidRevisionId(bzr_rev_id, cls)
 
72
        return bzr_rev_id[len(cls.revid_prefix)+1:], cls()
50
73
 
51
74
    def generate_file_id(self, path):
 
75
        # Git paths are just bytestrings
 
76
        # We must just hope they are valid UTF-8..
 
77
        assert isinstance(path, str)
52
78
        if path == "":
53
79
            return ROOT_ID
54
 
        return escape_file_id(path.encode('utf-8'))
 
80
        return escape_file_id(path)
 
81
 
 
82
    def parse_file_id(self, file_id):
 
83
        if file_id == ROOT_ID:
 
84
            return ""
 
85
        return unescape_file_id(file_id)
55
86
 
56
87
    def import_commit(self, commit):
57
88
        """Convert a git commit to a bzr revision.
62
93
            raise AssertionError("Commit object can't be None")
63
94
        rev = ForeignRevision(commit.id, self, self.revision_id_foreign_to_bzr(commit.id))
64
95
        rev.parent_ids = tuple([self.revision_id_foreign_to_bzr(p) for p in commit.parents])
65
 
        rev.message = commit.message.decode("utf-8", "replace")
66
 
        rev.committer = str(commit.committer).decode("utf-8", "replace")
 
96
        rev.message = escape_invalid_chars(commit.message.decode("utf-8", "replace"))[0]
 
97
        rev.committer = escape_invalid_chars(str(commit.committer).decode("utf-8", "replace"))[0]
67
98
        if commit.committer != commit.author:
68
 
            rev.properties['author'] = str(commit.author).decode("utf-8", "replace")
 
99
            rev.properties['author'] = escape_invalid_chars(str(commit.author).decode("utf-8", "replace"))[0]
 
100
 
 
101
        if commit.commit_time != commit.author_time:
 
102
            rev.properties['author-timestamp'] = str(commit.author_time)
 
103
        if commit.commit_timezone != commit.author_timezone:
 
104
            rev.properties['author-timezone'] = str(commit.author_timezone)
69
105
        rev.timestamp = commit.commit_time
70
 
        rev.timezone = 0
 
106
        rev.timezone = commit.commit_timezone
71
107
        return rev
72
108
 
73
109
 
74
 
class BzrGitMappingExperimental(BzrGitMapping):
 
110
class BzrGitMappingv1(BzrGitMapping):
 
111
    revid_prefix = 'git-v1'
 
112
    experimental = False
 
113
 
 
114
 
 
115
class BzrGitMappingExperimental(BzrGitMappingv1):
75
116
    revid_prefix = 'git-experimental'
76
117
    experimental = True
77
118
 
78
119
 
79
 
default_mapping = BzrGitMappingExperimental()
 
120
class GitMappingRegistry(VcsMappingRegistry):
 
121
 
 
122
    def revision_id_bzr_to_foreign(self, bzr_revid):
 
123
        if not bzr_revid.startswith("git-"):
 
124
            raise errors.InvalidRevisionId(bzr_revid, None)
 
125
        (mapping_version, git_sha) = bzr_revid.split(":", 1)
 
126
        mapping = self.get(mapping_version)
 
127
        return mapping.revision_id_bzr_to_foreign(bzr_revid)
 
128
 
 
129
    parse_revision_id = revision_id_bzr_to_foreign
 
130
 
 
131
 
 
132
mapping_registry = GitMappingRegistry()
 
133
mapping_registry.register_lazy('git-v1', "bzrlib.plugins.git.mapping",
 
134
                                   "BzrGitMappingv1")
 
135
mapping_registry.register_lazy('git-experimental', "bzrlib.plugins.git.mapping",
 
136
                                   "BzrGitMappingExperimental")
 
137
 
 
138
 
 
139
class ForeignGit(ForeignVcs):
 
140
    """Foreign Git."""
 
141
 
 
142
    def __init__(self):
 
143
        super(ForeignGit, self).__init__(mapping_registry)
 
144
 
 
145
    @classmethod
 
146
    def show_foreign_revid(cls, foreign_revid):
 
147
        return { "git commit": foreign_revid }
 
148
 
 
149
 
 
150
foreign_git = ForeignGit()
 
151
default_mapping = BzrGitMappingv1()
 
152
 
 
153
 
 
154
def text_to_blob(texts, entry):
 
155
    from dulwich.objects import Blob
 
156
    text = texts.get_record_stream([(entry.file_id, entry.revision)], 'unordered', True).next().get_bytes_as('fulltext')
 
157
    blob = Blob()
 
158
    blob._text = text
 
159
    return blob
 
160
 
 
161
 
 
162
def symlink_to_blob(entry):
 
163
    from dulwich.objects import Blob
 
164
    blob = Blob()
 
165
    blob._text = entry.symlink_target
 
166
    return blob
 
167
 
 
168
 
 
169
def entry_mode(entry):
 
170
    if entry.kind == 'directory':
 
171
        return stat.S_IFDIR
 
172
    elif entry.kind == 'symlink':
 
173
        return stat.S_IFLNK
 
174
    elif entry.kind == 'file':
 
175
        mode = stat.S_IFREG | 0644
 
176
        if entry.executable:
 
177
            mode |= 0111
 
178
        return mode
 
179
    else:
 
180
        raise AssertionError
 
181
 
 
182
 
 
183
def directory_to_tree(entry, lookup_ie_sha1):
 
184
    from dulwich.objects import Tree
 
185
    tree = Tree()
 
186
    for name in sorted(entry.children.keys()):
 
187
        ie = entry.children[name]
 
188
        tree.add(entry_mode(ie), name.encode("utf-8"), lookup_ie_sha1(ie))
 
189
    tree.serialize()
 
190
    return tree
 
191
 
 
192
 
 
193
def inventory_to_tree_and_blobs(inventory, texts, mapping, cur=None):
 
194
    """Convert a Bazaar tree to a Git tree.
 
195
 
 
196
    :return: Yields tuples with object sha1, object and path
 
197
    """
 
198
    from dulwich.objects import Tree
 
199
    import stat
 
200
    stack = []
 
201
    if cur is None:
 
202
        cur = ""
 
203
    tree = Tree()
 
204
 
 
205
    # stack contains the set of trees that we haven't 
 
206
    # finished constructing
 
207
    for path, entry in inventory.iter_entries():
 
208
        while stack and not path.startswith(osutils.pathjoin(cur, "")):
 
209
            # We've hit a file that's not a child of the previous path
 
210
            tree.serialize()
 
211
            sha = tree.id
 
212
            yield sha, tree, cur.encode("utf-8")
 
213
            t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
214
            cur, tree = stack.pop()
 
215
            tree.add(*t)
 
216
 
 
217
        if entry.kind == "directory":
 
218
            stack.append((cur, tree))
 
219
            cur = path
 
220
            tree = Tree()
 
221
        else:
 
222
            if entry.kind == "file":
 
223
                blob = text_to_blob(texts, entry)
 
224
            elif entry.kind == "symlink":
 
225
                blob = symlink_to_blob(entry)
 
226
            else:
 
227
                raise AssertionError("Unknown kind %s" % entry.kind)
 
228
            sha = blob.id
 
229
            yield sha, blob, path.encode("utf-8")
 
230
            name = urlutils.basename(path).encode("utf-8")
 
231
            tree.add(entry_mode(entry), name, sha)
 
232
 
 
233
    while len(stack) > 1:
 
234
        tree.serialize()
 
235
        sha = tree.id
 
236
        yield sha, tree, cur.encode("utf-8")
 
237
        t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
238
        cur, tree = stack.pop()
 
239
        tree.add(*t)
 
240
 
 
241
    tree.serialize()
 
242
    yield tree.id, tree, cur.encode("utf-8")
 
243
 
 
244
 
 
245
def revision_to_commit(rev, tree_sha, parent_lookup):
 
246
    """Turn a Bazaar revision in to a Git commit
 
247
 
 
248
    :param tree_sha: Tree sha for the commit
 
249
    :param parent_lookup: Function for looking up the GIT sha equiv of a bzr revision
 
250
    :return dulwich.objects.Commit represent the revision:
 
251
    """
 
252
    from dulwich.objects import Commit
 
253
    commit = Commit()
 
254
    commit._tree = tree_sha
 
255
    for p in rev.parent_ids:
 
256
        git_p = parent_lookup(p)
 
257
        if git_p is not None:
 
258
            assert len(git_p) == 40, "unexpected length for %r" % git_p
 
259
            commit._parents.append(git_p)
 
260
    commit._message = rev.message.encode("utf-8")
 
261
    commit._committer = rev.committer.encode("utf-8")
 
262
    commit._author = rev.get_apparent_authors()[0].encode("utf-8")
 
263
    commit._commit_time = long(rev.timestamp)
 
264
    if 'author-timestamp' in rev.properties:
 
265
        commit._author_time = long(rev.properties['author-timestamp'])
 
266
    else:
 
267
        commit._author_time = commit._commit_time
 
268
    commit._commit_timezone = rev.timezone
 
269
    if 'author-timezone' in rev.properties:
 
270
        commit._author_timezone = int(rev.properties['author-timezone'])
 
271
    else:
 
272
        commit._author_timezone = commit._commit_timezone
 
273
    commit.serialize()
 
274
    return commit