68
class RepoReconciler(object):
69
"""Reconciler that reconciles a repository.
73
def __init__(self, repo, other=None, thorough=False):
74
"""Construct a RepoReconciler.
76
:param thorough: perform a thorough check which may take longer but
77
will correct non-data loss issues such as incorrect
83
"""Perform reconciliation.
85
After reconciliation the following attributes document found issues:
86
inconsistent_parents: The number of revisions in the repository whose
87
ancestry was being reported incorrectly.
88
garbage_inventories: The number of inventory objects without revisions
89
that were garbage collected.
71
93
class GitCheck(check.Check):
73
95
def __init__(self, repository, check_repo=True):
80
102
def check(self, callback_refs=None, check_repo=True):
81
103
if callback_refs is None:
82
104
callback_refs = {}
83
with self.repository.lock_read(), \
84
ui.ui_factory.nested_progress_bar() as self.progress:
105
with self.repository.lock_read(), ui.ui_factory.nested_progress_bar() as self.progress:
85
106
shas = set(self.repository._git.object_store)
86
107
self.object_count = len(shas)
87
108
# TODO(jelmer): Check more things
135
155
def __init__(self, gitdir):
136
156
self._transport = gitdir.root_transport
137
157
super(GitRepository, self).__init__(GitRepositoryFormat(),
138
gitdir, control_files=None)
158
gitdir, control_files=None)
139
159
self.base = gitdir.root_transport.base
140
160
lazy_load_optimisers()
141
161
self._lock_mode = None
144
164
def add_fallback_repository(self, basis_url):
145
165
raise errors.UnstackableRepositoryFormat(self._format,
146
self.control_transport.base)
166
self.control_transport.base)
148
168
def is_shared(self):
217
237
def reconcile(self, other=None, thorough=False):
218
238
"""Reconcile this repository."""
219
from ..reconcile import ReconcileResult
220
ret = ReconcileResult()
239
reconciler = RepoReconciler(self, thorough=thorough)
240
reconciler.reconcile()
224
243
def supports_rich_root(self):
265
284
:param lossy: Whether to discard data that can not be natively
266
285
represented, when pushing to a foreign VCS
268
builder = GitCommitBuilder(
269
self, parents, config, timestamp, timezone, committer, revprops,
287
builder = GitCommitBuilder(self, parents, config,
288
timestamp, timezone, committer, revprops, revision_id,
271
290
self.start_write_group()
298
317
for (file_id, revision_id, identifier) in desired_files:
299
318
per_revision.setdefault(revision_id, []).append(
300
319
(file_id, identifier))
301
for revid, files in viewitems(per_revision):
320
for revid, files in per_revision.iteritems():
303
322
(commit_id, mapping) = self.lookup_bzr_revision_id(revid)
304
323
except errors.NoSuchRevision:
315
334
raise errors.RevisionNotPresent((fileid, revid), self)
317
336
obj = tree_lookup_path(
318
self._git.object_store.__getitem__, root_tree,
319
path.encode('utf-8'))
337
self._git.object_store.__getitem__, root_tree, path)
320
338
if isinstance(obj, tuple):
321
339
(mode, item_id) = obj
322
340
obj = self._git.object_store[item_id]
324
342
raise errors.RevisionNotPresent((fileid, revid), self)
326
if obj.type_name == b"tree":
344
if obj.type_name == "tree":
327
345
yield (identifier, [])
328
elif obj.type_name == b"blob":
346
elif obj.type_name == "blob":
329
347
yield (identifier, obj.chunked)
331
349
raise AssertionError("file text resolved to %r" % obj)
333
351
def gather_stats(self, revid=None, committers=None):
334
352
"""See Repository.gather_stats()."""
335
result = super(LocalGitRepository, self).gather_stats(
353
result = super(LocalGitRepository, self).gather_stats(revid, committers)
338
355
for sha in self._git.object_store:
339
356
o = self._git.object_store[sha]
340
if o.type_name == b"commit":
357
if o.type_name == "commit":
341
358
revs.append(o.id)
342
359
result['revisions'] = len(revs)
348
365
o = self._git.object_store[sha]
349
366
if not isinstance(o, Commit):
351
revid = mapping.revision_id_foreign_to_bzr(o.id)
368
rev, roundtrip_revid, verifiers = mapping.import_commit(o,
369
mapping.revision_id_foreign_to_bzr)
370
yield o.id, rev.revision_id, roundtrip_revid
354
372
def all_revision_ids(self):
356
for git_sha, revid in self._iter_revision_ids():
374
for git_sha, revid, roundtrip_revid in self._iter_revision_ids():
376
ret.add(roundtrip_revid)
360
381
def _get_parents(self, revid, no_alternates=False):
383
404
def get_parent_map(self, revids, no_alternates=False):
385
406
for revision_id in revids:
386
parents = self._get_parents(
387
revision_id, no_alternates=no_alternates)
407
parents = self._get_parents(revision_id, no_alternates=no_alternates)
388
408
if revision_id == _mod_revision.NULL_REVISION:
389
409
parent_map[revision_id] = ()
410
430
this_parent_map[revid] = parents
411
431
parent_map.update(this_parent_map)
413
for values in viewvalues(this_parent_map):
414
pending.update(values)
433
map(pending.update, this_parent_map.itervalues())
415
434
pending = pending.difference(parent_map)
416
435
return _mod_graph.KnownGraph(parent_map)
441
460
:raise KeyError: If foreign revision was not found
442
461
:return: bzr revision id
444
if not isinstance(foreign_revid, bytes):
463
if type(foreign_revid) is not str:
445
464
raise TypeError(foreign_revid)
446
465
if mapping is None:
447
466
mapping = self.get_mapping()
450
469
commit = self._git.object_store.peel_sha(foreign_revid)
451
470
if not isinstance(commit, Commit):
452
471
raise NotCommitError(commit.id)
453
revid = mapping.get_revision_id(commit)
472
rev, roundtrip_revid, verifiers = mapping.import_commit(commit,
473
mapping.revision_id_foreign_to_bzr)
454
474
# FIXME: check testament before doing this?
476
return roundtrip_revid
478
return rev.revision_id
457
480
def has_signature_for_revision_id(self, revision_id):
458
481
"""Check whether a GPG signature is present for this revision.
488
511
without_sig = Commit.from_string(commit.as_raw_string())
489
512
without_sig.gpgsig = None
491
(result, key, plain_text) = gpg_strategy.verify(
492
without_sig.as_raw_string(), commit.gpgsig)
514
(result, key, plain_text) = gpg_strategy.verify(without_sig.as_raw_string(), commit.gpgsig)
493
515
return (result, key)
495
517
def lookup_bzr_revision_id(self, bzr_revid, mapping=None):
504
(git_sha, mapping) = mapping_registry.revision_id_bzr_to_foreign(
526
(git_sha, mapping) = mapping_registry.revision_id_bzr_to_foreign(bzr_revid)
506
527
except errors.InvalidRevisionId:
507
raise errors.NoSuchRevision(self, bzr_revid)
529
mapping = self.get_mapping()
531
return (self._git.refs[mapping.revid_as_refname(bzr_revid)],
534
# Update refs from Git commit objects
535
# FIXME: Hitting this a lot will be very inefficient...
536
pb = ui.ui_factory.nested_progress_bar()
538
for i, (git_sha, revid, roundtrip_revid) in enumerate(self._iter_revision_ids()):
539
if not roundtrip_revid:
541
pb.update("resolving revision id", i)
542
refname = mapping.revid_as_refname(roundtrip_revid)
543
self._git.refs[refname] = git_sha
544
if roundtrip_revid == bzr_revid:
545
return git_sha, mapping
548
raise errors.NoSuchRevision(self, bzr_revid)
509
550
return (git_sha, mapping)
511
552
def get_revision(self, revision_id):
512
if not isinstance(revision_id, bytes):
553
if not isinstance(revision_id, str):
513
554
raise errors.InvalidRevisionId(revision_id, self)
514
555
git_commit_id, mapping = self.lookup_bzr_revision_id(revision_id)
518
559
raise errors.NoSuchRevision(self, revision_id)
519
560
revision, roundtrip_revid, verifiers = mapping.import_commit(
520
commit, self.lookup_foreign_revision_id, strict=False)
561
commit, self.lookup_foreign_revision_id)
521
562
if revision is None:
522
563
raise AssertionError
523
564
# FIXME: check verifiers ?
577
618
required_trees.update(revision.parent_ids[:1])
579
620
trees = dict((t.get_revision_id(), t) for
580
t in self.revision_trees(required_trees))
621
t in self.revision_trees(required_trees))
582
623
# Calculate the deltas
583
624
for revision in revisions:
587
628
old_tree = trees[revision.parent_ids[0]]
588
629
new_tree = trees[revision.revision_id]
589
630
if specific_fileids is not None:
590
specific_files = [new_tree.id2path(
591
fid) for fid in specific_fileids]
631
specific_files = [new_tree.id2path(fid) for fid in specific_fileids]
593
633
specific_files = None
594
yield new_tree.changes_from(
595
old_tree, specific_files=specific_files)
634
yield new_tree.changes_from(old_tree, specific_files=specific_files)
597
636
def set_make_working_trees(self, trees):
598
637
raise errors.UnsupportedOperation(self.set_make_working_trees, self)
600
639
def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref,
601
progress=None, limit=None):
640
progress=None, limit=None):
602
641
return self._git.fetch_objects(determine_wants, graph_walker, progress,
606
645
class GitRepositoryFormat(repository.RepositoryFormat):
654
693
def get_extra_interrepo_test_combinations():
655
from ..bzr.groupcompress_repo import RepositoryFormat2a
694
from ...bzr.groupcompress_repo import RepositoryFormat2a
656
695
from . import interrepo
658
(interrepo.InterLocalGitNonGitRepository,
659
GitRepositoryFormat(), RepositoryFormat2a()),
660
(interrepo.InterLocalGitLocalGitRepository,
661
GitRepositoryFormat(), GitRepositoryFormat()),
662
(interrepo.InterToLocalGitRepository,
663
RepositoryFormat2a(), GitRepositoryFormat()),
697
(interrepo.InterLocalGitNonGitRepository, GitRepositoryFormat(), RepositoryFormat2a()),
698
(interrepo.InterLocalGitLocalGitRepository, GitRepositoryFormat(), GitRepositoryFormat()),
699
(interrepo.InterToLocalGitRepository, RepositoryFormat2a(), GitRepositoryFormat()),