/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

SupportĀ tagĀ refs.

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 base64
 
22
import stat
 
23
 
 
24
from bzrlib import (
 
25
    errors,
 
26
    foreign,
 
27
    osutils,
 
28
    trace,
 
29
    urlutils,
 
30
    )
 
31
try:
 
32
    from bzrlib import bencode
 
33
except ImportError:
 
34
    from bzrlib.util import bencode
 
35
from bzrlib.inventory import (
 
36
    ROOT_ID,
 
37
    )
21
38
from bzrlib.foreign import (
22
 
        ForeignVcs, 
23
 
        VcsMappingRegistry, 
24
 
        ForeignRevision,
25
 
        )
 
39
    ForeignVcs,
 
40
    VcsMappingRegistry,
 
41
    ForeignRevision,
 
42
    )
 
43
from bzrlib.revision import (
 
44
    NULL_REVISION,
 
45
    )
 
46
from bzrlib.plugins.git.hg import (
 
47
    format_hg_metadata,
 
48
    extract_hg_metadata,
 
49
    )
 
50
 
 
51
DEFAULT_FILE_MODE = stat.S_IFREG | 0644
 
52
 
26
53
 
27
54
def escape_file_id(file_id):
28
55
    return file_id.replace('_', '__').replace(' ', '_s')
29
56
 
30
57
 
31
58
def unescape_file_id(file_id):
32
 
    return file_id.replace("_s", " ").replace("__", "_")
 
59
    ret = []
 
60
    i = 0
 
61
    while i < len(file_id):
 
62
        if file_id[i] != '_':
 
63
            ret.append(file_id[i])
 
64
        else:
 
65
            if file_id[i+1] == '_':
 
66
                ret.append("_")
 
67
            elif file_id[i+1] == 's':
 
68
                ret.append(" ")
 
69
            else:
 
70
                raise AssertionError("unknown escape character %s" %
 
71
                    file_id[i+1])
 
72
            i += 1
 
73
        i += 1
 
74
    return "".join(ret)
 
75
 
 
76
 
 
77
def fix_person_identifier(text):
 
78
    if "<" in text and ">" in text:
 
79
        return text
 
80
    return "%s <%s>" % (text, text)
 
81
 
 
82
 
 
83
def warn_escaped(commit, num_escaped):
 
84
    trace.warning("Escaped %d XML-invalid characters in %s. Will be unable "
 
85
                  "to regenerate the SHA map.", num_escaped, commit)
 
86
 
 
87
 
 
88
def warn_unusual_mode(commit, path, mode):
 
89
    trace.mutter("Unusual file mode %o for %s in %s. Storing as revision "
 
90
                 "property. ", mode, path, commit)
 
91
 
 
92
 
 
93
def squash_revision(target_repo, rev):
 
94
    """Remove characters that can't be stored from a revision, if necessary.
 
95
 
 
96
    :param target_repo: Repository in which the revision will be stored
 
97
    :param rev: Revision object, will be modified in-place
 
98
    """
 
99
    if not getattr(target_repo._serializer, "squashes_xml_invalid_characters", True):
 
100
        return
 
101
    from bzrlib.xml_serializer import escape_invalid_chars
 
102
    rev.message, num_escaped = escape_invalid_chars(rev.message)
 
103
    if num_escaped:
 
104
        warn_escaped(rev.foreign_revid, num_escaped)
 
105
    if 'author' in rev.properties:
 
106
        rev.properties['author'], num_escaped = escape_invalid_chars(
 
107
            rev.properties['author'])
 
108
        if num_escaped:
 
109
            warn_escaped(rev.foreign_revid, num_escaped)
 
110
    rev.committer, num_escaped = escape_invalid_chars(rev.committer)
 
111
    if num_escaped:
 
112
        warn_escaped(rev.foreign_revid, num_escaped)
33
113
 
34
114
 
35
115
class BzrGitMapping(foreign.VcsMapping):
40
120
        super(BzrGitMapping, self).__init__(foreign_git)
41
121
 
42
122
    def __eq__(self, other):
