/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 expected test output.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2008 Canonical Ltd
 
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, urlutils
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
 
        ForeignVcs, 
23
 
        VcsMappingRegistry, 
24
 
        ForeignRevision,
25
 
        )
 
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
 
42
 
26
43
 
27
44
def escape_file_id(file_id):
28
45
    return file_id.replace('_', '__').replace(' ', '_s')
29
46
 
30
47
 
31
48
def unescape_file_id(file_id):
32
 
    return file_id.replace("_s", " ").replace("__", "_")
 
49
    ret = []
 
50
    i = 0
 
51
    while i < len(file_id):
 
52
        if file_id[i] != '_':
 
53
            ret.append(file_id[i])
 
54
        else:
 
55
            if file_id[i+1] == '_':
 
56
                ret.append("_")
 
57
            elif file_id[i+1] == 's':
 
58
                ret.append(" ")
 
59
            else:
 
60
                raise AssertionError("unknown escape character %s" % file_id[i+1])
 
61
            i += 1
 
62
        i += 1
 
63
    return "".join(ret)
 
64
 
 
65
 
 
66
def fix_person_identifier(text):
 
67
    if "<" in text and ">" in text:
 
68
        return text
 
69
    return "%s <%s>" % (text, text)
33
70
 
34
71
 
35
72
class BzrGitMapping(foreign.VcsMapping):
55
92
        return bzr_rev_id[len(cls.revid_prefix)+1:], cls()
56
93
 
57
94
    def generate_file_id(self, path):
 
95
        # Git paths are just bytestrings
 
96
        # We must just hope they are valid UTF-8..
 
97
        assert isinstance(path, str)
58
98
        if path == "":
59
99
            return ROOT_ID
60
 
        return escape_file_id(path.encode('utf-8'))
 
100
        return escape_file_id(path)
 
101
 
 
102
    def parse_file_id(self, file_id):
 
103
        if file_id == ROOT_ID:
 
104
            return ""
 
105
        return unescape_file_id(file_id)
61
106
 
62
107
    def import_commit(self, commit):
63
108
        """Convert a git commit to a bzr revision.
68
113
            raise AssertionError("Commit object can't be None")
69
114
        rev = ForeignRevision(commit.id, self, self.revision_id_foreign_to_bzr(commit.id))
70
115
        rev.parent_ids = tuple([self.revision_id_foreign_to_bzr(p) for p in commit.parents])
71
 
        rev.message = commit.message.decode("utf-8", "replace")
72
 
        rev.committer = str(commit.committer).decode("utf-8", "replace")
 
116
        rev.message = escape_invalid_chars(commit.message.decode("utf-8", "replace"))[0]
 
117
        rev.committer = escape_invalid_chars(str(commit.committer).decode("utf-8", "replace"))[0]
73
118
        if commit.committer != commit.author:
74
 
            rev.properties['author'] = str(commit.author).decode("utf-8", "replace")
 
119
            rev.properties['author'] = escape_invalid_chars(str(commit.author).decode("utf-8", "replace"))[0]
 
120
 
 
121
        if commit.commit_time != commit.author_time:
 
122
            rev.properties['author-timestamp'] = str(commit.author_time)
 
123
        if commit.commit_timezone != commit.author_timezone:
 
124
            rev.properties['author-timezone'] = "%f" % (commit.author_timezone * .6)
75
125
        rev.timestamp = commit.commit_time
76
 
        rev.timezone = 0
 
126
        rev.timezone = int(commit.commit_timezone * .6)
 
127
        if rev.timezone / .6 != commit.commit_timezone:
 
128
            rev.properties['commit-timezone'] = "%f" % (commit.commit_timezone * .6)
77
129
        return rev
78
130
 
79
131
 
81
133
    revid_prefix = 'git-v1'
82
134
    experimental = False
83
135
 
 
136
    def __str__(self):
 
137
        return self.revid_prefix
 
138
 
84
139
 
85
140
class BzrGitMappingExperimental(BzrGitMappingv1):
86
141
    revid_prefix = 'git-experimental'
107
162
 
108
163
 
109
164
class ForeignGit(ForeignVcs):
110
 
    """Foreign Git."""
 
165
    """The Git Stupid Content Tracker"""
111
166
 
112
167
    def __init__(self):
113
168
        super(ForeignGit, self).__init__(mapping_registry)
121
176
default_mapping = BzrGitMappingv1()
122
177
 
123
178
 
124
 
def inventory_to_tree_and_blobs(repo, mapping, revision_id):
125
 
    from dulwich.objects import Tree, Blob
126
 
    from bzrlib.inventory import InventoryDirectory, InventoryFile
 
179
def text_to_blob(texts, entry):
 
180
    from dulwich.objects import Blob
 
181
    text = texts.get_record_stream([(entry.file_id, entry.revision)], 'unordered', True).next().get_bytes_as('fulltext')
 
182
    blob = Blob()
 
183
    blob._text = text
 
184
    return blob
 
185
 
 
186
 
 
187
def symlink_to_blob(entry):
 
188
    from dulwich.objects import Blob
 
189
    blob = Blob()
 
190
    blob._text = entry.symlink_target
 
191
    return blob
 
192
 
 
193
 
 
194
def entry_mode(entry):
 
195
    if entry.kind == 'directory':
 
196
        return stat.S_IFDIR
 
197
    elif entry.kind == 'symlink':
 
198
        return stat.S_IFLNK
 
199
    elif entry.kind == 'file':
 
200
        mode = stat.S_IFREG | 0644
 
201
        if entry.executable:
 
202
            mode |= 0111
 
203
        return mode
 
204
    else:
 
205
        raise AssertionError
 
206
 
 
207
 
 
208
def directory_to_tree(entry, lookup_ie_sha1):
 
209
    from dulwich.objects import Tree
 
210
    tree = Tree()
 
211
    for name in sorted(entry.children.keys()):
 
212
        ie = entry.children[name]
 
213
        tree.add(entry_mode(ie), name.encode("utf-8"), lookup_ie_sha1(ie))
 
214
    tree.serialize()
 
215
    return tree
 
216
 
 
217
 
 
218
def inventory_to_tree_and_blobs(inventory, texts, mapping, cur=None):
 
219
    """Convert a Bazaar tree to a Git tree.
 
