/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

Add description of git-v1 mapping.

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