43
 
        return type(self) == type(other) and self.revid_prefix == other.revid_prefix
 
123
        return (type(self) == type(other) and 
 
124
                self.revid_prefix == other.revid_prefix)
44
125
 
45
126
    @classmethod
46
127
    def revision_id_foreign_to_bzr(cls, git_rev_id):
47
128
        """Convert a git revision id handle to a Bazaar revision id."""
 
129
        if git_rev_id == "0" * 40:
 
130
            return NULL_REVISION
48
131
        return "%s:%s" % (cls.revid_prefix, git_rev_id)
49
132
 
50
133
    @classmethod
55
138
        return bzr_rev_id[len(cls.revid_prefix)+1:], cls()
56
139
 
57
140
    def generate_file_id(self, path):
 
141
        # Git paths are just bytestrings
 
142
        # We must just hope they are valid UTF-8..
58
143
        if path == "":
59
144
            return ROOT_ID
60
 
        return escape_file_id(path.encode('utf-8'))
 
145
        return escape_file_id(path)
 
146
 
 
147
    def parse_file_id(self, file_id):
 
148
        if file_id == ROOT_ID:
 
149
            return ""
 
150
        return unescape_file_id(file_id)
 
151
 
 
152
    def import_unusual_file_modes(self, rev, unusual_file_modes):
 
153
        if unusual_file_modes:
 
154
            ret = [(name, unusual_file_modes[name])
 
155
                   for name in sorted(unusual_file_modes.keys())]
 
156
            rev.properties['file-modes'] = bencode.bencode(ret)
 
157
 
 
158
    def export_unusual_file_modes(self, rev):
 
159
        try:
 
160
            return dict([(self.generate_file_id(path), mode) for (path, mode) in bencode.bdecode(rev.properties['file-modes'].encode("utf-8"))])
 
161
        except KeyError:
 
162
            return {}
 
163
 
 
164
    def _generate_git_svn_metadata(self, rev, encoding):
 
165
        try:
 
166
            return "\ngit-svn-id: %s\n" % rev.properties["git-svn-id"].encode(
 
167
                encoding)
 
168
        except KeyError:
 
169
            return ""
 
170
 
 
171
    def _generate_hg_message_tail(self, rev):
 
172
        extra = {}
 
173
        renames = []
 
174
        branch = 'default'
 
175
        for name in rev.properties:
 
176
            if name == 'hg:extra:branch':
 
177
                branch = rev.properties['hg:extra:branch']
 
178
            elif name.startswith('hg:extra'):
 
179
                extra[name[len('hg:extra:'):]] = base64.b64decode(
 
180
                    rev.properties[name])
 
181
            elif name == 'hg:renames':
 
182
                renames = bencode.bdecode(base64.b64decode(
 
183
                    rev.properties['hg:renames']))
 
184
            # TODO: Export other properties as 'bzr:' extras?
 
185
        ret = format_hg_metadata(renames, branch, extra)
 
186
        assert isinstance(ret, str)
 
187
        return ret
 
188
 
 
189
    def _extract_git_svn_metadata(self, rev, message):
 
190
        lines = message.split("\n")
 
191
        if not (lines[-1] == "" and lines[-2].startswith("git-svn-id:")):
 
192
            return message
 
193
        git_svn_id = lines[-2].split(": ", 1)[1]
 
194
        rev.properties['git-svn-id'] = git_svn_id
 
195
        (url, rev, uuid) = parse_git_svn_id(git_svn_id)
 
196
        # FIXME: Convert this to converted-from property somehow..
 
197
        ret = "\n".join(lines[:-2])
 
198
        assert isinstance(ret, str)
 
199
        return ret
 
200
 
 
201
    def _extract_hg_metadata(self, rev, message):
 
202
        (message, renames, branch, extra) = extract_hg_metadata(message)
 
203
        if branch is not None:
 
204
            rev.properties['hg:extra:branch'] = branch
 
205
        for name, value in extra.iteritems():
 
206
            rev.properties['hg:extra:' + name] = base64.b64encode(value)
 
