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 (
23
from dulwich.object_store import (
26
from itertools import (
39
from bzrlib.errors import (
43
from bzrlib.inventory import (
50
from bzrlib.repository import (
53
from bzrlib.revision import (
56
from bzrlib.revisiontree import (
59
from bzrlib.testament import (
62
from bzrlib.tsort import (
65
from bzrlib.versionedfile import (
66
ChunkedContentFactory,
69
from bzrlib.plugins.git.mapping import (
75
from bzrlib.plugins.git.object_store import (
80
from bzrlib.plugins.git.remote import (
83
from bzrlib.plugins.git.repository import (
90
def import_git_blob(texts, mapping, path, name, (base_hexsha, hexsha),
91
base_inv, parent_id, revision_id,
92
parent_invs, lookup_object, (base_mode, mode), store_updater,
68
94
"""Import a git blob object into a bzr repository.
70
:param repo: bzr repository
96
:param texts: VersionedFiles to add to
71
97
:param path: Path in the tree
72
98
: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):
99
:return: Inventory delta for this file
101
if mapping.is_control_file(path):
103
if base_hexsha == hexsha and base_mode == mode:
104
# If nothing has changed since the base revision, we're done
106
file_id = lookup_file_id(path)
107
if stat.S_ISLNK(mode):
111
ie = cls(file_id, name.decode("utf-8"), parent_id)
112
if ie.kind == "file":
113
ie.executable = mode_is_executable(mode)
114
if base_hexsha == hexsha and mode_kind(base_mode) == mode_kind(mode):
115
base_ie = base_inv[base_inv.path2id(path)]
116
ie.text_size = base_ie.text_size
117
ie.text_sha1 = base_ie.text_sha1
118
if ie.kind == "symlink":
119
ie.symlink_target = base_ie.symlink_target
120
if ie.executable == base_ie.executable:
121
ie.revision = base_ie.revision
123
blob = lookup_object(hexsha)
125
blob = lookup_object(hexsha)
126
if ie.kind == "symlink":
128
ie.symlink_target = blob.data
130
ie.text_size = sum(imap(len, blob.chunked))
131
ie.text_sha1 = osutils.sha_strings(blob.chunked)
132
# Check what revision we should store
134
for pinv in parent_invs:
139
if (pie.text_sha1 == ie.text_sha1 and
140
pie.executable == ie.executable and
141
pie.symlink_target == ie.symlink_target):
142
# found a revision in one of the parents to use
143
ie.revision = pie.revision
145
parent_key = (file_id, pie.revision)
146
if not parent_key in parent_keys:
147
parent_keys.append(parent_key)
148
if ie.revision is None:
149
# Need to store a new revision
150
ie.revision = revision_id
151
assert ie.revision is not None
152
if ie.kind == 'symlink':
155
chunks = blob.chunked
156
texts.insert_record_stream([
157
ChunkedContentFactory((file_id, ie.revision),
158
tuple(parent_keys), ie.text_sha1, chunks)])
160
if base_hexsha is not None:
161
old_path = path.decode("utf-8") # Renames are not supported yet
162
if stat.S_ISDIR(base_mode):
163
invdelta.extend(remove_disappeared_children(base_inv, old_path,
164
lookup_object(base_hexsha), [], lookup_object))
167
new_path = path.decode("utf-8")
168
invdelta.append((old_path, new_path, file_id, ie))
169
if base_hexsha != hexsha:
170
store_updater.add_object(blob, ie, path)
174
class SubmodulesRequireSubtrees(BzrError):
175
_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'."""
179
def import_git_submodule(texts, mapping, path, name, (base_hexsha, hexsha),
180
base_inv, parent_id, revision_id, parent_invs, lookup_object,
181
(base_mode, mode), store_updater, lookup_file_id):
182
if base_hexsha == hexsha and base_mode == mode:
184
file_id = lookup_file_id(path)
185
ie = TreeReference(file_id, name.decode("utf-8"), parent_id)
186
ie.revision = revision_id
187
if base_hexsha is None:
191
ie.reference_revision = mapping.revision_id_foreign_to_bzr(hexsha)
192
texts.insert_record_stream([
193
ChunkedContentFactory((file_id, ie.revision), (), None, [])])
194
invdelta = [(oldpath, path, file_id, ie)]
198
def remove_disappeared_children(base_inv, path, base_tree, existing_children,
200
"""Generate an inventory delta for removed children.
202
:param base_inv: Base inventory against which to generate the
204
:param path: Path to process (unicode)
205
:param base_tree: Git Tree base object
206
:param existing_children: Children that still exist
207
:param lookup_object: Lookup a git object by its SHA1
208
:return: Inventory delta, as list
210
assert type(path) is unicode
212
for name, mode, hexsha in base_tree.iteritems():
213
if name in existing_children:
215
c_path = posixpath.join(path, name.decode("utf-8"))
216
file_id = base_inv.path2id(c_path)
217
assert file_id is not None
218
ret.append((c_path, None, file_id, None))
219
if stat.S_ISDIR(mode):
220
ret.extend(remove_disappeared_children(
221
base_inv, c_path, lookup_object(hexsha), [], lookup_object))
225
def import_git_tree(texts, mapping, path, name, (base_hexsha, hexsha),
226
base_inv, parent_id, revision_id, parent_invs,
227
lookup_object, (base_mode, mode), store_updater,
228
lookup_file_id, allow_submodules=False):
82
229
"""Import a git tree object into a bzr repository.
84
:param repo: A Bzr repository object
85
:param path: Path in the tree
231
:param texts: VersionedFiles object to add to
232
:param path: Path in the tree (str)
233
:param name: Name of the tree (str)
86
234
:param tree: A git tree object
87
:param inv: Inventory object
235
:param base_inv: Base inventory against which to return inventory delta
236
: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):
238
assert type(path) is str
239
assert type(name) is str
240
if base_hexsha == hexsha and base_mode == mode:
241
# If nothing has changed since the base revision, we're done
244
file_id = lookup_file_id(path)
245
# We just have to hope this is indeed utf-8:
246
ie = InventoryDirectory(file_id, name.decode("utf-8"), parent_id)
247
tree = lookup_object(hexsha)
248
if base_hexsha is None:
250
old_path = None # Newly appeared here
252
base_tree = lookup_object(base_hexsha)
253
old_path = path.decode("utf-8") # Renames aren't supported yet
254
new_path = path.decode("utf-8")
255
if base_tree is None or type(base_tree) is not Tree:
256
ie.revision = revision_id
257
invdelta.append((old_path, new_path, ie.file_id, ie))
258
texts.insert_record_stream([
259
ChunkedContentFactory((ie.file_id, ie.revision), (), None, [])])
260
# Remember for next time
261
existing_children = set()
263
for child_mode, name, child_hexsha in tree.entries():
264
existing_children.add(name)
265
child_path = posixpath.join(path, name)
266
if type(base_tree) is Tree:
268
child_base_mode, child_base_hexsha = base_tree[name]
270
child_base_hexsha = None
273
child_base_hexsha = None
275
if stat.S_ISDIR(child_mode):
276
subinvdelta, grandchildmodes = import_git_tree(texts, mapping,
277
child_path, name, (child_base_hexsha, child_hexsha), base_inv,
278
file_id, revision_id, parent_invs, lookup_object,
279
(child_base_mode, child_mode), store_updater, lookup_file_id,
280
allow_submodules=allow_submodules)
281
elif S_ISGITLINK(child_mode): # submodule
282
if not allow_submodules:
283
raise SubmodulesRequireSubtrees()
284
subinvdelta, grandchildmodes = import_git_submodule(texts, mapping,
285
child_path, name, (child_base_hexsha, child_hexsha), base_inv,
286
file_id, revision_id, parent_invs, lookup_object,
287
(child_base_mode, child_mode), store_updater, lookup_file_id)
289
subinvdelta = import_git_blob(texts, mapping, child_path, name,
290
(child_base_hexsha, child_hexsha), base_inv, file_id,
291
revision_id, parent_invs, lookup_object,
292
(child_base_mode, child_mode), store_updater, lookup_file_id)
294
child_modes.update(grandchildmodes)
295
invdelta.extend(subinvdelta)
296
if child_mode not in (stat.S_IFDIR, DEFAULT_FILE_MODE,
297
stat.S_IFLNK, DEFAULT_FILE_MODE|0111):
298
child_modes[child_path] = child_mode
299
# Remove any children that have disappeared
300
if base_tree is not None and type(base_tree) is Tree:
301
invdelta.extend(remove_disappeared_children(base_inv, old_path,
302
base_tree, existing_children, lookup_object))
303
store_updater.add_object(tree, ie, path)
304
return invdelta, child_modes
307
def verify_commit_reconstruction(target_git_object_retriever, lookup_object,
308
o, rev, ret_tree, parent_trees, mapping, unusual_modes, verifiers):
309
new_unusual_modes = mapping.export_unusual_file_modes(rev)
310
if new_unusual_modes != unusual_modes:
311
raise AssertionError("unusual modes don't match: %r != %r" % (
312
unusual_modes, new_unusual_modes))
313
# Verify that we can reconstruct the commit properly
314
rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True,
317
raise AssertionError("Reconstructed commit differs: %r != %r" % (
321
for path, obj, ie in _tree_to_objects(ret_tree, parent_trees,
322
target_git_object_retriever._cache.idmap, unusual_modes, mapping.BZR_DUMMY_FILE):
323
old_obj_id = tree_lookup_path(lookup_object, o.tree, path)[1]
325
if obj.id != old_obj_id:
326
diff.append((path, lookup_object(old_obj_id), obj))
327
for (path, old_obj, new_obj) in diff:
328
while (old_obj.type_name == "tree" and
329
new_obj.type_name == "tree" and
330
sorted(old_obj) == sorted(new_obj)):
332
if old_obj[name][0] != new_obj[name][0]:
333
raise AssertionError("Modes for %s differ: %o != %o" %
334
(path, old_obj[name][0], new_obj[name][0]))
335
if old_obj[name][1] != new_obj[name][1]:
336
# Found a differing child, delve deeper
337
path = posixpath.join(path, name)
338
old_obj = lookup_object(old_obj[name][1])
339
new_obj = new_objs[path]
341
raise AssertionError("objects differ for %s: %r != %r" % (path,
345
def import_git_commit(repo, mapping, head, lookup_object,
346
target_git_object_retriever, trees_cache):
347
o = lookup_object(head)
348
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
349
lambda x: target_git_object_retriever.lookup_git_sha(x)[1][0])
350
# We have to do this here, since we have to walk the tree and
351
# we need to make sure to import the blobs / trees with the right
352
# path; this may involve adding them more than once.
353
parent_trees = trees_cache.revision_trees(rev.parent_ids)
354
if parent_trees == []:
355
base_inv = Inventory(root_id=None)
359
base_inv = parent_trees[0].inventory
360
base_tree = lookup_object(o.parents[0]).tree
361
base_mode = stat.S_IFDIR
362
store_updater = target_git_object_retriever._get_updater(rev)
363
fileid_map = mapping.get_fileid_map(lookup_object, o.tree)
364
inv_delta, unusual_modes = import_git_tree(repo.texts,
365
mapping, "", "", (base_tree, o.tree), base_inv,
366
None, rev.revision_id, [p.inventory for p in parent_trees],
367
lookup_object, (base_mode, stat.S_IFDIR), store_updater,
368
fileid_map.lookup_file_id,
369
allow_submodules=getattr(repo._format, "supports_tree_reference", False))
370
if unusual_modes != {}:
371
for path, mode in unusual_modes.iteritems():
372
warn_unusual_mode(rev.foreign_revid, path, mode)
373
mapping.import_unusual_file_modes(rev, unusual_modes)
375
basis_id = rev.parent_ids[0]
377
basis_id = NULL_REVISION
379
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
380
inv_delta, rev.revision_id, rev.parent_ids, base_inv)
382
testament = StrictTestament3(rev, inv)
383
calculated_verifiers = { "testament3-sha1": testament.as_sha1() }
384
if roundtrip_revid is not None:
385
original_revid = rev.revision_id
386
rev.revision_id = roundtrip_revid
387
if calculated_verifiers != verifiers:
388
trace.mutter("Testament SHA1 %r for %r did not match %r.",
389
calculated_verifiers["testament3-sha1"],
390
rev.revision_id, verifiers["testament3-sha1"])
391
rev.revision_id = original_revid
392
store_updater.add_object(o, calculated_verifiers, None)
393
store_updater.finish()
394
ret_tree = RevisionTree(repo, inv, rev.revision_id)
395
trees_cache.add(ret_tree)
396
repo.add_revision(rev.revision_id, rev)
397
if "verify" in debug.debug_flags:
398
verify_commit_reconstruction(target_git_object_retriever,
399
lookup_object, o, rev, ret_tree, parent_trees, mapping,
400
unusual_modes, verifiers)
403
def import_git_objects(repo, mapping, object_iter,
404
target_git_object_retriever, heads, pb=None, limit=None):
110
405
"""Import a set of git objects into a bzr repository.
112
:param repo: Bazaar repository
407
:param repo: Target Bazaar repository
113
408
:param mapping: Mapping to use
114
409
:param object_iter: Iterator over Git objects.
410
: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:
412
def lookup_object(sha):
414
return object_iter[sha]
416
return target_git_object_retriever[sha]
419
heads = list(set(heads))
420
trees_cache = LRUTreeCache(repo)
121
421
# Find and convert commit objects
122
for o in objects.iterkeys():
424
pb.update("finding revisions to fetch", len(graph), None)
426
assert isinstance(head, str)
428
o = lookup_object(head)
123
431
if isinstance(o, Commit):
124
rev = mapping.import_commit(o)
125
root_trees[rev] = objects[o.tree_sha]
432
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
434
if (repo.has_revision(rev.revision_id) or
435
(roundtrip_revid and repo.has_revision(roundtrip_revid))):
437
graph.append((o.id, o.parents))
438
heads.extend([p for p in o.parents if p not in checked])
439
elif isinstance(o, Tag):
440
if o.object[1] not in checked:
441
heads.append(o.object[1])
443
trace.warning("Unable to import head object %r" % o)
446
# Order the revisions
126
447
# 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)
449
revision_ids = topo_sort(graph)
451
if limit is not None:
452
revision_ids = revision_ids[:limit]
454
for offset in range(0, len(revision_ids), batch_size):
455
target_git_object_retriever.start_write_group()
457
repo.start_write_group()
459
for i, head in enumerate(
460
revision_ids[offset:offset+batch_size]):
462
pb.update("fetching revisions", offset+i,
464
import_git_commit(repo, mapping, head, lookup_object,
465
target_git_object_retriever, trees_cache)
468
repo.abort_write_group()
471
hint = repo.commit_write_group()
473
pack_hints.extend(hint)
475
target_git_object_retriever.abort_write_group()
478
target_git_object_retriever.commit_write_group()
479
return pack_hints, last_imported
159
482
class InterGitRepository(InterRepository):
161
_matching_repo_format = GitFormat()
484
_matching_repo_format = GitRepositoryFormat()
164
487
def _get_repo_format_to_test():
168
491
"""See InterRepository.copy_content."""
169
492
self.fetch(revision_id, pb, find_ghosts=False)
171
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
495
class InterGitNonGitRepository(InterGitRepository):
496
"""Base InterRepository that copies revisions from a Git into a non-Git
499
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
500
"""Fetch objects from a remote server.
502
:param determine_wants: determine_wants callback
503
:param mapping: BzrGitMapping to use
504
:param pb: Optional progress bar
505
:param limit: Maximum number of commits to import.
506
:return: Tuple with pack hint, last imported revision id and remote refs
508
raise NotImplementedError(self.fetch_objects)
510
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
511
mapping=None, fetch_spec=None):
173
512
if mapping is None:
174
513
mapping = self.source.get_mapping()
514
if revision_id is not None:
515
interesting_heads = [revision_id]
516
elif fetch_spec is not None:
517
interesting_heads = fetch_spec.heads
519
interesting_heads = None
520
def determine_wants(refs):
521
if interesting_heads is None:
522
ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
524
ret = [self.source.lookup_bzr_revision_id(revid)[0] for revid in interesting_heads if revid not in (None, NULL_REVISION)]
525
return [rev for rev in ret if not self.target.has_revision(self.source.lookup_foreign_revision_id(rev))]
526
(pack_hint, _, remote_refs) = self.fetch_objects(determine_wants, mapping, pb)
527
if pack_hint is not None and self.target._format.pack_compresses:
528
self.target.pack(hint=pack_hint)
532
_GIT_PROGRESS_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
533
def report_git_progress(pb, text):
534
text = text.rstrip("\r\n")
535
g = _GIT_PROGRESS_RE.match(text)
537
(text, pct, current, total) = g.groups()
538
pb.update(text, int(current), int(total))
540
pb.update(text, 0, 0)
543
class DetermineWantsRecorder(object):
545
def __init__(self, actual):
548
self.remote_refs = {}
550
def __call__(self, refs):
551
self.remote_refs = refs
552
self.wants = self.actual(refs)
556
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
557
"""InterRepository that copies revisions from a remote Git into a non-Git
560
def get_target_heads(self):
561
# FIXME: This should be more efficient
562
all_revs = self.target.all_revision_ids()
563
parent_map = self.target.get_parent_map(all_revs)
565
map(all_parents.update, parent_map.itervalues())
566
return set(all_revs) - all_parents
568
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
569
"""See `InterGitNonGitRepository`."""
175
570
def progress(text):
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)
571
report_git_progress(pb, text)
572
store = BazaarObjectStore(self.target, mapping)
187
573
self.target.lock_write()
189
import_git_objects(self.target, mapping,
190
self.source.fetch_objects(determine_wants, graph_walker,
575
heads = self.get_target_heads()
576
graph_walker = store.get_graph_walker(
577
[store._lookup_revision_sha1(head) for head in heads])
578
wants_recorder = DetermineWantsRecorder(determine_wants)
582
create_pb = pb = ui.ui_factory.nested_progress_bar()
584
objects_iter = self.source.fetch_objects(
585
wants_recorder, graph_walker, store.get_raw,
587
(pack_hint, last_rev) = import_git_objects(self.target, mapping,
588
objects_iter, store, wants_recorder.wants, pb, limit)
589
return (pack_hint, last_rev, wants_recorder.remote_refs)
193
594
self.target.unlock()
196
597
def is_compatible(source, target):
197
598
"""Be compatible with GitRepository."""
198
# FIXME: Also check target uses VersionedFile
199
return (isinstance(source, LocalGitRepository) and
200
target.supports_rich_root())
599
return (isinstance(source, RemoteGitRepository) and
600
target.supports_rich_root() and
601
not isinstance(target, GitRepository) and
602
target.texts is not None)
605
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
606
"""InterRepository that copies revisions from a local Git into a non-Git
609
def fetch_objects(self, determine_wants, mapping, pb=None, limit=None):
610
"""See `InterGitNonGitRepository`."""
611
remote_refs = self.source._git.get_refs()
612
wants = determine_wants(remote_refs)
615
create_pb = pb = ui.ui_factory.nested_progress_bar()
616
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
618
self.target.lock_write()
620
(pack_hint, last_rev) = import_git_objects(self.target, mapping,
621
self.source._git.object_store,
622
target_git_object_retriever, wants, pb, limit)
623
return (pack_hint, last_rev, remote_refs)
631
def is_compatible(source, target):
632
"""Be compatible with GitRepository."""
633
return (isinstance(source, LocalGitRepository) and
634
target.supports_rich_root() and
635
not isinstance(target, GitRepository) and
636
target.texts is not None)
639
class InterGitGitRepository(InterGitRepository):
640
"""InterRepository that copies between Git repositories."""
642
def fetch_objects(self, determine_wants, mapping, pb=None):
644
trace.note("git: %s", text)
645
graphwalker = self.target._git.get_graph_walker()
646
if (isinstance(self.source, LocalGitRepository) and
647
isinstance(self.target, LocalGitRepository)):
648
refs = self.source._git.fetch(self.target._git, determine_wants,
650
return (None, None, refs)
651
elif (isinstance(self.source, LocalGitRepository) and
652
isinstance(self.target, RemoteGitRepository)):
653
raise NotImplementedError
654
elif (isinstance(self.source, RemoteGitRepository) and
655
isinstance(self.target, LocalGitRepository)):
656
f, commit = self.target._git.object_store.add_thin_pack()
658
refs = self.source.bzrdir.root_transport.fetch_pack(
659
determine_wants, graphwalker, f.write, progress)
661
return (None, None, refs)
668
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
669
mapping=None, fetch_spec=None, branches=None):
671
mapping = self.source.get_mapping()
673
if revision_id is not None:
674
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
675
elif fetch_spec is not None:
676
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads]
677
if branches is not None:
678
determine_wants = lambda x: [x[y] for y in branches if not x[y] in r.object_store]
679
elif fetch_spec is None and revision_id is None:
680
determine_wants = r.object_store.determine_wants_all
682
determine_wants = lambda x: [y for y in args if not y in r.object_store]
683
self.fetch_objects(determine_wants, mapping)
686
def is_compatible(source, target):
687
"""Be compatible with GitRepository."""
688
return (isinstance(source, GitRepository) and
689
isinstance(target, GitRepository))