/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

fileids/revids are plain strings, not unicode

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
from bzrlib import (
 
22
    errors,
 
23
    foreign,
 
24
    urlutils,
 
25
    )
 
26
from bzrlib.inventory import (
 
27
    ROOT_ID,
 
28
    )
21
29
from bzrlib.foreign import (
22
 
        ForeignRevision,
23
 
        )
 
30
    ForeignVcs, 
 
31
    VcsMappingRegistry, 
 
32
    ForeignRevision,
 
33
    )
 
34
from bzrlib.xml_serializer import (
 
35
    escape_invalid_chars,
 
36
    )
 
37
 
 
38
DEFAULT_TREE_MODE = 0040000
 
39
DEFAULT_FILE_MODE = 0100644
 
40
DEFAULT_SYMLINK_MODE = 0120000
24
41
 
25
42
 
26
43
def escape_file_id(file_id):
35
52
    """Class that maps between Git and Bazaar semantics."""
36
53
    experimental = False
37
54
 
38
 
    def revision_id_foreign_to_bzr(self, git_rev_id):
 
55
    def __init__(self):
 
56
        super(BzrGitMapping, self).__init__(foreign_git)
 
57
 
 
58
    def __eq__(self, other):
 
59
        return type(self) == type(other) and self.revid_prefix == other.revid_prefix
 
60
 
 
61
    @classmethod
 
62
    def revision_id_foreign_to_bzr(cls, git_rev_id):
39
63
        """Convert a git revision id handle to a Bazaar revision id."""
40
 
        return "%s:%s" % (self.revid_prefix, git_rev_id)
 
64
        return "%s:%s" % (cls.revid_prefix, git_rev_id)
41
65
 
42
 
    def revision_id_bzr_to_foreign(self, bzr_rev_id):
 
66
    @classmethod
 
67
    def revision_id_bzr_to_foreign(cls, bzr_rev_id):
43
68
        """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 }
 
69
        if not bzr_rev_id.startswith("%s:" % cls.revid_prefix):
 
70
            raise errors.InvalidRevisionId(bzr_rev_id, cls)
 
71
        return bzr_rev_id[len(cls.revid_prefix)+1:], cls()
50
72
 
51
73
    def generate_file_id(self, path):
 
74
        # Git paths are just bytestrings
 
75
        # We must just hope they are valid UTF-8..
 
76
        assert isinstance(path, str)
52
77
        if path == "":
53
78
            return ROOT_ID
54
 
        return escape_file_id(path.encode('utf-8'))
 
79
        return escape_file_id(path)
 
80
 
 
81
    def parse_file_id(self, file_id):
 
82
        if file_id == ROOT_ID:
 
83
            return ""
 
84
        return unescape_file_id(file_id)
55
85
 
56
86
    def import_commit(self, commit):
57
87
        """Convert a git commit to a bzr revision.
62
92
            raise AssertionError("Commit object can't be None")
63
93
        rev = ForeignRevision(commit.id, self, self.revision_id_foreign_to_bzr(commit.id))
64
94
        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")
 
95
        rev.message = escape_invalid_chars(commit.message.decode("utf-8", "replace"))[0]
 
96
        rev.committer = escape_invalid_chars(str(commit.committer).decode("utf-8", "replace"))[0]
67
97
        if commit.committer != commit.author:
68
 
            rev.properties['author'] = str(commit.author).decode("utf-8", "replace")
 
98
            rev.properties['author'] = escape_invalid_chars(str(commit.author).decode("utf-8", "replace"))[0]
 
99
 
 
100
        if commit.commit_time != commit.author_time:
 
101
            rev.properties['author-timestamp'] = str(commit.author_time)
69
102
        rev.timestamp = commit.commit_time
70
103
        rev.timezone = 0
71
104
        return rev
72
105
 
73
106
 
74
 
class BzrGitMappingExperimental(BzrGitMapping):
 
107
class BzrGitMappingv1(BzrGitMapping):
 
108
    revid_prefix = 'git-v1'
 
109
    experimental = False
 
110
 
 
111
 
 
112
class BzrGitMappingExperimental(BzrGitMappingv1):
75
113
    revid_prefix = 'git-experimental'
76
114
    experimental = True
77
115
 
78
116
 
79
 
default_mapping = BzrGitMappingExperimental()
 
117
class GitMappingRegistry(VcsMappingRegistry):
 
118
 
 
119
    def revision_id_bzr_to_foreign(self, bzr_revid):
 
120
        if not bzr_revid.startswith("git-"):
 
121
            raise errors.InvalidRevisionId(bzr_revid, None)
 
122
        (mapping_version, git_sha) = bzr_revid.split(":", 1)
 