207
        if renames:
 
208
            rev.properties['hg:renames'] = base64.b64encode(bencode.bencode(
 
209
                [(new, old) for (old, new) in renames.iteritems()]))
 
210
        return message
 
211
 
 
212
    def _decode_commit_message(self, rev, message, encoding):
 
213
        return message.decode(encoding)
 
214
 
 
215
    def _encode_commit_message(self, rev, message, encoding):
 
216
        return message.encode(encoding)
 
217
 
 
218
    def export_commit(self, rev, tree_sha, parent_lookup):
 
219
        """Turn a Bazaar revision in to a Git commit
 
220
 
 
221
        :param tree_sha: Tree sha for the commit
 
222
        :param parent_lookup: Function for looking up the GIT sha equiv of a
 
223
            bzr revision
 
224
        :return dulwich.objects.Commit represent the revision:
 
225
        """
 
226
        from dulwich.objects import Commit
 
227
        commit = Commit()
 
228
        commit.tree = tree_sha
 
229
        for p in rev.parent_ids:
 
230
            try:
 
231
                git_p = parent_lookup(p)
 
232
            except KeyError:
 
233
                git_p = None
 
234
            if git_p is not None:
 
235
                assert len(git_p) == 40, "unexpected length for %r" % git_p
 
236
                commit.parents.append(git_p)
 
237
        try:
 
238
            encoding = rev.properties['git-explicit-encoding']
 
239
        except KeyError:
 
240
            encoding = rev.properties.get('git-implicit-encoding', 'utf-8')
 
241
        commit.encoding = rev.properties.get('git-explicit-encoding')
 
242
        commit.committer = fix_person_identifier(rev.committer.encode(
 
243
            encoding))
 
244
        commit.author = fix_person_identifier(
 
245
            rev.get_apparent_authors()[0].encode(encoding))
 
246
        commit.commit_time = long(rev.timestamp)
 
247
        if 'author-timestamp' in rev.properties:
 
248
            commit.author_time = long(rev.properties['author-timestamp'])
 
249
        else:
 
250
            commit.author_time = commit.commit_time
 
251
        commit.commit_timezone = rev.timezone
 
252
        if 'author-timezone' in rev.properties:
 
253
            commit.author_timezone = int(rev.properties['author-timezone'])
 
254
        else:
 
255
            commit.author_timezone = commit.commit_timezone
 
256
        commit.message = self._encode_commit_message(rev, rev.message, 
 
257
            encoding)
 
258
        return commit
61
259
 
62
260
    def import_commit(self, commit):
63
261
        """Convert a git commit to a bzr revision.
66
264
        """
67
265
        if commit is None:
68
266
            raise AssertionError("Commit object can't be None")
69
 
        rev = ForeignRevision(commit.id, self, self.revision_id_foreign_to_bzr(commit.id))
 
267
        rev = ForeignRevision(commit.id, self,
 
268
                self.revision_id_foreign_to_bzr(commit.id))
70
269
        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")
73
 
        if commit.committer != commit.author:
74
 
            rev.properties['author'] = str(commit.author).decode("utf-8", "replace")
 
270
        def decode_using_encoding(rev, commit, encoding):
 
271
            rev.committer = str(commit.committer).decode(encoding)
 
272
            if commit.committer != commit.author:
 
273
                rev.properties['author'] = str(commit.author).decode(encoding)
 
274
            rev.message = self._decode_commit_message(rev, commit.message, 
 
275
                encoding)
 
276
        if commit.encoding is not None:
 
277
            rev.properties['git-explicit-encoding'] = commit.encoding
 
278
            decode_using_encoding(rev, commit, commit.encoding)
 
279
        else:
 
280
            for encoding in ('utf-8', 'latin1'):
 
281
                try:
 
282
                    decode_using_encoding(rev, commit, encoding)
 
283
                except UnicodeDecodeError:
 
284
                    pass
 
285
                else:
 
286
                    if encoding != 'utf-8':
 
287
                        rev.properties['git-implicit-encoding'] = encoding
 
288
                    break
 