220
 
 
221
    :return: Yields tuples with object sha1, object and path
 
222
    """
 
223
    from dulwich.objects import Tree
127
224
    import stat
128
225
    stack = []
129
 
    cur = ""
 
226
    if cur is None:
 
227
        cur = ""
130
228
    tree = Tree()
131
229
 
132
 
    inv = repo.get_inventory(revision_id)
133
 
 
134
230
    # stack contains the set of trees that we haven't 
135
231
    # finished constructing
136
 
 
137
 
    for path, entry in inv.iter_entries():
138
 
        while stack and not path.startswith(cur):
 
232
    for path, entry in inventory.iter_entries():
 
233
        while stack and not path.startswith(osutils.pathjoin(cur, "")):
 
234
            # We've hit a file that's not a child of the previous path
139
235
            tree.serialize()
140
 
            sha = tree.sha().hexdigest()
141
 
            yield sha, tree, cur
 
236
            sha = tree.id
 
237
            yield sha, tree, cur.encode("utf-8")
142
238
            t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
143
239
            cur, tree = stack.pop()
144
240
            tree.add(*t)
145
241
 
146
 
        if type(entry) == InventoryDirectory:
 
242
        if entry.kind == "directory":
147
243
            stack.append((cur, tree))
148
244
            cur = path
149
245
            tree = Tree()
150
 
 
151
 
        if type(entry) == InventoryFile:
152
 
            #FIXME: We can make potentially make this Lazy to avoid shaing lots of stuff
153
 
            # and having all these objects in memory at once
154
 
            blob = Blob()
155
 
            _, blob._text = repo.iter_files_bytes([(entry.file_id, entry.revision, path)]).next()
156
 
            sha = blob.sha().hexdigest()
157
 
            yield sha, blob, path
158
 
 
 
246
        else:
 
247
            if entry.kind == "file":
 
248
                blob = text_to_blob(texts, entry)
 
249
            elif entry.kind == "symlink":
 
250
                blob = symlink_to_blob(entry)
 
251
            else:
 
252
                raise AssertionError("Unknown kind %s" % entry.kind)
 
253
            sha = blob.id
 
254
            yield sha, blob, path.encode("utf-8")
159
255
            name = urlutils.basename(path).encode("utf-8")
160
 
            mode = stat.S_IFREG | 0644
161
 
            if entry.executable:
162
 
                mode |= 0111
163
 
            tree.add(mode, name, sha)
 
256
            tree.add(entry_mode(entry), name, sha)
164
257
 
165
258
    while len(stack) > 1:
166
259
        tree.serialize()
167
 
        sha = tree.sha().hexdigest()
168
 
        yield sha, tree, cur
 
260
        sha = tree.id
 
261
        yield sha, tree, cur.encode("utf-8")
169
262
        t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
170
263
        cur, tree = stack.pop()
171
264
        tree.add(*t)
172
265
 
173
266
    tree.serialize()
174
 
    yield tree.sha().hexdigest(), tree, cur
 
267
    yield tree.id, tree, cur.encode("utf-8")
175
268
 
176
269
 
177
270
def revision_to_commit(rev, tree_sha, parent_lookup):
187
280
    for p in rev.parent_ids:
188
281
        git_p = parent_lookup(p)
189
282
        if git_p is not None:
 
283
            assert len(git_p) == 40, "unexpected length for %r" % git_p
190
284
            commit._parents.append(git_p)
191
285
    commit._message = rev.message.encode("utf-8")
192
 
    commit._committer = rev.committer.encode("utf-8")
193
 
    commit._author = rev.get_apparent_author().encode("utf-8")
 
286
    commit._committer = fix_person_identifier(rev.committer.encode("utf-8"))
 
287
    commit._author = fix_person_identifier(rev.get_apparent_authors()[0].encode("utf-8"))
194
288
    commit._commit_time = long(rev.timestamp)
 
289
    if 'author-timestamp' in rev.properties:
 
290
        commit._author_time = long(rev.properties['author-timestamp'])
 
291
    else:
 
292
        commit._author_time = commit._commit_time
 
293
    if 'committer-timezone' in rev.properties:
 
294
        commit._commit_timezone = int(float(rev.properties['commit-timezone']) / .6)
 
295
    else:
 
296
        commit._commit_timezone = int(rev.timezone / .6) 
 
297
    if 'author-timezone' in rev.properties:
 
298
        commit._author_timezone = int(float(rev.properties['author-timezone']) / .6)
 
299
    else:
 
300
        commit._author_timezone = commit._commit_timezone 
195
301
    commit.serialize()
196
302
    return commit