88
84
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)
85
trace.mutter("Unusual file mode %o for %s in %s. Storing as revision property. ",
93
89
def squash_revision(target_repo, rev):
120
116
super(BzrGitMapping, self).__init__(foreign_git)
122
118
def __eq__(self, other):
123
return (type(self) == type(other) and
124
self.revid_prefix == other.revid_prefix)
119
return type(self) == type(other) and self.revid_prefix == other.revid_prefix
127
122
def revision_id_foreign_to_bzr(cls, git_rev_id):
128
123
"""Convert a git revision id handle to a Bazaar revision id."""
129
if git_rev_id == "0" * 40:
131
124
return "%s:%s" % (cls.revid_prefix, git_rev_id)
152
145
def import_unusual_file_modes(self, rev, unusual_file_modes):
153
146
if unusual_file_modes:
154
ret = [(name, unusual_file_modes[name])
155
for name in sorted(unusual_file_modes.keys())]
147
ret = [(name, unusual_file_modes[name]) for name in sorted(unusual_file_modes.keys())]
156
148
rev.properties['file-modes'] = bencode.bencode(ret)
158
150
def export_unusual_file_modes(self, rev):
164
def _generate_git_svn_metadata(self, rev, encoding):
156
def _generate_git_svn_metadata(self, rev):
166
return "\ngit-svn-id: %s\n" % rev.properties["git-svn-id"].encode(
158
return "\ngit-svn-id: %s\n" % rev.properties["git-svn-id"].encode("utf-8")
176
167
if name == 'hg:extra:branch':
177
168
branch = rev.properties['hg:extra:branch']
178
169
elif name.startswith('hg:extra'):
179
extra[name[len('hg:extra:'):]] = base64.b64decode(
180
rev.properties[name])
170
extra[name[len('hg:extra:'):]] = base64.b64decode(rev.properties[name])
181
171
elif name == 'hg:renames':
182
renames = bencode.bdecode(base64.b64decode(
183
rev.properties['hg:renames']))
172
renames = bencode.bdecode(base64.b64decode(rev.properties['hg:renames']))
184
173
# TODO: Export other properties as 'bzr:' extras?
185
174
ret = format_hg_metadata(renames, branch, extra)
186
175
assert isinstance(ret, str)
205
194
for name, value in extra.iteritems():
206
195
rev.properties['hg:extra:' + name] = base64.b64encode(value)
208
rev.properties['hg:renames'] = base64.b64encode(bencode.bencode(
209
[(new, old) for (old, new) in renames.iteritems()]))
197
rev.properties['hg:renames'] = base64.b64encode(bencode.bencode([(new, old) for (old, new) in renames.iteritems()]))
212
def _decode_commit_message(self, rev, message, encoding):
213
return message.decode(encoding)
200
def _decode_commit_message(self, rev, message):
201
return message.decode("utf-8", "replace")
215
def _encode_commit_message(self, rev, message, encoding):
216
return message.encode(encoding)
203
def _encode_commit_message(self, rev, message):
204
return message.encode("utf-8")
218
206
def export_commit(self, rev, tree_sha, parent_lookup):
219
207
"""Turn a Bazaar revision in to a Git commit
221
209
:param tree_sha: Tree sha for the commit
222
:param parent_lookup: Function for looking up the GIT sha equiv of a
210
:param parent_lookup: Function for looking up the GIT sha equiv of a bzr revision
224
211
:return dulwich.objects.Commit represent the revision:
226
213
from dulwich.objects import Commit
227
214
commit = Commit()
228
215
commit.tree = tree_sha
229
216
for p in rev.parent_ids:
231
git_p = parent_lookup(p)
217
git_p = parent_lookup(p)
234
218
if git_p is not None:
235
219
assert len(git_p) == 40, "unexpected length for %r" % git_p
236
220
commit.parents.append(git_p)
238
encoding = rev.properties['git-explicit-encoding']
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(
244
commit.author = fix_person_identifier(
245
rev.get_apparent_authors()[0].encode(encoding))
221
commit.committer = fix_person_identifier(rev.committer.encode("utf-8"))
222
commit.author = fix_person_identifier(rev.get_apparent_authors()[0].encode("utf-8"))
246
223
commit.commit_time = long(rev.timestamp)
247
224
if 'author-timestamp' in rev.properties:
248
225
commit.author_time = long(rev.properties['author-timestamp'])
253
230
commit.author_timezone = int(rev.properties['author-timezone'])
255
232
commit.author_timezone = commit.commit_timezone
256
commit.message = self._encode_commit_message(rev, rev.message,
233
commit.message = self._encode_commit_message(rev, rev.message)
260
236
def import_commit(self, commit):
265
241
if commit is None:
266
242
raise AssertionError("Commit object can't be None")
267
rev = ForeignRevision(commit.id, self,
268
self.revision_id_foreign_to_bzr(commit.id))
243
rev = ForeignRevision(commit.id, self, self.revision_id_foreign_to_bzr(commit.id))
269
244
rev.parent_ids = tuple([self.revision_id_foreign_to_bzr(p) for p in commit.parents])
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,
276
if commit.encoding is not None:
277
rev.properties['git-explicit-encoding'] = commit.encoding
278
decode_using_encoding(rev, commit, commit.encoding)
280
for encoding in ('utf-8', 'latin1'):
282
decode_using_encoding(rev, commit, encoding)
283
except UnicodeDecodeError:
286
if encoding != 'utf-8':
287
rev.properties['git-implicit-encoding'] = encoding
245
rev.committer = str(commit.committer).decode("utf-8", "replace")
246
if commit.committer != commit.author:
247
rev.properties['author'] = str(commit.author).decode("utf-8", "replace")
289
249
if commit.commit_time != commit.author_time:
290
250
rev.properties['author-timestamp'] = str(commit.author_time)
291
251
if commit.commit_timezone != commit.author_timezone:
292
rev.properties['author-timezone'] = "%d" % commit.author_timezone
252
rev.properties['author-timezone'] = "%d" % (commit.author_timezone, )
293
253
rev.timestamp = commit.commit_time
294
254
rev.timezone = commit.commit_timezone
255
rev.message = self._decode_commit_message(rev, commit.message)
307
268
revid_prefix = 'git-experimental'
308
269
experimental = True
310
def _decode_commit_message(self, rev, message, encoding):
271
def _decode_commit_message(self, rev, message):
311
272
message = self._extract_hg_metadata(rev, message)
312
273
message = self._extract_git_svn_metadata(rev, message)
313
return message.decode(encoding)
274
return message.decode("utf-8", "replace")
315
def _encode_commit_message(self, rev, message, encoding):
316
ret = message.encode(encoding)
276
def _encode_commit_message(self, rev, message):
277
ret = message.encode("utf-8")
317
278
ret += self._generate_hg_message_tail(rev)
318
ret += self._generate_git_svn_metadata(rev, encoding)
279
ret += self._generate_git_svn_metadata(rev)
321
282
def import_commit(self, commit):
328
289
"""Registry with available git mappings."""
330
291
def revision_id_bzr_to_foreign(self, bzr_revid):
331
if bzr_revid == NULL_REVISION:
332
return "0" * 20, None
333
292
if not bzr_revid.startswith("git-"):
334
293
raise errors.InvalidRevisionId(bzr_revid, None)
335
294
(mapping_version, git_sha) = bzr_revid.split(":", 1)
342
301
mapping_registry = GitMappingRegistry()
343
302
mapping_registry.register_lazy('git-v1', "bzrlib.plugins.git.mapping",
345
mapping_registry.register_lazy('git-experimental',
346
"bzrlib.plugins.git.mapping", "BzrGitMappingExperimental")
304
mapping_registry.register_lazy('git-experimental', "bzrlib.plugins.git.mapping",
305
"BzrGitMappingExperimental")
347
306
mapping_registry.set_default('git-v1')
377
336
default_mapping = mapping_registry.get_default()()
339
def text_to_blob(texts, entry):
340
from dulwich.objects import Blob
341
text = texts.get_record_stream([(entry.file_id, entry.revision)], 'unordered', True).next().get_bytes_as('fulltext')
380
347
def symlink_to_blob(entry):
381
348
from dulwich.objects import 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
350
blob._text = entry.symlink_target
441
402
def directory_to_tree(entry, lookup_ie_sha1, unusual_modes):
442
403
from dulwich.objects import Tree
444
for name, value in entry.children.iteritems():
405
for name in sorted(entry.children.keys()):
445
406
ie = entry.children[name]
447
408
mode = unusual_modes[ie.file_id]
453
414
if entry.parent_id is not None and len(tree) == 0:
454
415
# Only the root can be an empty tree
459
421
def extract_unusual_modes(rev):
461
foreign_revid, mapping = mapping_registry.parse_revision_id(
423
foreign_revid, mapping = mapping_registry.parse_revision_id(rev.revision_id)
463
424
except errors.InvalidRevisionId:
466
427
return mapping.export_unusual_file_modes(rev)
469
def inventory_to_tree_and_blobs(inventory, texts, mapping, unusual_modes,
430
def inventory_to_tree_and_blobs(inventory, texts, mapping, unusual_modes, cur=None):
471
431
"""Convert a Bazaar tree to a Git tree.
473
433
:return: Yields tuples with object sha1, object and path
484
444
for path, entry in inventory.iter_entries():
485
445
while stack and not path.startswith(osutils.pathjoin(cur, "")):
486
446
# We've hit a file that's not a child of the previous path
488
449
yield sha, tree, cur.encode("utf-8")
489
450
mode = unusual_modes.get(cur.encode("utf-8"), stat.S_IFDIR)
499
460
if entry.kind == "file":
500
from dulwich.objects import Blob
501
stream = texts.get_record_stream(
502
[(entry.file_id, entry.revision)], 'unordered', True)
504
blob.chunked = stream.next().get_bytes_as('chunks')
461
blob = text_to_blob(texts, entry)
505
462
elif entry.kind == "symlink":
506
463
blob = symlink_to_blob(entry)