289
        if commit.commit_time != commit.author_time:
 
290
            rev.properties['author-timestamp'] = str(commit.author_time)
 
291
        if commit.commit_timezone != commit.author_timezone:
 
292
            rev.properties['author-timezone'] = "%d" % commit.author_timezone
75
293
        rev.timestamp = commit.commit_time
76
 
        rev.timezone = 0
 
294
        rev.timezone = commit.commit_timezone
77
295
        return rev
78
296
 
79
297
 
81
299
    revid_prefix = 'git-v1'
82
300
    experimental = False
83
301
 
 
302
    def __str__(self):
 
303
        return self.revid_prefix
 
304
 
84
305
 
85
306
class BzrGitMappingExperimental(BzrGitMappingv1):
86
307
    revid_prefix = 'git-experimental'
87
308
    experimental = True
88
309
 
 
310
    def _decode_commit_message(self, rev, message, encoding):
 
311
        message = self._extract_hg_metadata(rev, message)
 
312
        message = self._extract_git_svn_metadata(rev, message)
 
313
        return message.decode(encoding)
 
314
 
 
315
    def _encode_commit_message(self, rev, message, encoding):
 
316
        ret = message.encode(encoding)
 
317
        ret += self._generate_hg_message_tail(rev)
 
318
        ret += self._generate_git_svn_metadata(rev, encoding)
 
319
        return ret
 
320
 
 
321
    def import_commit(self, commit):
 
322
        rev = super(BzrGitMappingExperimental, self).import_commit(commit)
 
323
        rev.properties['converted_revision'] = "git %s\n" % commit.id
 
324
        return rev
 
325
 
89
326
 
90
327
class GitMappingRegistry(VcsMappingRegistry):
 
328
    """Registry with available git mappings."""
91
329
 
92
330
    def revision_id_bzr_to_foreign(self, bzr_revid):
 
331
        if bzr_revid == NULL_REVISION:
 
332
            return "0" * 20, None
93
333
        if not bzr_revid.startswith("git-"):
94
334
            raise errors.InvalidRevisionId(bzr_revid, None)
95
335
        (mapping_version, git_sha) = bzr_revid.split(":", 1)
101
341
 
102
342
mapping_registry = GitMappingRegistry()
103
343
mapping_registry.register_lazy('git-v1', "bzrlib.plugins.git.mapping",
104
 
                                   "BzrGitMappingv1")
105
 
mapping_registry.register_lazy('git-experimental', "bzrlib.plugins.git.mapping",
106
 
                                   "BzrGitMappingExperimental")
 
344
    "BzrGitMappingv1")
 
345
mapping_registry.register_lazy('git-experimental',
 
346
    "bzrlib.plugins.git.mapping", "BzrGitMappingExperimental")
 
347
mapping_registry.set_default('git-v1')
107
348
 
108
349
 
109
350
class ForeignGit(ForeignVcs):
110
 
    """Foreign Git."""
 
351
    """The Git Stupid Content Tracker"""
 
352
 
 
353
    @property
 
354
    def branch_format(self):
 
355
        from bzrlib.plugins.git.branch import GitBranchFormat
 
356
        return GitBranchFormat()
 
357
 
 
358
    @property
 
359
    def repository_format(self):
 
360
        from bzrlib.plugins.git.repository import GitRepositoryFormat
 
361
        return GitRepositoryFormat()
111
362
 
112
363
    def __init__(self):
113
364
        super(ForeignGit, self).__init__(mapping_registry)
 
365
        self.abbreviation = "git"
 
366
 
 
367
    @classmethod
 
368
    def serialize_foreign_revid(self, foreign_revid):
 
369
        return foreign_revid
114
370
 
115
371
    @classmethod
116
372
    def show_foreign_revid(cls, foreign_revid):
118
374
 
119
375
 
120
376
foreign_git = ForeignGit()
121
 
default_mapping = BzrGitMappingv1()
122
 
 
123
 
 
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
 
377
default_mapping = mapping_registry.get_default()()
 
378
 
 
379
 
 
380
def symlink_to_blob(entry):
 
