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
subinvdelta = import_git_blob(texts, mapping, child_path, name,
296
(child_base_hexsha, child_hexsha), base_inv, file_id,
297
revision_id, parent_invs, lookup_object,
298
(child_base_mode, child_mode), store_updater, lookup_file_id)
300
child_modes.update(grandchildmodes)
301
invdelta.extend(subinvdelta)
302
if child_mode not in (stat.S_IFDIR, DEFAULT_FILE_MODE,
303
stat.S_IFLNK, DEFAULT_FILE_MODE|0111):
304
child_modes[child_path] = child_mode
305
# Remove any children that have disappeared
306
if base_tree is not None and type(base_tree) is Tree:
307
invdelta.extend(remove_disappeared_children(base_inv, old_path,
308
base_tree, existing_children, lookup_object))
309
store_updater.add_object(tree, ie, path)
310
return invdelta, child_modes
313
def verify_commit_reconstruction(target_git_object_retriever, lookup_object,
314
o, rev, ret_tree, parent_trees, mapping, unusual_modes, verifiers):
315
new_unusual_modes = mapping.export_unusual_file_modes(rev)
316
if new_unusual_modes != unusual_modes:
317
raise AssertionError("unusual modes don't match: %r != %r" % (
318
unusual_modes, new_unusual_modes))
319
# Verify that we can reconstruct the commit properly
320
rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True,
323
raise AssertionError("Reconstructed commit differs: %r != %r" % (
327
for path, obj, ie in _tree_to_objects(ret_tree, parent_trees,
328
target_git_object_retriever._cache.idmap, unusual_modes,
329
mapping.BZR_DUMMY_FILE):
330
old_obj_id = tree_lookup_path(lookup_object, o.tree, path)[1]
332
if obj.id != old_obj_id:
333
diff.append((path, lookup_object(old_obj_id), obj))
334
for (path, old_obj, new_obj) in diff:
335
while (old_obj.type_name == "tree" and
336
new_obj.type_name == "tree" and
337
sorted(old_obj) == sorted(new_obj)):
339
if old_obj[name][0] != new_obj[name][0]:
340
raise AssertionError("Modes for %s differ: %o != %o" %
341
(path, old_obj[name][0], new_obj[name][0]))
342
if old_obj[name][1] != new_obj[name][1]:
343
# Found a differing child, delve deeper
344
path = posixpath.join(path, name)
345
old_obj = lookup_object(old_obj[name][1])
346
new_obj = new_objs[path]
348
raise AssertionError("objects differ for %s: %r != %r" % (path,
352
def import_git_commit(repo, mapping, head, lookup_object,
353
target_git_object_retriever, trees_cache):
354
o = lookup_object(head)
355
# Note that this uses mapping.revision_id_foreign_to_bzr. If the parents
356
# were bzr roundtripped revisions they would be specified in the
358
rev, roundtrip_revid, verifiers = mapping.import_commit(
359
o, mapping.revision_id_foreign_to_bzr)
360
# We have to do this here, since we have to walk the tree and
361
# we need to make sure to import the blobs / trees with the right
362
# path; this may involve adding them more than once.
363
parent_trees = trees_cache.revision_trees(rev.parent_ids)
364
if parent_trees == []:
365
base_inv = Inventory(root_id=None)
369
base_inv = parent_trees[0].inventory
370
base_tree = lookup_object(o.parents[0]).tree
371
base_mode = stat.S_IFDIR
372
store_updater = target_git_object_retriever._get_updater(rev)
373
fileid_map = mapping.get_fileid_map(lookup_object, o.tree)
374
inv_delta, unusual_modes = import_git_tree(repo.texts,
375
mapping, "", "", (base_tree, o.tree), base_inv,
376
None, rev.revision_id, [p.inventory for p in parent_trees],
377
lookup_object, (base_mode, stat.S_IFDIR), store_updater,
378
fileid_map.lookup_file_id,
379
allow_submodules=getattr(repo._format, "supports_tree_reference",
381
if unusual_modes != {}:
382
for path, mode in unusual_modes.iteritems():
383
warn_unusual_mode(rev.foreign_revid, path, mode)
384
mapping.import_unusual_file_modes(rev, unusual_modes)
386
basis_id = rev.parent_ids[0]
388
basis_id = NULL_REVISION
390
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
391
inv_delta, rev.revision_id, rev.parent_ids, base_inv)
392
ret_tree = InventoryRevisionTree(repo, inv, rev.revision_id)
393
if roundtrip_revid is not None:
394
original_revid = rev.revision_id
395
rev.revision_id = roundtrip_revid
397
if getattr(StrictTestament3, "from_revision_tree", None):
398
testament = StrictTestament3(rev, ret_tree)
400
testament = StrictTestament3(rev, inv)
401
calculated_verifiers = { "testament3-sha1": testament.as_sha1() }
402
if calculated_verifiers != verifiers:
403
trace.mutter("Testament SHA1 %r for %r did not match %r.",
404
calculated_verifiers["testament3-sha1"],
405
rev.revision_id, verifiers["testament3-sha1"])
406
rev.revision_id = original_revid
408
calculated_verifiers = {}
409
store_updater.add_object(o, calculated_verifiers, None)
410
store_updater.finish()
411
trees_cache.add(ret_tree)
412
repo.add_revision(rev.revision_id, rev)
413
if "verify" in debug.debug_flags:
414
verify_commit_reconstruction(target_git_object_retriever,
415
lookup_object, o, rev, ret_tree, parent_trees, mapping,
416
unusual_modes, verifiers)
419
def import_git_objects(repo, mapping, object_iter,
420
target_git_object_retriever, heads, pb=None, limit=None):
110
421
"""Import a set of git objects into a bzr repository.
112
:param repo: Bazaar repository
423
:param repo: Target Bazaar repository
113
424
:param mapping: Mapping to use
114
425
:param object_iter: Iterator over Git objects.
426
: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:
428
def lookup_object(sha):
430
return object_iter[sha]
432
return target_git_object_retriever[sha]
435
heads = list(set(heads))
436
trees_cache = LRUTreeCache(repo)
121
437
# Find and convert commit objects
122
for o in objects.iterkeys():
440
pb.update("finding revisions to fetch", len(graph), None)
442
assert isinstance(head, str)
444
o = lookup_object(head)
123
447
if isinstance(o, Commit):
124
rev = mapping.import_commit(o)
125
root_trees[rev] = objects[o.tree_sha]
448
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
449
mapping.revision_id_foreign_to_bzr)
450
if (repo.has_revision(rev.revision_id) or
451
(roundtrip_revid and repo.has_revision(roundtrip_revid))):
453
graph.append((o.id, o.parents))
454
heads.extend([p for p in o.parents if p not in checked])
455
elif isinstance(o, Tag):
456
if o.object[1] not in checked:
457
heads.append(o.object[1])
459
trace.warning("Unable to import head object %r" % o)
462
# Order the revisions
126
463
# 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)
465
revision_ids = topo_sort(graph)
467
if limit is not None:
468
revision_ids = revision_ids[:limit]
470
for offset in range(0, len(revision_ids), batch_size):
471
target_git_object_retriever.start_write_group()
473
repo.start_write_group()
475
for i, head in enumerate(
476
revision_ids[offset:offset+batch_size]):
478
pb.update("fetching revisions", offset+i,
480
import_git_commit(repo, mapping, head, lookup_object,
481
target_git_object_retriever, trees_cache)
484
repo.abort_write_group()
487
hint = repo.commit_write_group()
489
pack_hints.extend(hint)
491
target_git_object_retriever.abort_write_group()
494
target_git_object_retriever.commit_write_group()
495
return pack_hints, last_imported
159
498
class InterGitRepository(InterRepository):
161
_matching_repo_format = GitFormat()
500
_matching_repo_format = GitRepositoryFormat()
502
def _target_has_shas(self, shas):
503
raise NotImplementedError(self._target_has_shas)
505
def get_determine_wants_heads(self, wants, include_tags=False):
507
def determine_wants(refs):
508
potential = set(wants)
511
[v[1] or v[0] for v in extract_tags(refs).itervalues()])
512
return list(potential - self._target_has_shas(potential))
513
return determine_wants
515
def determine_wants_all(self, refs):
516
potential = set([sha for (ref, sha) in refs.iteritems() if not
517
ref.endswith("^{}")])
518
return list(potential - self._target_has_shas(potential))
164
521
def _get_repo_format_to_test():
168
525
"""See InterRepository.copy_content."""
169
526
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())
529
class InterGitNonGitRepository(InterGitRepository):
530
"""Base InterRepository that copies revisions from a Git into a non-Git
533
def _target_has_shas(self, shas):
534
revids = [self.source.lookup_foreign_revision_id(sha) for sha in shas]
535
return self.target.has_revisions(revids)
537
def get_determine_wants_revids(self, revids, include_tags=False):
539
for revid in set(revids):
540
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
542
return self.get_determine_wants_heads(wants,
543
include_tags=include_tags)
545
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
546
"""Fetch objects from a remote server.
548
:param determine_wants: determine_wants callback
549
:param mapping: BzrGitMapping to use
550
:param pb: Optional progress bar
551
:param limit: Maximum number of commits to import.
552
:return: Tuple with pack hint, last imported revision id and remote refs
554
raise NotImplementedError(self.fetch_objects)
556
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
557
mapping=None, fetch_spec=None):
559
mapping = self.source.get_mapping()
560
if revision_id is not None:
561
interesting_heads = [revision_id]
562
elif fetch_spec is not None:
563
recipe = fetch_spec.get_recipe()
564
if recipe[0] in ("search", "proxy-search"):
565
interesting_heads = recipe[1]
567
raise AssertionError("Unsupported search result type %s" %
570
interesting_heads = None
572
if interesting_heads is not None:
573
determine_wants = self.get_determine_wants_revids(
574
interesting_heads, include_tags=False)
576
determine_wants = self.determine_wants_all
578
(pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
580
if pack_hint is not None and self.target._format.pack_compresses:
581
self.target.pack(hint=pack_hint)
582
assert isinstance(remote_refs, dict)
586
_GIT_PROGRESS_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
587
def report_git_progress(pb, text):
588
text = text.rstrip("\r\n")
589
g = _GIT_PROGRESS_RE.match(text)
591
(text, pct, current, total) = g.groups()
592
pb.update(text, int(current), int(total))
594
pb.update(text, 0, 0)
597
class DetermineWantsRecorder(object):
599
def __init__(self, actual):
602
self.remote_refs = {}
604
def __call__(self, refs):
605
self.remote_refs = refs
606
self.wants = self.actual(refs)
610
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
611
"""InterRepository that copies revisions from a remote Git into a non-Git
614
def get_target_heads(self):
615
# FIXME: This should be more efficient
616
all_revs = self.target.all_revision_ids()
617
parent_map = self.target.get_parent_map(all_revs)
619
map(all_parents.update, parent_map.itervalues())
620
return set(all_revs) - all_parents
622
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
623
"""See `InterGitNonGitRepository`."""
625
report_git_progress(pb, text)
626
store = BazaarObjectStore(self.target, mapping)
629
heads = self.get_target_heads()
630
graph_walker = store.get_graph_walker(
631
[store._lookup_revision_sha1(head) for head in heads])
632
wants_recorder = DetermineWantsRecorder(determine_wants)
636
create_pb = pb = ui.ui_factory.nested_progress_bar()
638
objects_iter = self.source.fetch_objects(
639
wants_recorder, graph_walker, store.get_raw,
641
trace.mutter("Importing %d new revisions",
642
len(wants_recorder.wants))
643
(pack_hint, last_rev) = import_git_objects(self.target,
644
mapping, objects_iter, store, wants_recorder.wants, pb,
646
return (pack_hint, last_rev, wants_recorder.remote_refs)
654
def is_compatible(source, target):
655
"""Be compatible with GitRepository."""
656
if not isinstance(source, RemoteGitRepository):
658
if not target.supports_rich_root():
660
if isinstance(target, GitRepository):
662
if not getattr(target._format, "supports_full_versioned_files", True):
667
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
668
"""InterRepository that copies revisions from a local Git into a non-Git
671
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
672
"""See `InterGitNonGitRepository`."""
673
remote_refs = self.source._git.get_refs()
674
wants = determine_wants(remote_refs)
677
create_pb = pb = ui.ui_factory.nested_progress_bar()
678
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
680
target_git_object_retriever.lock_write()
682
(pack_hint, last_rev) = import_git_objects(self.target,
683
mapping, self.source._git.object_store,
684
target_git_object_retriever, wants, pb, limit)
685
return (pack_hint, last_rev, remote_refs)
687
target_git_object_retriever.unlock()
693
def is_compatible(source, target):
694
"""Be compatible with GitRepository."""
695
if not isinstance(source, LocalGitRepository):
697
if not target.supports_rich_root():
699
if isinstance(target, GitRepository):
701
if not getattr(target._format, "supports_full_versioned_files", True):
706
class InterGitGitRepository(InterGitRepository):
707
"""InterRepository that copies between Git repositories."""
709
def fetch_objects(self, determine_wants, mapping, pb=None):
711
trace.note("git: %s", text)
712
graphwalker = self.target._git.get_graph_walker()
713
if (isinstance(self.source, LocalGitRepository) and
714
isinstance(self.target, LocalGitRepository)):
715
refs = self.source._git.fetch(self.target._git, determine_wants,
717
return (None, None, refs)
718
elif (isinstance(self.source, LocalGitRepository) and
719
isinstance(self.target, RemoteGitRepository)):
720
raise NotImplementedError
721
elif (isinstance(self.source, RemoteGitRepository) and
722
isinstance(self.target, LocalGitRepository)):
723
f, commit = self.target._git.object_store.add_thin_pack()
725
refs = self.source.bzrdir.root_transport.fetch_pack(
726
determine_wants, graphwalker, f.write, progress)
728
return (None, None, refs)
735
def _target_has_shas(self, shas):
736
return set([sha for sha in shas if self.target._git.object_store])
738
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
739
mapping=None, fetch_spec=None, branches=None):
741
mapping = self.source.get_mapping()
743
if revision_id is not None:
744
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
745
elif fetch_spec is not None:
746
recipe = fetch_spec.get_recipe()
747
if recipe[0] in ("search", "proxy-search"):
750
raise AssertionError(
751
"Unsupported search result type %s" % recipe[0])
752
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in
754
if branches is not None:
755
determine_wants = lambda x: [x[y] for y in branches if not x[y] in r.object_store and x[y] != ZERO_SHA]
756
elif fetch_spec is None and revision_id is None:
757
determine_wants = self.determine_wants_all
759
determine_wants = lambda x: [y for y in args if not y in r.object_store and y != ZERO_SHA]
760
wants_recorder = DetermineWantsRecorder(determine_wants)
761
self.fetch_objects(wants_recorder, mapping)
762
return wants_recorder.remote_refs
765
def is_compatible(source, target):
766
"""Be compatible with GitRepository."""
767
return (isinstance(source, GitRepository) and
768
isinstance(target, GitRepository))
770
def get_determine_wants_revids(self, revids, include_tags=False):
772
for revid in set(revids):
773
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
775
return self.get_determine_wants_heads(wants,
776
include_tags=include_tags)