123
        mapping = self.get(mapping_version)
 
124
        return mapping.revision_id_bzr_to_foreign(bzr_revid)
 
125
 
 
126
    parse_revision_id = revision_id_bzr_to_foreign
 
127
 
 
128
 
 
129
mapping_registry = GitMappingRegistry()
 
130
mapping_registry.register_lazy('git-v1', "bzrlib.plugins.git.mapping",
 
131
                                   "BzrGitMappingv1")
 
132
mapping_registry.register_lazy('git-experimental', "bzrlib.plugins.git.mapping",
 
133
                                   "BzrGitMappingExperimental")
 
134
 
 
135
 
 
136
class ForeignGit(ForeignVcs):
 
137
    """Foreign Git."""
 
138
 
 
139
    def __init__(self):
 
140
        super(ForeignGit, self).__init__(mapping_registry)
 
141
 
 
142
    @classmethod
 
143
    def show_foreign_revid(cls, foreign_revid):
 
144
        return { "git commit": foreign_revid }
 
145
 
 
146
 
 
147
foreign_git = ForeignGit()
 
148
default_mapping = BzrGitMappingv1()
 
149
 
 
150
 
 
151
def text_to_blob(text):
 
152
    from dulwich.objects import Blob
 
153
    blob = Blob()
 
154
    blob._text = text
 
155
    return blob
 
156
 
 
157
 
 
158
def inventory_to_tree_and_blobs(inventory, texts, mapping):
 
159
    from dulwich.objects import Tree
 
160
    from bzrlib.inventory import InventoryDirectory, InventoryFile
 
161
    import stat
 
162
    stack = []
 
163
    cur = ""
 
164
    tree = Tree()
 
165
 
 
166
    # stack contains the set of trees that we haven't 
 
167
    # finished constructing
 
168
    for path, entry in inventory.iter_entries():
 
169
        while stack and not path.startswith(cur):
 
170
            tree.serialize()
 
171
            sha = tree.id
 
172
            yield sha, tree, cur
 
173
            t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
174
            cur, tree = stack.pop()
 
175
            tree.add(*t)
 
176
 
 
177
        if type(entry) == InventoryDirectory:
 
178
            stack.append((cur, tree))
 
179
            cur = path
 
180
            tree = Tree()
 
181
 
 
182
        if type(entry) == InventoryFile:
 
183
            #FIXME: We can make potentially make this Lazy to avoid shaing lots of stuff
 
184
            # and having all these objects in memory at once
 
185
            text = texts.get_record_stream([(entry.file_id, entry.revision)], 'unordered', True).next().get_bytes_as('fulltext')
 
186
            blob = text_to_blob(text)
 
187
            sha = blob.id
 
188
            yield sha, blob, path
 
189
 
 
190
            name = urlutils.basename(path).encode("utf-8")
 
191
            mode = stat.S_IFREG | 0644
 
192
            if entry.executable:
 
193
                mode |= 0111
 
194
            tree.add(mode, name, sha)
 
195
 
 
196
    while len(stack) > 1:
 
197
        tree.serialize()
 
198
        sha = tree.id
 
199
        yield sha, tree, cur
 
200
        t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
201
        cur, tree = stack.pop()
 
202
        tree.add(*t)
 
203
 
 
204
    tree.serialize()
 
205
    yield tree.id, tree, cur
 
206
 
 
207
 
 
208
def revision_to_commit(rev, tree_sha, parent_lookup):
 
209
    """Turn a Bazaar revision in to a Git commit
 
210
 
 
211
    :param tree_sha: Tree sha for the commit
 
212
    :param parent_lookup: Function for looking up the GIT sha equiv of a bzr revision
 
213
    :return dulwich.objects.Commit represent the revision:
 
214
    """
 
215
    from dulwich.objects import Commit
 
216
    commit = Commit()
 
217
    commit._tree = tree_sha
 
218
    for p in rev.parent_ids:
 
219
        git_p = parent_lookup(p)
 
220
        if git_p is not None:
 
221
            assert len(git_p) == 40, "unexpected length for %r" % git_p
 
222
            commit._parents.append(git_p)
 
223
    commit._message = rev.message.encode("utf-8")
 
224
    commit._committer = rev.committer.encode("utf-8")
 
225
    commit._author = rev.get_apparent_authors()[0].encode("utf-8")
 
226
    commit._commit_time = long(rev.timestamp)
 
227
    if 'author-timestamp' in rev.properties:
 
228
        commit._author_time = long(rev.properties['author-timestamp'])
 
229
    else:
 
230
        commit._author_time = commit._commit_time
 
231
    commit.serialize()
 
232
    return commit