381
    from dulwich.objects import Blob
 
382
    blob = Blob()
 
383
    symlink_target = entry.symlink_target
 
384
    if type(symlink_target) == unicode:
 
385
        symlink_target = symlink_target.encode('utf-8')
 
386
    blob.data = symlink_target
 
387
    return blob
 
388
 
 
389
 
 
390
def mode_is_executable(mode):
 
391
    """Check if mode should be considered executable."""
 
392
    return bool(mode & 0111)
 
393
 
 
394
 
 
395
def mode_kind(mode):
 
396
    """Determine the Bazaar inventory kind based on Unix file mode."""
 
397
    entry_kind = (mode & 0700000) / 0100000
 
398
    if entry_kind == 0:
 
399
        return 'directory'
 
400
    elif entry_kind == 1:
 
401
        file_kind = (mode & 070000) / 010000
 
402
        if file_kind == 0:
 
403
            return 'file'
 
404
        elif file_kind == 2:
 
405
            return 'symlink'
 
406
        elif file_kind == 6:
 
407
            return 'tree-reference'
 
408
        else:
 
409
            raise AssertionError(
 
410
                "Unknown file kind %d, perms=%o." % (file_kind, mode,))
 
411
    else:
 
412
        raise AssertionError(
 
413
            "Unknown kind, perms=%r." % (mode,))
 
414
 
 
415
 
 
416
def object_mode(kind, executable):
 
417
    if kind == 'directory':
 
418
        return stat.S_IFDIR
 
419
    elif kind == 'symlink':
 
420
        mode = stat.S_IFLNK
 
421
        if executable:
 
422
            mode |= 0111
 
423
        return mode
 
424
    elif kind == 'file':
 
425
        mode = stat.S_IFREG | 0644
 
426
        if executable:
 
427
            mode |= 0111
 
428
        return mode
 
429
    elif kind == 'tree-reference':
 
430
        from dulwich.objects import S_IFGITLINK
 
431
        return S_IFGITLINK
 
432
    else:
 
433
        raise AssertionError
 
434
 
 
435
 
 
436
def entry_mode(entry):
 
437
    """Determine the git file mode for an inventory entry."""
 
438
    return object_mode(entry.kind, entry.executable)
 
439
 
 
440
 
 
441
def directory_to_tree(entry, lookup_ie_sha1, unusual_modes):
 
442
    from dulwich.objects import Tree
 
443
    tree = Tree()
 
444
    for name, value in entry.children.iteritems():
 
445
        ie = entry.children[name]
 
446
        try:
 
447
            mode = unusual_modes[ie.file_id]
 
448
        except KeyError:
 
449
            mode = entry_mode(ie)
 
450
        hexsha = lookup_ie_sha1(ie)
 
451
        if hexsha is not None:
 
452
            tree.add(mode, name.encode("utf-8"), hexsha)
 
453
    if entry.parent_id is not None and len(tree) == 0:
 
454
        # Only the root can be an empty tree
 
455
        return None
 
456
    return tree
 
457
 
 
458
 
 
459
def extract_unusual_modes(rev):
 
460
    try:
 
461
        foreign_revid, mapping = mapping_registry.parse_revision_id(
 
462
            rev.revision_id)
 
463
    except errors.InvalidRevisionId:
 
464
        return {}
 
465
    else:
 
466
        return mapping.export_unusual_file_modes(rev)
 
467
 
 
468
 
 
469
def inventory_to_tree_and_blobs(inventory, texts, mapping, unusual_modes,
 
470
                                cur=None):
 
471
    """Convert a Bazaar tree to a Git tree.
 
472
 
 
473
    :return: Yields tuples with object sha1, object and path
 
474
    """
 
475
    from dulwich.objects import Tree
127
476
    import stat
128
477
    stack = []
129
 
    cur = ""
 
478
    if cur is None:
 
479
        cur = ""
130
480
    tree = Tree()
131
481
 
132
 
    inv = repo.get_inventory(revision_id)
133
 
 
134
 
    # stack contains the set of trees that we haven't 
 
