/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

Fix branch cloning.

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