14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib import osutils
18
from bzrlib.errors import InvalidRevisionId
19
from bzrlib.inventory import Inventory
20
from bzrlib.repository import InterRepository
21
from bzrlib.trace import info
23
from bzrlib.plugins.git import git
24
from bzrlib.plugins.git.repository import LocalGitRepository, GitRepository, GitFormat
25
from bzrlib.plugins.git.remote import RemoteGitRepository
27
from dulwich.objects import Commit
29
from cStringIO import StringIO
32
class BzrFetchGraphWalker(object):
34
def __init__(self, repository, mapping):
35
self.repository = repository
36
self.mapping = mapping
38
self.heads = set(repository.all_revision_ids())
42
revid = self.mapping.revision_id_foreign_to_bzr(sha)
45
def remove(self, revid):
48
self.heads.remove(revid)
49
if revid in self.parents:
50
for p in self.parents[revid]:
55
ret = self.heads.pop()
56
ps = self.repository.get_parent_map([ret])[ret]
57
self.parents[ret] = ps
58
self.heads.update([p for p in ps if not p in self.done])
61
return self.mapping.revision_id_bzr_to_foreign(ret)
62
except InvalidRevisionId:
67
def import_git_blob(repo, mapping, path, blob):
17
from dulwich.objects import (
24
from dulwich.object_store import (
27
from itertools import (
40
from bzrlib.errors import (
44
from bzrlib.inventory import (
51
from bzrlib.repository import (
54
from bzrlib.revision import (
58
from bzrlib.revisiontree import InventoryRevisionTree
59
except ImportError: # bzr < 2.4
60
from bzrlib.revisiontree import RevisionTree as InventoryRevisionTree
61
from bzrlib.testament import (
64
from bzrlib.tsort import (
67
from bzrlib.versionedfile import (
68
ChunkedContentFactory,
71
from bzrlib.plugins.git.mapping import (
77
from bzrlib.plugins.git.object_store import (
82
from bzrlib.plugins.git.refs import extract_tags
83
from bzrlib.plugins.git.remote import (
86
from bzrlib.plugins.git.repository import (
93
def import_git_blob(texts, mapping, path, name, (base_hexsha, hexsha),
94
base_inv, parent_id, revision_id,
95
parent_invs, lookup_object, (base_mode, mode), store_updater,
68
97
"""Import a git blob object into a bzr repository.
70
:param repo: bzr repository
99
:param texts: VersionedFiles to add to
71
100
:param path: Path in the tree
72
101
:param blob: A git blob
74
file_id = mapping.generate_file_id(path)
75
repo.texts.add_lines((file_id, blob.id),
77
osutils.split_lines(blob.data))
78
inv.add_path(path, "file", file_id)
81
def import_git_tree(repo, mapping, path, tree, inv, lookup_object):
102
:return: Inventory delta for this file
104
if mapping.is_control_file(path):
106
if base_hexsha == hexsha and base_mode == mode:
107
# If nothing has changed since the base revision, we're done
109
file_id = lookup_file_id(path)
110
if stat.S_ISLNK(mode):
114
ie = cls(file_id, name.decode("utf-8"), parent_id)
115
if ie.kind == "file":
116
ie.executable = mode_is_executable(mode)
117
if base_hexsha == hexsha and mode_kind(base_mode) == mode_kind(mode):
118
base_ie = base_inv[base_inv.path2id(path)]
119
ie.text_size = base_ie.text_size
120
ie.text_sha1 = base_ie.text_sha1
121
if ie.kind == "symlink":
122
ie.symlink_target = base_ie.symlink_target
123
if ie.executable == base_ie.executable:
124
ie.revision = base_ie.revision
126
blob = lookup_object(hexsha)
128
blob = lookup_object(hexsha)
129
if ie.kind == "symlink":
131
ie.symlink_target = blob.data
133
ie.text_size = sum(imap(len, blob.chunked))
134
ie.text_sha1 = osutils.sha_strings(blob.chunked)
135
# Check what revision we should store
137
for pinv in parent_invs:
142
if (pie.text_sha1 == ie.text_sha1 and
143
pie.executable == ie.executable and
144
pie.symlink_target == ie.symlink_target):
145
# found a revision in one of the parents to use
146
ie.revision = pie.revision
148
parent_key = (file_id, pie.revision)
149
if not parent_key in parent_keys:
150
parent_keys.append(parent_key)
151
if ie.revision is None:
152
# Need to store a new revision
153
ie.revision = revision_id
154
assert ie.revision is not None
155
if ie.kind == 'symlink':
158
chunks = blob.chunked
159
texts.insert_record_stream([
160
ChunkedContentFactory((file_id, ie.revision),
161
tuple(parent_keys), ie.text_sha1, chunks)])
163
if base_hexsha is not None:
164
old_path = path.decode("utf-8") # Renames are not supported yet
165
if stat.S_ISDIR(base_mode):
166
invdelta.extend(remove_disappeared_children(base_inv, old_path,
167
lookup_object(base_hexsha), [], lookup_object))
170
new_path = path.decode("utf-8")
171
invdelta.append((old_path, new_path, file_id, ie))
172
if base_hexsha != hexsha:
173
store_updater.add_object(blob, ie, path)
177
class SubmodulesRequireSubtrees(BzrError):
178
_fmt = """The repository you are fetching from contains submodules. To continue, upgrade your Bazaar repository to a format that supports nested trees, such as 'development-subtree'."""
182
def import_git_submodule(texts, mapping, path, name, (base_hexsha, hexsha),
183
base_inv, parent_id, revision_id, parent_invs, lookup_object,
184
(base_mode, mode), store_updater, lookup_file_id):
185
if base_hexsha == hexsha and base_mode == mode:
187
file_id = lookup_file_id(path)
188
ie = TreeReference(file_id, name.decode("utf-8"), parent_id)
189
ie.revision = revision_id
190
if base_hexsha is None:
194
ie.reference_revision = mapping.revision_id_foreign_to_bzr(hexsha)
195
texts.insert_record_stream([
196
ChunkedContentFactory((file_id, ie.revision), (), None, [])])
197
invdelta = [(oldpath, path, file_id, ie)]
201
def remove_disappeared_children(base_inv, path, base_tree, existing_children,
203
"""Generate an inventory delta for removed children.
205
:param base_inv: Base inventory against which to generate the
207
:param path: Path to process (unicode)
208
:param base_tree: Git Tree base object
209
:param existing_children: Children that still exist
210
:param lookup_object: Lookup a git object by its SHA1
211
:return: Inventory delta, as list
213
assert type(path) is unicode
215
for name, mode, hexsha in base_tree.iteritems():
216
if name in existing_children:
218
c_path = posixpath.join(path, name.decode("utf-8"))
219
file_id = base_inv.path2id(c_path)
220
assert file_id is not None
221
ret.append((c_path, None, file_id, None))
222
if stat.S_ISDIR(mode):
223
ret.extend(remove_disappeared_children(
224
base_inv, c_path, lookup_object(hexsha), [], lookup_object))
228
def import_git_tree(texts, mapping, path, name, (base_hexsha, hexsha),
229
base_inv, parent_id, revision_id, parent_invs,
230
lookup_object, (base_mode, mode), store_updater,
231
lookup_file_id, allow_submodules=False):
82
232
"""Import a git tree object into a bzr repository.
84
:param repo: A Bzr repository object
85
:param path: Path in the tree
234
:param texts: VersionedFiles object to add to
235
:param path: Path in the tree (str)
236
:param name: Name of the tree (str)
86
237
:param tree: A git tree object
87
:param inv: Inventory object
238
:param base_inv: Base inventory against which to return inventory delta
239
:return: Inventory delta for this subtree
89
file_id = mapping.generate_file_id(path)
90
repo.texts.add_lines((file_id, tree.id),
93
inv.add_path(path, "directory", file_id)
94
for mode, name, hexsha in tree.entries():
95
entry_kind = (mode & 0700000) / 0100000
96
basename = name.decode("utf-8")
100
child_path = urlutils.join(path, name)
102
import_git_tree(repo, mapping, child_path, lookup_object, inv)
103
elif entry_kind == 1:
104
import_git_blob(repo, mapping, child_path, lookup_object, inv)
106
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
109
def import_git_objects(repo, mapping, object_iter):
241
assert type(path) is str
242
assert type(name) is str
243
if base_hexsha == hexsha and base_mode == mode:
244
# If nothing has changed since the base revision, we're done
247
file_id = lookup_file_id(path)
248
# We just have to hope this is indeed utf-8:
249
ie = InventoryDirectory(file_id, name.decode("utf-8"), parent_id)
250
tree = lookup_object(hexsha)
251
if base_hexsha is None:
253
old_path = None # Newly appeared here
255
base_tree = lookup_object(base_hexsha)
256
old_path = path.decode("utf-8") # Renames aren't supported yet
257
new_path = path.decode("utf-8")
258
if base_tree is None or type(base_tree) is not Tree:
259
ie.revision = revision_id
260
invdelta.append((old_path, new_path, ie.file_id, ie))
261
texts.insert_record_stream([
262
ChunkedContentFactory((ie.file_id, ie.revision), (), None, [])])
263
# Remember for next time
264
existing_children = set()
266
for name, child_mode, child_hexsha in tree.iteritems():
267
existing_children.add(name)
268
child_path = posixpath.join(path, name)
269
if type(base_tree) is Tree:
271
child_base_mode, child_base_hexsha = base_tree[name]
273
child_base_hexsha = None
276
child_base_hexsha = None
278
if stat.S_ISDIR(child_mode):
279
subinvdelta, grandchildmodes = import_git_tree(texts, mapping,
280
child_path, name, (child_base_hexsha, child_hexsha), base_inv,
281
file_id, revision_id, parent_invs, lookup_object,
282
(child_base_mode, child_mode), store_updater, lookup_file_id,
283
allow_submodules=allow_submodules)
284
elif S_ISGITLINK(child_mode): # submodule
285
if not allow_submodules:
286
raise SubmodulesRequireSubtrees()
287
subinvdelta, grandchildmodes = import_git_submodule(texts, mapping,
288
child_path, name, (child_base_hexsha, child_hexsha), base_inv,
289
file_id, revision_id, parent_invs, lookup_object,
290
(child_base_mode, child_mode), store_updater, lookup_file_id)
292
subinvdelta = import_git_blob(texts, mapping, child_path, name,
293
(child_base_hexsha, child_hexsha), base_inv, file_id,
294
revision_id, parent_invs, lookup_object,
295
(child_base_mode, child_mode), store_updater, lookup_file_id)
297
child_modes.update(grandchildmodes)
298
invdelta.extend(subinvdelta)
299
if child_mode not in (stat.S_IFDIR, DEFAULT_FILE_MODE,
300
stat.S_IFLNK, DEFAULT_FILE_MODE|0111):
301
child_modes[child_path] = child_mode
302
# Remove any children that have disappeared
303
if base_tree is not None and type(base_tree) is Tree:
304
invdelta.extend(remove_disappeared_children(base_inv, old_path,
305
base_tree, existing_children, lookup_object))
306
store_updater.add_object(tree, ie, path)
307
return invdelta, child_modes
310
def verify_commit_reconstruction(target_git_object_retriever, lookup_object,
311
o, rev, ret_tree, parent_trees, mapping, unusual_modes, verifiers):
312
new_unusual_modes = mapping.export_unusual_file_modes(rev)
313
if new_unusual_modes != unusual_modes:
314
raise AssertionError("unusual modes don't match: %r != %r" % (
315
unusual_modes, new_unusual_modes))
316
# Verify that we can reconstruct the commit properly
317
rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True,
320
raise AssertionError("Reconstructed commit differs: %r != %r" % (
324
for path, obj, ie in _tree_to_objects(ret_tree, parent_trees,
325
target_git_object_retriever._cache.idmap, unusual_modes, mapping.BZR_DUMMY_FILE):
326
old_obj_id = tree_lookup_path(lookup_object, o.tree, path)[1]
328
if obj.id != old_obj_id:
329
diff.append((path, lookup_object(old_obj_id), obj))
330
for (path, old_obj, new_obj) in diff:
331
while (old_obj.type_name == "tree" and
332
new_obj.type_name == "tree" and
333
sorted(old_obj) == sorted(new_obj)):
335
if old_obj[name][0] != new_obj[name][0]:
336
raise AssertionError("Modes for %s differ: %o != %o" %
337
(path, old_obj[name][0], new_obj[name][0]))
338
if old_obj[name][1] != new_obj[name][1]:
339
# Found a differing child, delve deeper
340
path = posixpath.join(path, name)
341
old_obj = lookup_object(old_obj[name][1])
342
new_obj = new_objs[path]
344
raise AssertionError("objects differ for %s: %r != %r" % (path,
348
def import_git_commit(repo, mapping, head, lookup_object,
349
target_git_object_retriever, trees_cache):
350
o = lookup_object(head)
351
# Note that this uses mapping.revision_id_foreign_to_bzr. If the parents
352
# were bzr roundtripped revisions they would be specified in the
354
rev, roundtrip_revid, verifiers = mapping.import_commit(
355
o, mapping.revision_id_foreign_to_bzr)
356
# We have to do this here, since we have to walk the tree and
357
# we need to make sure to import the blobs / trees with the right
358
# path; this may involve adding them more than once.
359
parent_trees = trees_cache.revision_trees(rev.parent_ids)
360
if parent_trees == []:
361
base_inv = Inventory(root_id=None)
365
base_inv = parent_trees[0].inventory
366
base_tree = lookup_object(o.parents[0]).tree
367
base_mode = stat.S_IFDIR
368
store_updater = target_git_object_retriever._get_updater(rev)
369
fileid_map = mapping.get_fileid_map(lookup_object, o.tree)
370
inv_delta, unusual_modes = import_git_tree(repo.texts,
371
mapping, "", "", (base_tree, o.tree), base_inv,
372
None, rev.revision_id, [p.inventory for p in parent_trees],
373
lookup_object, (base_mode, stat.S_IFDIR), store_updater,
374
fileid_map.lookup_file_id,
375
allow_submodules=getattr(repo._format, "supports_tree_reference", False))
376
if unusual_modes != {}:
377
for path, mode in unusual_modes.iteritems():
378
warn_unusual_mode(rev.foreign_revid, path, mode)
379
mapping.import_unusual_file_modes(rev, unusual_modes)
381
basis_id = rev.parent_ids[0]
383
basis_id = NULL_REVISION
385
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
386
inv_delta, rev.revision_id, rev.parent_ids, base_inv)
387
ret_tree = InventoryRevisionTree(repo, inv, rev.revision_id)
388
if roundtrip_revid is not None:
389
original_revid = rev.revision_id
390
rev.revision_id = roundtrip_revid
392
if getattr(StrictTestament3, "from_revision_tree", None):
393
testament = StrictTestament3(rev, ret_tree)
395
testament = StrictTestament3(rev, inv)
396
calculated_verifiers = { "testament3-sha1": testament.as_sha1() }
397
if calculated_verifiers != verifiers:
398
trace.mutter("Testament SHA1 %r for %r did not match %r.",
399
calculated_verifiers["testament3-sha1"],
400
rev.revision_id, verifiers["testament3-sha1"])
401
rev.revision_id = original_revid
403
calculated_verifiers = {}
404
store_updater.add_object(o, calculated_verifiers, None)
405
store_updater.finish()
406
trees_cache.add(ret_tree)
407
repo.add_revision(rev.revision_id, rev)
408
if "verify" in debug.debug_flags:
409
verify_commit_reconstruction(target_git_object_retriever,
410
lookup_object, o, rev, ret_tree, parent_trees, mapping,
411
unusual_modes, verifiers)
414
def import_git_objects(repo, mapping, object_iter,
415
target_git_object_retriever, heads, pb=None, limit=None):
110
416
"""Import a set of git objects into a bzr repository.
112
:param repo: Bazaar repository
418
:param repo: Target Bazaar repository
113
419
:param mapping: Mapping to use
114
420
:param object_iter: Iterator over Git objects.
421
:return: Tuple with pack hints and last imported revision id
116
# TODO: a more (memory-)efficient implementation of this
118
for o in object_iter:
423
def lookup_object(sha):
425
return object_iter[sha]
427
return target_git_object_retriever[sha]
430
heads = list(set(heads))
431
trees_cache = LRUTreeCache(repo)
121
432
# Find and convert commit objects
122
for o in objects.iterkeys():
435
pb.update("finding revisions to fetch", len(graph), None)
437
assert isinstance(head, str)
439
o = lookup_object(head)
123
442
if isinstance(o, Commit):
124
rev = mapping.import_commit(o)
125
root_trees[rev] = objects[o.tree_sha]
443
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
444
mapping.revision_id_foreign_to_bzr)
445
if (repo.has_revision(rev.revision_id) or
446
(roundtrip_revid and repo.has_revision(roundtrip_revid))):
448
graph.append((o.id, o.parents))
449
heads.extend([p for p in o.parents if p not in checked])
450
elif isinstance(o, Tag):
451
if o.object[1] not in checked:
452
heads.append(o.object[1])
454
trace.warning("Unable to import head object %r" % o)
457
# Order the revisions
126
458
# Create the inventory objects
127
for rev, root_tree in root_trees.iteritems():
128
# We have to do this here, since we have to walk the tree and
129
# we need to make sure to import the blobs / trees with the riht
130
# path; this may involve adding them more than once.
132
def lookup_object(sha):
135
return reconstruct_git_object(repo, mapping, sha)
136
import_git_tree(repo, mapping, "", tree, inv, lookup_object)
137
repo.add_revision(rev.revision_id, rev, inv)
140
def reconstruct_git_commit(repo, rev):
141
raise NotImplementedError(self.reconstruct_git_commit)
144
def reconstruct_git_object(repo, mapping, sha):
146
revid = mapping.revision_id_foreign_to_bzr(sha)
148
rev = repo.get_revision(revid)
149
except NoSuchRevision:
152
return reconstruct_git_commit(rev)
156
raise KeyError("No such object %s" % sha)
460
revision_ids = topo_sort(graph)
462
if limit is not None:
463
revision_ids = revision_ids[:limit]
465
for offset in range(0, len(revision_ids), batch_size):
466
target_git_object_retriever.start_write_group()
468
repo.start_write_group()
470
for i, head in enumerate(
471
revision_ids[offset:offset+batch_size]):
473
pb.update("fetching revisions", offset+i,
475
import_git_commit(repo, mapping, head, lookup_object,
476
target_git_object_retriever, trees_cache)
479
repo.abort_write_group()
482
hint = repo.commit_write_group()
484
pack_hints.extend(hint)
486
target_git_object_retriever.abort_write_group()
489
target_git_object_retriever.commit_write_group()
490
return pack_hints, last_imported
159
493
class InterGitRepository(InterRepository):
161
_matching_repo_format = GitFormat()
495
_matching_repo_format = GitRepositoryFormat()
497
def _target_has_shas(self, shas):
498
raise NotImplementedError(self._target_has_shas)
500
def get_determine_wants_heads(self, wants, include_tags=False):
502
def determine_wants(refs):
503
potential = set(wants)
506
[v[1] or v[0] for v in extract_tags(refs).itervalues()])
507
return list(potential - self._target_has_shas(potential))
508
return determine_wants
510
def determine_wants_all(self, refs):
511
potential = set([sha for (ref, sha) in refs.iteritems() if not
512
ref.endswith("^{}")])
513
return list(potential - self._target_has_shas(potential))
164
516
def _get_repo_format_to_test():
168
520
"""See InterRepository.copy_content."""
169
521
self.fetch(revision_id, pb, find_ghosts=False)
171
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
174
mapping = self.source.get_mapping()
177
pb.note("git: %s" % text)
179
info("git: %s" % text)
180
def determine_wants(heads):
181
if revision_id is None:
184
ret = [mapping.revision_id_bzr_to_foreign(revision_id)]
185
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
186
graph_walker = BzrFetchGraphWalker(self.target, mapping)
187
self.target.lock_write()
189
import_git_objects(self.target, mapping,
190
self.source.fetch_objects(determine_wants, graph_walker,
196
def is_compatible(source, target):
197
"""Be compatible with GitRepository."""
198
# FIXME: Also check target uses VersionedFile
199
return (isinstance(source, LocalGitRepository) and
200
target.supports_rich_root())
524
class InterGitNonGitRepository(InterGitRepository):
525
"""Base InterRepository that copies revisions from a Git into a non-Git
528
def _target_has_shas(self, shas):
529
revids = [self.source.lookup_foreign_revision_id(sha) for sha in shas]
530
return self.target.has_revisions(revids)
532
def get_determine_wants_revids(self, revids, include_tags=False):
534
for revid in set(revids):
535
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
537
return self.get_determine_wants_heads(wants, include_tags=include_tags)
539
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
540
"""Fetch objects from a remote server.
542
:param determine_wants: determine_wants callback
543
:param mapping: BzrGitMapping to use
544
:param pb: Optional progress bar
545
:param limit: Maximum number of commits to import.
546
:return: Tuple with pack hint, last imported revision id and remote refs
548
raise NotImplementedError(self.fetch_objects)
550
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
551
mapping=None, fetch_spec=None):
553
mapping = self.source.get_mapping()
554
if revision_id is not None:
555
interesting_heads = [revision_id]
556
elif fetch_spec is not None:
557
recipe = fetch_spec.get_recipe()
558
if recipe[0] in ("search", "proxy-search"):
559
interesting_heads = recipe[1]
561
raise AssertionError("Unsupported search result type %s" %
564
interesting_heads = None
566
if interesting_heads is not None:
567
determine_wants = self.get_determine_wants_revids(interesting_heads,
570
determine_wants = self.determine_wants_all
572
(pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
574
if pack_hint is not None and self.target._format.pack_compresses:
575
self.target.pack(hint=pack_hint)
576
assert isinstance(remote_refs, dict)
580
_GIT_PROGRESS_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
581
def report_git_progress(pb, text):
582
text = text.rstrip("\r\n")
583
g = _GIT_PROGRESS_RE.match(text)
585
(text, pct, current, total) = g.groups()
586
pb.update(text, int(current), int(total))
588
pb.update(text, 0, 0)
591
class DetermineWantsRecorder(object):
593
def __init__(self, actual):
596
self.remote_refs = {}
598
def __call__(self, refs):
599
self.remote_refs = refs
600
self.wants = self.actual(refs)
604
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
605
"""InterRepository that copies revisions from a remote Git into a non-Git
608
def get_target_heads(self):
609
# FIXME: This should be more efficient
610
all_revs = self.target.all_revision_ids()
611
parent_map = self.target.get_parent_map(all_revs)
613
map(all_parents.update, parent_map.itervalues())
614
return set(all_revs) - all_parents
616
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
617
"""See `InterGitNonGitRepository`."""
619
report_git_progress(pb, text)
620
store = BazaarObjectStore(self.target, mapping)
623
heads = self.get_target_heads()
624
graph_walker = store.get_graph_walker(
625
[store._lookup_revision_sha1(head) for head in heads])
626
wants_recorder = DetermineWantsRecorder(determine_wants)
630
create_pb = pb = ui.ui_factory.nested_progress_bar()
632
objects_iter = self.source.fetch_objects(
633
wants_recorder, graph_walker, store.get_raw,
635
trace.mutter("Importing %d new revisions",
636
len(wants_recorder.wants))
637
(pack_hint, last_rev) = import_git_objects(self.target,
638
mapping, objects_iter, store, wants_recorder.wants, pb,
640
return (pack_hint, last_rev, wants_recorder.remote_refs)
648
def is_compatible(source, target):
649
"""Be compatible with GitRepository."""
650
if not isinstance(source, RemoteGitRepository):
652
if not target.supports_rich_root():
654
if isinstance(target, GitRepository):
656
if not getattr(target._format, "supports_full_versioned_files", True):
661
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
662
"""InterRepository that copies revisions from a local Git into a non-Git
665
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
666
"""See `InterGitNonGitRepository`."""
667
remote_refs = self.source._git.get_refs()
668
wants = determine_wants(remote_refs)
671
create_pb = pb = ui.ui_factory.nested_progress_bar()
672
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
674
target_git_object_retriever.lock_write()
676
(pack_hint, last_rev) = import_git_objects(self.target,
677
mapping, self.source._git.object_store,
678
target_git_object_retriever, wants, pb, limit)
679
return (pack_hint, last_rev, remote_refs)
681
target_git_object_retriever.unlock()
687
def is_compatible(source, target):
688
"""Be compatible with GitRepository."""
689
if not isinstance(source, LocalGitRepository):
691
if not target.supports_rich_root():
693
if isinstance(target, GitRepository):
695
if not getattr(target._format, "supports_full_versioned_files", True):
700
class InterGitGitRepository(InterGitRepository):
701
"""InterRepository that copies between Git repositories."""
703
def fetch_objects(self, determine_wants, mapping, pb=None):
705
trace.note("git: %s", text)
706
graphwalker = self.target._git.get_graph_walker()
707
if (isinstance(self.source, LocalGitRepository) and
708
isinstance(self.target, LocalGitRepository)):
709
refs = self.source._git.fetch(self.target._git, determine_wants,
711
return (None, None, refs)
712
elif (isinstance(self.source, LocalGitRepository) and
713
isinstance(self.target, RemoteGitRepository)):
714
raise NotImplementedError
715
elif (isinstance(self.source, RemoteGitRepository) and
716
isinstance(self.target, LocalGitRepository)):
717
f, commit = self.target._git.object_store.add_thin_pack()
719
refs = self.source.bzrdir.root_transport.fetch_pack(
720
determine_wants, graphwalker, f.write, progress)
722
return (None, None, refs)
729
def _target_has_shas(self, shas):
730
return set([sha for sha in shas if self.target._git.object_store])
732
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
733
mapping=None, fetch_spec=None, branches=None):
735
mapping = self.source.get_mapping()
737
if revision_id is not None:
738
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
739
elif fetch_spec is not None:
740
recipe = fetch_spec.get_recipe()
741
if recipe[0] in ("search", "proxy-search"):
744
raise AssertionError("Unsupported search result type %s" % recipe[0])
745
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in heads]
746
if branches is not None:
747
determine_wants = lambda x: [x[y] for y in branches if not x[y] in r.object_store and x[y] != ZERO_SHA]
748
elif fetch_spec is None and revision_id is None:
749
determine_wants = self.determine_wants_all
751
determine_wants = lambda x: [y for y in args if not y in r.object_store and y != ZERO_SHA]
752
wants_recorder = DetermineWantsRecorder(determine_wants)
753
self.fetch_objects(wants_recorder, mapping)
754
return wants_recorder.remote_refs
757
def is_compatible(source, target):
758
"""Be compatible with GitRepository."""
759
return (isinstance(source, GitRepository) and
760
isinstance(target, GitRepository))
762
def get_determine_wants_revids(self, revids, include_tags=False):
764
for revid in set(revids):
765
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
767
return self.get_determine_wants_heads(wants, include_tags=include_tags)