482
    # stack contains the set of trees that we haven't
135
483
    # finished constructing
136
 
 
137
 
    for path, entry in inv.iter_entries():
138
 
        while stack and not path.startswith(cur):
139
 
            tree.serialize()
140
 
            sha = tree.sha().hexdigest()
141
 
            yield sha, tree, cur
142
 
            t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
484
    for path, entry in inventory.iter_entries():
 
485
        while stack and not path.startswith(osutils.pathjoin(cur, "")):
 
486
            # We've hit a file that's not a child of the previous path
 
487
            sha = tree.id
 
488
            yield sha, tree, cur.encode("utf-8")
 
489
            mode = unusual_modes.get(cur.encode("utf-8"), stat.S_IFDIR)
 
490
            t = (mode, urlutils.basename(cur).encode('UTF-8'), sha)
143
491
            cur, tree = stack.pop()
144
492
            tree.add(*t)
145
493
 
146
 
        if type(entry) == InventoryDirectory:
 
494
        if entry.kind == "directory":
147
495
            stack.append((cur, tree))
148
496
            cur = path
149
497
            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
 
 
 
498
        else:
 
499
            if entry.kind == "file":
 
500
                from dulwich.objects import Blob
 
501
                stream = texts.get_record_stream(
 
502
                    [(entry.file_id, entry.revision)], 'unordered', True)
 
503
                blob = Blob()
 
504
                blob.chunked = stream.next().get_bytes_as('chunks')
 
505
            elif entry.kind == "symlink":
 
506
                blob = symlink_to_blob(entry)
 
507
            else:
 
508
                raise AssertionError("Unknown kind %s" % entry.kind)
 
509
            sha = blob.id
 
510
            yield sha, blob, path.encode("utf-8")
159
511
            name = urlutils.basename(path).encode("utf-8")
160
 
            mode = stat.S_IFREG | 0644
161
 
            if entry.executable:
162
 
                mode |= 0111
 
512
            mode = unusual_modes.get(path.encode("utf-8"), entry_mode(entry))
163
513
            tree.add(mode, name, sha)
164
514
 
165
515
    while len(stack) > 1:
166
 
        tree.serialize()
167
 
        sha = tree.sha().hexdigest()
168
 
        yield sha, tree, cur
169
 
        t = (stat.S_IFDIR, urlutils.basename(cur).encode('UTF-8'), sha)
 
516
        sha = tree.id
 
517
        yield sha, tree, cur.encode("utf-8")
 
518
        mode = unusual_modes.get(cur.encode('utf-8'), stat.S_IFDIR)
 
519
        t = (mode, urlutils.basename(cur).encode('UTF-8'), sha)
170
520
        cur, tree = stack.pop()
171
521
        tree.add(*t)
172
522
 
173
 
    tree.serialize()
174
 
    yield tree.sha().hexdigest(), tree, cur
175
 
 
176
 
 
177
 
def revision_to_commit(rev, tree_sha, parent_lookup):
178
 
    """Turn a Bazaar revision in to a Git commit
179
 
 
180
 
    :param tree_sha: Tree sha for the commit
181
 
    :param parent_lookup: Function for looking up the GIT sha equiv of a bzr revision
182
 
    :return dulwich.objects.Commit represent the revision:
183
 
    """
184
 
    from dulwich.objects import Commit
185
 
    commit = Commit()
186
 
    commit._tree = tree_sha
187
 
    for p in rev.parent_ids:
188
 
        git_p = parent_lookup(p)
189
 
        if git_p is not None:
190
 
            commit._parents.append(git_p)
191
 
    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")
194
 
    commit._commit_time = long(rev.timestamp)
195
 
    commit.serialize()
196
 
    return commit
 
523
    yield tree.id, tree, cur.encode("utf-8")
 
524
 
 
525
 
 
526
def parse_git_svn_id(text):
 
527
    (head, uuid) = text.rsplit(" ", 1)
 
528
    (full_url, rev) = head.rsplit("@", 1)
 
529
    return (full_url, int(rev), uuid)