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
tree_supplement = 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
tree_supplement.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
398
if getattr(StrictTestament3, "from_revision_tree", None):
399
testament = StrictTestament3(rev, ret_tree)
401
testament = StrictTestament3(rev, inv)
402
calculated_verifiers = { "testament3-sha1": testament.as_sha1() }
403
if calculated_verifiers != verifiers:
404
trace.mutter("Testament SHA1 %r for %r did not match %r.",
405
calculated_verifiers["testament3-sha1"],
406
rev.revision_id, verifiers["testament3-sha1"])
407
rev.revision_id = original_revid
409
calculated_verifiers = {}
411
calculated_verifiers = {}
412
store_updater.add_object(o, calculated_verifiers, None)
413
store_updater.finish()
414
trees_cache.add(ret_tree)
415
repo.add_revision(rev.revision_id, rev)
416
if "verify" in debug.debug_flags:
417
verify_commit_reconstruction(target_git_object_retriever,
418
lookup_object, o, rev, ret_tree, parent_trees, mapping,
419
unusual_modes, verifiers)
422
def import_git_objects(repo, mapping, object_iter,
423
target_git_object_retriever, heads, pb=None, limit=None):
110
424
"""Import a set of git objects into a bzr repository.
112
:param repo: Bazaar repository
426
:param repo: Target Bazaar repository
113
427
:param mapping: Mapping to use
114
428
:param object_iter: Iterator over Git objects.
429
: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:
431
def lookup_object(sha):
433
return object_iter[sha]
435
return target_git_object_retriever[sha]
438
heads = list(set(heads))
439
trees_cache = LRUTreeCache(repo)
121
440
# Find and convert commit objects
122
for o in objects.iterkeys():
443
pb.update("finding revisions to fetch", len(graph), None)
445
assert isinstance(head, str)
447
o = lookup_object(head)
123
450
if isinstance(o, Commit):
124
rev = mapping.import_commit(o)
125
root_trees[rev] = objects[o.tree_sha]
451
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
452
mapping.revision_id_foreign_to_bzr)
453
if (repo.has_revision(rev.revision_id) or
454
(roundtrip_revid and repo.has_revision(roundtrip_revid))):
456
graph.append((o.id, o.parents))
457
heads.extend([p for p in o.parents if p not in checked])
458
elif isinstance(o, Tag):
459
if o.object[1] not in checked:
460
heads.append(o.object[1])
462
trace.warning("Unable to import head object %r" % o)
465
# Order the revisions
126
466
# 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)
468
revision_ids = topo_sort(graph)
470
if limit is not None:
471
revision_ids = revision_ids[:limit]
473
for offset in range(0, len(revision_ids), batch_size):
474
target_git_object_retriever.start_write_group()
476
repo.start_write_group()
478
for i, head in enumerate(
479
revision_ids[offset:offset+batch_size]):
481
pb.update("fetching revisions", offset+i,
483
import_git_commit(repo, mapping, head, lookup_object,
484
target_git_object_retriever, trees_cache)
487
repo.abort_write_group()
490
hint = repo.commit_write_group()
492
pack_hints.extend(hint)
494
target_git_object_retriever.abort_write_group()
497
target_git_object_retriever.commit_write_group()
498
return pack_hints, last_imported
159
501
class InterGitRepository(InterRepository):
161
_matching_repo_format = GitFormat()
503
_matching_repo_format = GitRepositoryFormat()
505
def _target_has_shas(self, shas):
506
raise NotImplementedError(self._target_has_shas)
508
def get_determine_wants_heads(self, wants, include_tags=False):
510
def determine_wants(refs):
511
potential = set(wants)
514
[v[1] or v[0] for v in extract_tags(refs).itervalues()])
515
return list(potential - self._target_has_shas(potential))
516
return determine_wants
518
def determine_wants_all(self, refs):
519
potential = set([sha for (ref, sha) in refs.iteritems() if not
520
ref.endswith("^{}")])
521
return list(potential - self._target_has_shas(potential))
164
524
def _get_repo_format_to_test():
168
528
"""See InterRepository.copy_content."""
169
529
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())
532
class InterGitNonGitRepository(InterGitRepository):
533
"""Base InterRepository that copies revisions from a Git into a non-Git
536
def _target_has_shas(self, shas):
537
revids = [self.source.lookup_foreign_revision_id(sha) for sha in shas]
538
return self.target.has_revisions(revids)
540
def get_determine_wants_revids(self, revids, include_tags=False):
542
for revid in set(revids):
543
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
545
return self.get_determine_wants_heads(wants,
546
include_tags=include_tags)
548
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
549
"""Fetch objects from a remote server.
551
:param determine_wants: determine_wants callback
552
:param mapping: BzrGitMapping to use
553
:param pb: Optional progress bar
554
:param limit: Maximum number of commits to import.
555
:return: Tuple with pack hint, last imported revision id and remote refs
557
raise NotImplementedError(self.fetch_objects)
559
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
560
mapping=None, fetch_spec=None):
562
mapping = self.source.get_mapping()
563
if revision_id is not None:
564
interesting_heads = [revision_id]
565
elif fetch_spec is not None:
566
recipe = fetch_spec.get_recipe()
567
if recipe[0] in ("search", "proxy-search"):
568
interesting_heads = recipe[1]
570
raise AssertionError("Unsupported search result type %s" %
573
interesting_heads = None
575
if interesting_heads is not None:
576
determine_wants = self.get_determine_wants_revids(
577
interesting_heads, include_tags=False)
579
determine_wants = self.determine_wants_all
581
(pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
583
if pack_hint is not None and self.target._format.pack_compresses:
584
self.target.pack(hint=pack_hint)
585
assert isinstance(remote_refs, dict)
589
_GIT_PROGRESS_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
590
def report_git_progress(pb, text):
591
text = text.rstrip("\r\n")
592
g = _GIT_PROGRESS_RE.match(text)
594
(text, pct, current, total) = g.groups()
595
pb.update(text, int(current), int(total))
597
pb.update(text, 0, 0)
600
class DetermineWantsRecorder(object):
602
def __init__(self, actual):
605
self.remote_refs = {}
607
def __call__(self, refs):
608
self.remote_refs = refs
609
self.wants = self.actual(refs)
613
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
614
"""InterRepository that copies revisions from a remote Git into a non-Git
617
def get_target_heads(self):
618
# FIXME: This should be more efficient
619
all_revs = self.target.all_revision_ids()
620
parent_map = self.target.get_parent_map(all_revs)
622
map(all_parents.update, parent_map.itervalues())
623
return set(all_revs) - all_parents
625
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
626
"""See `InterGitNonGitRepository`."""
628
report_git_progress(pb, text)
629
store = BazaarObjectStore(self.target, mapping)
632
heads = self.get_target_heads()
633
graph_walker = store.get_graph_walker(
634
[store._lookup_revision_sha1(head) for head in heads])
635
wants_recorder = DetermineWantsRecorder(determine_wants)
639
create_pb = pb = ui.ui_factory.nested_progress_bar()
641
objects_iter = self.source.fetch_objects(
642
wants_recorder, graph_walker, store.get_raw,
644
trace.mutter("Importing %d new revisions",
645
len(wants_recorder.wants))
646
(pack_hint, last_rev) = import_git_objects(self.target,
647
mapping, objects_iter, store, wants_recorder.wants, pb,
649
return (pack_hint, last_rev, wants_recorder.remote_refs)
657
def is_compatible(source, target):
658
"""Be compatible with GitRepository."""
659
if not isinstance(source, RemoteGitRepository):
661
if not target.supports_rich_root():
663
if isinstance(target, GitRepository):
665
if not getattr(target._format, "supports_full_versioned_files", True):
670
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
671
"""InterRepository that copies revisions from a local Git into a non-Git
674
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
675
"""See `InterGitNonGitRepository`."""
676
remote_refs = self.source._git.get_refs()
677
wants = determine_wants(remote_refs)
680
create_pb = pb = ui.ui_factory.nested_progress_bar()
681
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
683
target_git_object_retriever.lock_write()
685
(pack_hint, last_rev) = import_git_objects(self.target,
686
mapping, self.source._git.object_store,
687
target_git_object_retriever, wants, pb, limit)
688
return (pack_hint, last_rev, remote_refs)
690
target_git_object_retriever.unlock()
696
def is_compatible(source, target):
697
"""Be compatible with GitRepository."""
698
if not isinstance(source, LocalGitRepository):
700
if not target.supports_rich_root():
702
if isinstance(target, GitRepository):
704
if not getattr(target._format, "supports_full_versioned_files", True):
709
class InterGitGitRepository(InterGitRepository):
710
"""InterRepository that copies between Git repositories."""
712
def fetch_objects(self, determine_wants, mapping, pb=None):
714
trace.note("git: %s", text)
715
graphwalker = self.target._git.get_graph_walker()
716
if (isinstance(self.source, LocalGitRepository) and
717
isinstance(self.target, LocalGitRepository)):
718
refs = self.source._git.fetch(self.target._git, determine_wants,
720
return (None, None, refs)
721
elif (isinstance(self.source, LocalGitRepository) and
722
isinstance(self.target, RemoteGitRepository)):
723
raise NotImplementedError
724
elif (isinstance(self.source, RemoteGitRepository) and
725
isinstance(self.target, LocalGitRepository)):
726
f, commit = self.target._git.object_store.add_thin_pack()
728
refs = self.source.bzrdir.root_transport.fetch_pack(
729
determine_wants, graphwalker, f.write, progress)
731
return (None, None, refs)
738
def _target_has_shas(self, shas):
739
return set([sha for sha in shas if self.target._git.object_store])
741
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
742
mapping=None, fetch_spec=None, branches=None):
744
mapping = self.source.get_mapping()
746
if revision_id is not None:
747
args = [self.source.lookup_bzr_revision_id(revision_id)[0]]
748
elif fetch_spec is not None:
749
recipe = fetch_spec.get_recipe()
750
if recipe[0] in ("search", "proxy-search"):
753
raise AssertionError(
754
"Unsupported search result type %s" % recipe[0])
755
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in
757
if branches is not None:
758
determine_wants = lambda x: [x[y] for y in branches if not x[y] in r.object_store and x[y] != ZERO_SHA]
759
elif fetch_spec is None and revision_id is None:
760
determine_wants = self.determine_wants_all
762
determine_wants = lambda x: [y for y in args if not y in r.object_store and y != ZERO_SHA]
763
wants_recorder = DetermineWantsRecorder(determine_wants)
764
self.fetch_objects(wants_recorder, mapping)
765
return wants_recorder.remote_refs
768
def is_compatible(source, target):
769
"""Be compatible with GitRepository."""
770
return (isinstance(source, GitRepository) and
771
isinstance(target, GitRepository))
773
def get_determine_wants_revids(self, revids, include_tags=False):
775
for revid in set(revids):
776
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
778
return self.get_determine_wants_heads(wants,
779
include_tags=include_tags)