/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

Move config to a separate file, support BranchConfig.username().

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):
31
49
    return file_id.replace("_s", " ").replace("__", "_")
32
50
 
33
51
 
 
52
def fix_person_identifier(text):
 
53
    if "<" in text and ">" in text:
 
54
        return text
 
55
    return "%s <%s>" % (text, text)
 
56
 
 
57
 
34
58
class BzrGitMapping(foreign.VcsMapping):
35
59
    """Class that maps between Git and Bazaar semantics."""
36
60
    experimental = False
37
61
 
38
 
    def revision_id_foreign_to_bzr(self, git_rev_id):
 
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):
39
70
        """Convert a git revision id handle to a Bazaar revision id."""
40
 
        return "%s:%s" % (self.revid_prefix, git_rev_id)
 
71
        return "%s:%s" % (cls.revid_prefix, git_rev_id)
41
72
 
42
 
    def revision_id_bzr_to_foreign(self, bzr_rev_id):
 
73
    @classmethod
 
74
    def revision_id_bzr_to_foreign(cls, bzr_rev_id):
43
75
        """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 }
 
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()
50
79
 
51
80
    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)
52
84
        if path == "":
53
85
            return ROOT_ID
54
 
        return escape_file_id(path.encode('utf-8'))
 
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)
55
92
 
56
93
    def import_commit(self, commit):
57
94
        """Convert a git commit to a bzr revision.
62
99
            raise AssertionError("Commit object can't be None")
63
100
        rev = ForeignRevision(commit.id, self, self.revision_id_foreign_to_bzr(commit.id))
64
101
        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")
 
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]
67
104
        if commit.committer != commit.author:
68
 
            rev.properties['author'] = str(commit.author).decode("utf-8", "replace")
 
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)
69
111
        rev.timestamp = commit.commit_time
70
 
        rev.timezone = 0
 
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)
71
115
        return rev
72
116
 
73
117
 
74
 
class BzrGitMappingExperimental(BzrGitMapping):
 
118
class BzrGitMappingv1(BzrGitMapping):
 
119
    revid_prefix = 'git-v1'
 
120
    experimental = False
 
121
 
 
122
 
 
123
class BzrGitMappingExperimental(BzrGitMappingv1):
75
124
    revid_prefix = 'git-experimental'
76
125
    experimental = True
77
126
 
78
127
 
79
 
default_mapping = BzrGitMappingExperimental()
 
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