/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

Simplify converter a bit.

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]
69
99
        rev.timestamp = commit.commit_time
70
100
        rev.timezone = 0
71
101
        return rev
72
102
 
73
103
 
74
 
class BzrGitMappingExperimental(BzrGitMapping):
 
104
class BzrGitMappingv1(BzrGitMapping):
 
105
    revid_prefix = 'git-v1'
 
106
    experimental = False
 
107
 
 
108
 
 
109
class BzrGitMappingExperimental(BzrGitMappingv1):
75
110
    revid_prefix = 'git-experimental'
76
111
    experimental = True
77
112
 
78
113
 
79
 
default_mapping = BzrGitMappingExperimental()
 
114
class GitMappingRegistry(VcsMappingRegistry):
 
115
 
 
116
    def revision_id_bzr_to_foreign(self, bzr_revid):
 
117
        if not bzr_revid.startswith("git-"):
 
118
            raise errors.InvalidRevisionId(bzr_revid, None)
 
119
        (mapping_version, git_sha) = bzr_revid.split(":", 1)
 
120
        mapping = self.get(mapping_version)
 
121
        return mapping.revision_id_bzr_to_foreign(bzr_revid)
 
122
 
 
123
    parse_revision_id = revision_id_bzr_to_foreign
 
124
 
 
125
 
 
126
mapping_registry = GitMappingRegistry()
 
127
mapping_registry.register_lazy('git-v1', "bzrlib.plugins.git.mapping",
 
128
                                   "BzrGitMappingv1")
 
129
mapping_registry.register_lazy('git-experimental', "bzrlib.plugins.git.mapping",
 
130
                                   "BzrGitMappingExperimental")
 
131
 
 
132
 
 
133
class ForeignGit(ForeignVcs):
 
134
    """Foreign Git."""
 
135
 
 
136
    def __init__(self):
 
137
        super(ForeignGit, self).__init__(mapping_registry)
 
138
 
 
139
    @classmethod
 
140
    def show_foreign_revid(cls, foreign_revid):
 
141
        return { "git commit": foreign_revid }
 
142
 
 
143
 
 
144
foreign_git = ForeignGit()
 
145
default_mapping = BzrGitMappingv1()
 
146
 
 
147
 
 
148
def text_to_blob(text):
 
149
    from dulwich.objects import Blob
 
150
    blob = Blob()
 
151
    blob._text = text
 
152
    return blob
 
153
 
 
154
 
 
155
def inventory_to_tree_and_blobs(repo, mapping, revision_id):
 
156
    from dulwich.objects import Tree
 
157
    from bzrlib.inventory import InventoryDirectory, InventoryFile
 
158
    import stat
 
159
    stack = []
 
160
    cur = ""
 
161
    tree = Tree()
 
162
 
 
163
    inv = repo.get_inventory(revision_id)
 
164
 
 
165
    # stack contains the set of trees that we haven't 
 
166
    # finished constructing
 
167
    for path, entry in inv.iter_entries():
 
168
        while stack and not path.startswith(cur):
 
169
            tree.serialize()
 
170
            sha = tree.id
 
171
            yield sha, tree, cur
 
172
            t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
173
            cur, tree = stack.pop()
 
174
            tree.add(*t)
 
175
 
 
176
        if type(entry) == InventoryDirectory:
 
177
            stack.append((cur, tree))
 
178
            cur = path
 
179
            tree = Tree()
 
180
 
 
181
        if type(entry) == InventoryFile:
 
182
            #FIXME: We can make potentially make this Lazy to avoid shaing lots of stuff
 
183
            # and having all these objects in memory at once
 
184
            text = repo.texts.get_record_stream([(entry.file_id, entry.revision)], 'unordered', True).next().get_bytes_as('fulltext')
 
185
            blob = text_to_blob(text)
 
186
            sha = blob.id
 
187
            yield sha, blob, path
 
188
 
 
189
            name = urlutils.basename(path).encode("utf-8")
 
190
            mode = stat.S_IFREG | 0644
 
191
            if entry.executable:
 
192
                mode |= 0111
 
193
            tree.add(mode, name, sha)
 
194
 
 
195
    while len(stack) > 1:
 
196
        tree.serialize()
 
197
        sha = tree.id
 
198
        yield sha, tree, cur
 
199
        t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
200
        cur, tree = stack.pop()
 
201
        tree.add(*t)
 
202
 
 
203
    tree.serialize()
 
204
    yield tree.id, tree, cur
 
205
 
 
206
 
 
207
def revision_to_commit(rev, tree_sha, parent_lookup):
 
208
    """Turn a Bazaar revision in to a Git commit
 
209
 
 
210
    :param tree_sha: Tree sha for the commit
 
211
    :param parent_lookup: Function for looking up the GIT sha equiv of a bzr revision
 
212
    :return dulwich.objects.Commit represent the revision:
 
213
    """
 
214
    from dulwich.objects import Commit
 
215
    commit = Commit()
 
216
    commit._tree = tree_sha
 
217
    for p in rev.parent_ids:
 
218
        git_p = parent_lookup(p)
 
219
        if git_p is not None:
 
220
            assert len(git_p) == 40, "unexpected length for %r" % git_p
 
221
            commit._parents.append(git_p)
 
222
    commit._message = rev.message.encode("utf-8")
 
223
    commit._committer = rev.committer.encode("utf-8")
 
224
    commit._author = rev.get_apparent_authors()[0].encode("utf-8")
 
225
    commit._commit_time = long(rev.timestamp)
 
226
    commit.serialize()
 
227
    return commit