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. "
179
"To continue, upgrade your Bazaar repository to a format that "
180
"supports nested trees, such as 'development-subtree'.")
184
def import_git_submodule(texts, mapping, path, name, (base_hexsha, hexsha),
185
base_inv, parent_id, revision_id, parent_invs, lookup_object,
186
(base_mode, mode), store_updater, lookup_file_id):
187
"""Import a git submodule."""
188
if base_hexsha == hexsha and base_mode == mode:
190
file_id = lookup_file_id(path)
191
ie = TreeReference(file_id, name.decode("utf-8"), parent_id)
192
ie.revision = revision_id
193
if base_hexsha is None:
197
ie.reference_revision = mapping.revision_id_foreign_to_bzr(hexsha)
198
texts.insert_record_stream([
199
ChunkedContentFactory((file_id, ie.revision), (), None, [])])
200
invdelta = [(oldpath, path, file_id, ie)]
204
def remove_disappeared_children(base_inv, path, base_tree, existing_children,
206
"""Generate an inventory delta for removed children.
208
:param base_inv: Base inventory against which to generate the
210
:param path: Path to process (unicode)
211
:param base_tree: Git Tree base object
212
:param existing_children: Children that still exist
213
:param lookup_object: Lookup a git object by its SHA1
214
:return: Inventory delta, as list
216
assert type(path) is unicode
218
for name, mode, hexsha in base_tree.iteritems():
219
if name in existing_children:
221
c_path = posixpath.join(path, name.decode("utf-8"))
222
file_id = base_inv.path2id(c_path)
223
assert file_id is not None
224
ret.append((c_path, None, file_id, None))
225
if stat.S_ISDIR(mode):
226
ret.extend(remove_disappeared_children(
227
base_inv, c_path, lookup_object(hexsha), [], lookup_object))
231
def import_git_tree(texts, mapping, path, name, (base_hexsha, hexsha),
232
base_inv, parent_id, revision_id, parent_invs,
233
lookup_object, (base_mode, mode), store_updater,
234
lookup_file_id, allow_submodules=False):
82
235
"""Import a git tree object into a bzr repository.
84
:param repo: A Bzr repository object
85
:param path: Path in the tree
237
:param texts: VersionedFiles object to add to
238
:param path: Path in the tree (str)
239
:param name: Name of the tree (str)
86
240
:param tree: A git tree object
87
:param inv: Inventory object
241
:param base_inv: Base inventory against which to return inventory delta
242
: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):
244
assert type(path) is str
245
assert type(name) is str
246
if base_hexsha == hexsha and base_mode == mode:
247
# If nothing has changed since the base revision, we're done
250
file_id = lookup_file_id(path)
251
# We just have to hope this is indeed utf-8:
252
ie = InventoryDirectory(file_id, name.decode("utf-8"), parent_id)
253
tree = lookup_object(hexsha)
254
if base_hexsha is None:
256
old_path = None # Newly appeared here
258
base_tree = lookup_object(base_hexsha)
259
old_path = path.decode("utf-8") # Renames aren't supported yet
260
new_path = path.decode("utf-8")
261
if base_tree is None or type(base_tree) is not Tree:
262
ie.revision = revision_id
263
invdelta.append((old_path, new_path, ie.file_id, ie))
264
texts.insert_record_stream([
265
ChunkedContentFactory((ie.file_id, ie.revision), (), None, [])])
266
# Remember for next time
267
existing_children = set()
269
for name, child_mode, child_hexsha in tree.iteritems():
270
existing_children.add(name)
271
child_path = posixpath.join(path, name)
272
if type(base_tree) is Tree:
274
child_base_mode, child_base_hexsha = base_tree[name]
276
child_base_hexsha = None
279
child_base_hexsha = None
281
if stat.S_ISDIR(child_mode):
282
subinvdelta, grandchildmodes = import_git_tree(texts, mapping,
283
child_path, name, (child_base_hexsha, child_hexsha), base_inv,
284
file_id, revision_id, parent_invs, lookup_object,
285
(child_base_mode, child_mode), store_updater, lookup_file_id,
286
allow_submodules=allow_submodules)
287
elif S_ISGITLINK(child_mode): # submodule
288
if not allow_submodules:
289
raise SubmodulesRequireSubtrees()
290
subinvdelta, grandchildmodes = import_git_submodule(texts, mapping,
291
child_path, name, (child_base_hexsha, child_hexsha), base_inv,
292
file_id, revision_id, parent_invs, lookup_object,
293
(child_base_mode, child_mode), store_updater, lookup_file_id)
295
if not mapping.is_special_file(name):
296
subinvdelta = import_git_blob(texts, mapping, child_path, name,
297
(child_base_hexsha, child_hexsha), base_inv, file_id,
298
revision_id, parent_invs, lookup_object,
299
(child_base_mode, child_mode), store_updater, lookup_file_id)
303
child_modes.update(grandchildmodes)
304
invdelta.extend(subinvdelta)
305
if child_mode not in (stat.S_IFDIR, DEFAULT_FILE_MODE,
306
stat.S_IFLNK, DEFAULT_FILE_MODE|0111):
307
child_modes[child_path] = child_mode
308
# Remove any children that have disappeared
309
if base_tree is not None and type(base_tree) is Tree:
310
invdelta.extend(remove_disappeared_children(base_inv, old_path,
311
base_tree, existing_children, lookup_object))
312
store_updater.add_object(tree, ie, path)
313
return invdelta, child_modes
316
def verify_commit_reconstruction(target_git_object_retriever, lookup_object,
317
o, rev, ret_tree, parent_trees, mapping, unusual_modes, verifiers):
318
new_unusual_modes = mapping.export_unusual_file_modes(rev)
319
if new_unusual_modes != unusual_modes:
320
raise AssertionError("unusual modes don't match: %r != %r" % (
321
unusual_modes, new_unusual_modes))
322
# Verify that we can reconstruct the commit properly
323
rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True,
326
raise AssertionError("Reconstructed commit differs: %r != %r" % (
330
for path, obj, ie in _tree_to_objects(ret_tree, parent_trees,
331
target_git_object_retriever._cache.idmap, unusual_modes,
332
mapping.BZR_DUMMY_FILE):
333
old_obj_id = tree_lookup_path(lookup_object, o.tree, path)[1]
335
if obj.id != old_obj_id:
336
diff.append((path, lookup_object(old_obj_id), obj))
337
for (path, old_obj, new_obj) in diff:
338
while (old_obj.type_name == "tree" and
339
new_obj.type_name == "tree" and
340
sorted(old_obj) == sorted(new_obj)):
342
if old_obj[name][0] != new_obj[name][0]:
343
raise AssertionError("Modes for %s differ: %o != %o" %
344
(path, old_obj[name][0], new_obj[name][0]))
345
if old_obj[name][1] != new_obj[name][1]:
346
# Found a differing child, delve deeper
347
path = posixpath.join(path, name)
348
old_obj = lookup_object(old_obj[name][1])
349
new_obj = new_objs[path]
351
raise AssertionError("objects differ for %s: %r != %r" % (path,
355
def import_git_commit(repo, mapping, head, lookup_object,
356
target_git_object_retriever, trees_cache):
357
o = lookup_object(head)
358
# Note that this uses mapping.revision_id_foreign_to_bzr. If the parents
359
# were bzr roundtripped revisions they would be specified in the
361
rev, roundtrip_revid, verifiers = mapping.import_commit(
362
o, mapping.revision_id_foreign_to_bzr)
363
if roundtrip_revid is not None:
364
original_revid = rev.revision_id
365
rev.revision_id = roundtrip_revid
366
# We have to do this here, since we have to walk the tree and
367
# we need to make sure to import the blobs / trees with the right
368
# path; this may involve adding them more than once.
369
parent_trees = trees_cache.revision_trees(rev.parent_ids)
370
if parent_trees == []:
371
base_inv = Inventory(root_id=None)
375
base_inv = parent_trees[0].inventory
376
base_tree = lookup_object(o.parents[0]).tree
377
base_mode = stat.S_IFDIR
378
store_updater = target_git_object_retriever._get_updater(rev)
379
tree_supplement = mapping.get_fileid_map(lookup_object, o.tree)
380
inv_delta, unusual_modes = import_git_tree(repo.texts,
381
mapping, "", "", (base_tree, o.tree), base_inv,
382
None, rev.revision_id, [p.inventory for p in parent_trees],
383
lookup_object, (base_mode, stat.S_IFDIR), store_updater,
384
tree_supplement.lookup_file_id,
385
allow_submodules=getattr(repo._format, "supports_tree_reference",
387
if unusual_modes != {}:
388
for path, mode in unusual_modes.iteritems():
389
warn_unusual_mode(rev.foreign_revid, path, mode)
390
mapping.import_unusual_file_modes(rev, unusual_modes)
392
basis_id = rev.parent_ids[0]
394
basis_id = NULL_REVISION
396
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
397
inv_delta, rev.revision_id, rev.parent_ids, base_inv)
398
ret_tree = InventoryRevisionTree(repo, inv, rev.revision_id)
400
if verifiers and roundtrip_revid is not None:
401
if getattr(StrictTestament3, "from_revision_tree", None):
402
testament = StrictTestament3(rev, ret_tree)
404
testament = StrictTestament3(rev, inv)
405
calculated_verifiers = { "testament3-sha1": testament.as_sha1() }
406
if calculated_verifiers != verifiers:
407
trace.mutter("Testament SHA1 %r for %r did not match %r.",
408
calculated_verifiers["testament3-sha1"],
409
rev.revision_id, verifiers["testament3-sha1"])
410
rev.revision_id = original_revid
411
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
412
inv_delta, rev.revision_id, rev.parent_ids, base_inv)
413
ret_tree = InventoryRevisionTree(repo, inv, rev.revision_id)
415
calculated_verifiers = {}
416
store_updater.add_object(o, calculated_verifiers, None)
417
store_updater.finish()
418
trees_cache.add(ret_tree)
419
repo.add_revision(rev.revision_id, rev)
420
if "verify" in debug.debug_flags:
421
verify_commit_reconstruction(target_git_object_retriever,
422
lookup_object, o, rev, ret_tree, parent_trees, mapping,
423
unusual_modes, verifiers)
426
def import_git_objects(repo, mapping, object_iter,
427
target_git_object_retriever, heads, pb=None, limit=None):
110
428
"""Import a set of git objects into a bzr repository.
112
:param repo: Bazaar repository
430
:param repo: Target Bazaar repository
113
431
:param mapping: Mapping to use
114
432
:param object_iter: Iterator over Git objects.
433
: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:
435
def lookup_object(sha):
437
return object_iter[sha]
439
return target_git_object_retriever[sha]
442
heads = list(set(heads))
443
trees_cache = LRUTreeCache(repo)
121
444
# Find and convert commit objects
122
for o in objects.iterkeys():
447
pb.update("finding revisions to fetch", len(graph), None)
449
assert isinstance(head, str)
451
o = lookup_object(head)
123
454
if isinstance(o, Commit):
124
rev = mapping.import_commit(o)
125
root_trees[rev] = objects[o.tree_sha]
455
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
456
mapping.revision_id_foreign_to_bzr)
457
if (repo.has_revision(rev.revision_id) or
458
(roundtrip_revid and repo.has_revision(roundtrip_revid))):
460
graph.append((o.id, o.parents))
461
heads.extend([p for p in o.parents if p not in checked])
462
elif isinstance(o, Tag):
463
if o.object[1] not in checked:
464
heads.append(o.object[1])
466
trace.warning("Unable to import head object %r" % o)
469
# Order the revisions
126
470
# 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)
472
revision_ids = topo_sort(graph)
474
if limit is not None:
475
revision_ids = revision_ids[:limit]
477
for offset in range(0, len(revision_ids), batch_size):
478
target_git_object_retriever.start_write_group()
480
repo.start_write_group()
482
for i, head in enumerate(
483
revision_ids[offset:offset+batch_size]):
485
pb.update("fetching revisions", offset+i,
487
import_git_commit(repo, mapping, head, lookup_object,
488
target_git_object_retriever, trees_cache)
491
repo.abort_write_group()
494
hint = repo.commit_write_group()
496
pack_hints.extend(hint)
498
target_git_object_retriever.abort_write_group()
501
target_git_object_retriever.commit_write_group()
502
return pack_hints, last_imported
159
505
class InterGitRepository(InterRepository):
161
_matching_repo_format = GitFormat()
507
_matching_repo_format = GitRepositoryFormat()
509
def _target_has_shas(self, shas):
510
raise NotImplementedError(self._target_has_shas)
512
def get_determine_wants_heads(self, wants, include_tags=False):
514
def determine_wants(refs):
515
potential = set(wants)
518
[v[1] or v[0] for v in extract_tags(refs).itervalues()])
519
return list(potential - self._target_has_shas(potential))
520
return determine_wants
522
def determine_wants_all(self, refs):
523
potential = set([sha for (ref, sha) in refs.iteritems() if not
524
ref.endswith("^{}")])
525
return list(potential - self._target_has_shas(potential))
164
528
def _get_repo_format_to_test():
168
532
"""See InterRepository.copy_content."""
169
533
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())
536
class InterGitNonGitRepository(InterGitRepository):
537
"""Base InterRepository that copies revisions from a Git into a non-Git
540
def _target_has_shas(self, shas):
541
revids = [self.source.lookup_foreign_revision_id(sha) for sha in shas]
542
return self.target.has_revisions(revids)
544
def get_determine_wants_revids(self, revids, include_tags=False):
546
for revid in set(revids):
547
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
549
return self.get_determine_wants_heads(wants,
550
include_tags=include_tags)
552
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
553
"""Fetch objects from a remote server.
555
:param determine_wants: determine_wants callback
556
:param mapping: BzrGitMapping to use
557
:param pb: Optional progress bar
558
:param limit: Maximum number of commits to import.
559
:return: Tuple with pack hint, last imported revision id and remote refs
561
raise NotImplementedError(self.fetch_objects)
563
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
564
mapping=None, fetch_spec=None):
566
mapping = self.source.get_mapping()
567
if revision_id is not None:
568
interesting_heads = [revision_id]
569
elif fetch_spec is not None:
570
recipe = fetch_spec.get_recipe()
571
if recipe[0] in ("search", "proxy-search"):
572
interesting_heads = recipe[1]
574
raise AssertionError("Unsupported search result type %s" %
577
interesting_heads = None
579
if interesting_heads is not None:
580
determine_wants = self.get_determine_wants_revids(
581
interesting_heads, include_tags=False)
583
determine_wants = self.determine_wants_all
585
(pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
587
if pack_hint is not None and self.target._format.pack_compresses:
588
self.target.pack(hint=pack_hint)
589
assert isinstance(remote_refs, dict)
593
_GIT_PROGRESS_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
594
def report_git_progress(pb, text):
595
text = text.rstrip("\r\n")
596
g = _GIT_PROGRESS_RE.match(text)
598
(text, pct, current, total) = g.groups()
599
pb.update(text, int(current), int(total))
601
pb.update(text, 0, 0)
604
class DetermineWantsRecorder(object):
606
def __init__(self, actual):
609
self.remote_refs = {}
611
def __call__(self, refs):
612
self.remote_refs = refs
613
self.wants = self.actual(refs)
617
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
618
"""InterRepository that copies revisions from a remote Git into a non-Git
621
def get_target_heads(self):
622
# FIXME: This should be more efficient
623
all_revs = self.target.all_revision_ids()
624
parent_map = self.target.get_parent_map(all_revs)
626
map(all_parents.update, parent_map.itervalues())
627
return set(all_revs) - all_parents
629
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
630
"""See `InterGitNonGitRepository`."""
632
report_git_progress(pb, text)
633
store = BazaarObjectStore(self.target, mapping)
636
heads = self.get_target_heads()
637
graph_walker = store.get_graph_walker(
638
[store._lookup_revision_sha1(head) for head in heads])
639
wants_recorder = DetermineWantsRecorder(determine_wants)
643
create_pb = pb = ui.ui_factory.nested_progress_bar()
645
objects_iter = self.source.fetch_objects(
646
wants_recorder, graph_walker, store.get_raw,
648
trace.mutter("Importing %d new revisions",
649
len(wants_recorder.wants))
650
(pack_hint, last_rev) = import_git_objects(self.target,
651
mapping, objects_iter, store, wants_recorder.wants, pb,
653
return (pack_hint, last_rev, wants_recorder.remote_refs)
661
def is_compatible(source, target):
662
"""Be compatible with GitRepository."""
663
if not isinstance(source, RemoteGitRepository):
665
if not target.supports_rich_root():
667
if isinstance(target, GitRepository):
669
if not getattr(target._format, "supports_full_versioned_files", True):
674
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
675
"""InterRepository that copies revisions from a local Git into a non-Git
678
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
679
"""See `InterGitNonGitRepository`."""
680
remote_refs = self.source._git.get_refs()
681
wants = determine_wants(remote_refs)
684
create_pb = pb = ui.ui_factory.nested_progress_bar()
685
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
687
target_git_object_retriever.lock_write()
689
(pack_hint, last_rev) = import_git_objects(self.target,
690
mapping, self.source._git.object_store,
691
target_git_object_retriever, wants, pb, limit)
692
return (pack_hint, last_rev, remote_refs)
694
target_git_object_retriever.unlock()
700
def is_compatible(source, target):
701
"""Be compatible with GitRepository."""
702
if not isinstance(source, LocalGitRepository):
704
if not target.supports_rich_root():
706
if isinstance(target, GitRepository):
708
if not getattr(target._format, "supports_full_versioned_files", True):
713
class InterGitGitRepository(InterGitRepository):
714
"""InterRepository that copies between Git repositories."""
716
def fetch_objects(self, determine_wants, mapping, pb=None):
718
trace.note("git: %s", text)
719
graphwalker = self.target._git.get_graph_walker()
720
if (isinstance(self.source, LocalGitRepository) and
721
isinstance(self.target, LocalGitRepository)):
722
refs = self.source._git.fetch(self.target._git, determine_wants,
724
return (None, None, refs)
725
elif (isinstance(self.source, LocalGitRepository) and
726
isinstance(self.target, RemoteGitRepository)):
727
raise NotImplementedError
728
elif (isinstance(self.source, RemoteGitRepository) and
729
isinstance(self.target, LocalGitRepository)):
730
f, commit = self.target._git.object_store.add_thin_pack()
732
refs = self.source.bzrdir.root_transport.fetch_pack(
733
determine_wants, graphwalker, f.write, progress)
735
return (None, None, refs)
742
def _target_has_shas(self, shas):
743
return set([sha for sha in shas if self.target._git.object_store])
745
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
746
mapping=None, fetch_spec=None, branches=None):
748
mapping = self.source.get_mapping()
750
if revision_id is not None:
751
args = [self.source.lookup_bzr_revision_id(revision_id)[0]]
752
elif fetch_spec is not None:
753
recipe = fetch_spec.get_recipe()
754
if recipe[0] in ("search", "proxy-search"):
757
raise AssertionError(
758
"Unsupported search result type %s" % recipe[0])
759
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in
761
if branches is not None:
762
determine_wants = lambda x: [x[y] for y in branches if not x[y] in r.object_store and x[y] != ZERO_SHA]
763
elif fetch_spec is None and revision_id is None:
764
determine_wants = self.determine_wants_all
766
determine_wants = lambda x: [y for y in args if not y in r.object_store and y != ZERO_SHA]
767
wants_recorder = DetermineWantsRecorder(determine_wants)
768
self.fetch_objects(wants_recorder, mapping)
769
return wants_recorder.remote_refs
772
def is_compatible(source, target):
773
"""Be compatible with GitRepository."""
774
return (isinstance(source, GitRepository) and
775
isinstance(target, GitRepository))
777
def get_determine_wants_revids(self, revids, include_tags=False):
779
for revid in set(revids):
780
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
782
return self.get_determine_wants_heads(wants,
783
include_tags=include_tags)