210
210
self.inconsistent_stored_lhs_parent = 0
211
211
self.invalid_fileprop_cnt = 0
212
212
self.text_revision_in_parents_cnt = 0
213
self.text_not_changed_cnt = 0
214
213
self.paths_not_under_branch_root = 0
215
214
self.different_uuid_cnt = 0
216
215
self.multiple_mappings_cnt = 0
230
229
if self.hidden_rev_cnt > 0:
231
230
note('%6d hidden bzr-created revisions', self.hidden_rev_cnt)
232
231
if self.inconsistent_stored_lhs_parent > 0:
233
note('%6d inconsistent stored left-hand side parent revision ids',
232
note('%6d inconsistent stored left-hand side parent revision ids',
234
233
self.inconsistent_stored_lhs_parent)
235
234
if self.invalid_fileprop_cnt > 0:
236
note('%6d invalid bzr-related file properties',
235
note('%6d invalid bzr-related file properties',
237
236
self.invalid_fileprop_cnt)
238
if self.text_not_changed_cnt > 0:
239
note('%6d files were not changed but had their revision/fileid changed',
240
self.text_not_changed_cnt)
241
237
if self.paths_not_under_branch_root > 0:
242
238
note('%6d files were modified that were not under the branch root',
243
239
self.paths_not_under_branch_root)
276
272
mapping = revmeta.get_original_mapping()
277
273
if mapping is None:
279
if (len(fileprop_mappings) > 0 and
280
find_mapping_revprops(revmeta.get_revprops()) not in
275
if (len(fileprop_mappings) > 0 and
276
find_mapping_revprops(revmeta.get_revprops()) not in
281
277
(None, fileprop_mappings[0])):
282
278
self.inconsistent_fileprop_revprop_cnt += 1
283
279
self.checked_roundtripped_cnt += 1
320
316
def check_texts(self, revmeta, mapping):
321
317
# Check for inconsistencies in text file ids/revisions
322
318
text_revisions = revmeta.get_text_revisions(mapping)
323
text_parents = revmeta.get_text_parents(mapping)
324
319
text_ids = revmeta.get_fileid_overrides(mapping)
325
320
fileid_map = self.repository.get_fileid_map(revmeta, mapping)
326
321
path_changes = revmeta.get_paths()
327
for path in set(text_ids.keys() + text_revisions.keys() + text_parents.keys()):
328
if (path in text_revisions and
329
text_revisions[path] in text_parents.get(path, [])):
330
self.text_revision_in_parents_cnt += 1
322
for path in set(text_ids.keys() + text_revisions.keys()):
331
323
full_path = urlutils.join(revmeta.branch_path, path)
332
if not full_path in path_changes:
333
mutter('in %s text %r/%r (%r) id changed but not changed',
334
revmeta.get_revision_id(mapping),
335
text_ids.get(path), text_revisions.get(path),
336
text_parents.get(path))
337
self.text_not_changed_cnt += 1
339
# TODO Check consistency against parent data
340
for path, tps in text_parents.iteritems():
341
if len(tps) > len(revmeta.get_parent_ids(mapping)):
342
self.invalid_text_parents_len += 1
324
# TODO Check consistency against parent data
343
325
ghost_parents = False
344
326
parent_revmetas = []
345
327
parent_mappings = []
355
337
parent_fileid_map = self.repository.get_fileid_map(parent_revmeta, parent_mapping)
356
338
parent_fileid_maps.append(parent_fileid_map)
357
339
for path, text_revision in text_revisions.iteritems():
358
# Every text revision either has to match the actual revision's
359
# revision id (if it was last changed there) or the text revisions
340
# Every text revision either has to match the actual revision's
341
# revision id (if it was last changed there) or the text revisions
360
342
# in one of the parents.
361
343
fileid = fileid_map.lookup(mapping, path)[0]
362
344
parent_text_revisions = []
363
345
for parent_fileid_map, parent_mapping in zip(parent_fileid_maps, parent_mappings):
364
346
parent_text_revisions.append(parent_fileid_map.reverse_lookup(parent_mapping, fileid))
365
if (text_revision != revmeta.get_revision_id(mapping) and
347
if (text_revision != revmeta.get_revision_id(mapping) and
366
348
not ghost_parents and not text_revision in parent_text_revisions):
367
349
self.invalid_text_revisions += 1
370
352
class SvnRepository(ForeignRepository):
372
Provides a simplified interface to a Subversion repository
354
Provides a simplified interface to a Subversion repository
373
355
by using the RA (remote access) API from subversion
375
357
def __init__(self, bzrdir, transport, branch_path=None):
409
391
use_cache = self.get_config().get_use_cache()
411
393
if use_cache is None:
412
# TODO: Don't enable log cache in some situations, e.g.
394
# TODO: Don't enable log cache in some situations, e.g.
413
395
# for large repositories ?
414
396
if self.base.startswith("file://"):
415
397
# Default to no log caching for local connections
450
432
self.branchprop_list = PathPropertyProvider(self._log)
452
self._revmeta_provider = revmeta.RevisionMetadataProvider(self,
434
self._revmeta_provider = revmeta.RevisionMetadataProvider(self,
454
436
self.transport.has_capability("commit-revprops") in (True, None))
524
506
for fileid, altered_versions in fileids.iteritems():
525
507
yield ("file", fileid, altered_versions)
527
509
# We're done with the files_pb. Note that it finished by the caller,
528
510
# just as it was created by the caller.
549
531
return parse_svn_dateprop(self._log.revprop_list(revnum)[subvertpy.properties.PROP_REVISION_DATE])
550
532
if committers is not None and revid is not None:
551
533
all_committers = set()
552
for rev in self.get_revisions(filter(lambda r: r is not None and r != NULL_REVISION, self.get_ancestry(revid))):
534
for rev in self.get_revisions(filter(lambda r: r is not None and r != NULL_REVISION, self.has_revisions(self.get_ancestry(revid)))):
553
535
if rev.committer != '':
554
536
all_committers.add(rev.committer)
555
537
result['committers'] = len(all_committers)
568
550
return mapping_registry.get_default()
570
552
def _properties_to_set(self, mapping):
571
"""Determine what sort of custom properties to set when
553
"""Determine what sort of custom properties to set when
572
554
committing a new round-tripped revision.
574
:return: tuple with two booleans: whether to use revision properties
556
:return: tuple with two booleans: whether to use revision properties
575
557
and whether to use file properties.
577
559
supports_custom_revprops = self.transport.has_capability("commit-revprops")
614
596
parent_branch_path = parentrevmeta.branch_path
615
597
parentrevnum = parentrevmeta.revnum
616
598
start_empty = False
617
editor = TreeDeltaBuildEditor(revision.svn_meta, revision.mapping,
618
self.get_fileid_map(revision.svn_meta, revision.mapping),
599
editor = TreeDeltaBuildEditor(revision.svn_meta, revision.mapping,
600
self.get_fileid_map(revision.svn_meta, revision.mapping),
620
602
conn = self.transport.get_connection(parent_branch_path)
651
633
def get_layout(self):
652
634
"""Determine layout to use for this repository.
654
This will use whatever layout the user has specified, or
636
This will use whatever layout the user has specified, or
655
637
otherwise the layout that was guessed by bzr-svn.
657
639
return self.get_layout_source()[0]
682
664
return self._layout, self._layout_source
684
666
def _find_guessed_layout(self):
685
# TODO: Retrieve guessed-layout from config and see if it accepts self._hinted_branch_path
667
# Retrieve guessed-layout from config and see if it accepts self._hinted_branch_path
686
668
layoutname = self.get_config().get_guessed_layout()
687
669
if layoutname is not None:
688
670
config_guessed_layout = layout.layout_registry.get(layoutname)()
689
if (self._hinted_branch_path is None or
671
if (self._hinted_branch_path is None or
690
672
config_guessed_layout.is_branch(self._hinted_branch_path)):
691
673
self._guessed_layout = config_guessed_layout
692
674
self._guessed_appropriate_layout = config_guessed_layout
695
677
config_guessed_layout = None
678
# No guessed layout stored yet or it doesn't accept self._hinted_branch_path
696
679
revnum = self.get_latest_revnum()
697
(self._guessed_layout, self._guessed_appropriate_layout) = repository_guess_layout(self,
680
(self._guessed_layout, self._guessed_appropriate_layout) = repository_guess_layout(self,
698
681
revnum, self._hinted_branch_path)
699
682
if self._guessed_layout != config_guessed_layout and revnum > 200:
700
683
self.get_config().set_guessed_layout(self._guessed_layout)
741
724
assert revision_id != None
742
725
return self.revision_tree(revision_id).inventory
744
def _iter_inventories(self, revision_ids):
727
def _iter_inventories(self, revision_ids, ordering):
745
728
for revid in revision_ids:
746
729
yield self.get_inventory(revid)
749
732
return self.fileid_map.get_map(revmeta.get_foreign_revid(), mapping)
751
734
def all_revision_ids(self, layout=None, mapping=None):
752
"""Find all revision ids in this repository, using the specified or
735
"""Find all revision ids in this repository, using the specified or
755
:note: This will use the standard layout to find the revisions,
756
any revisions using non-standard branch locations (even
757
if part of the ancestry of valid revisions) won't be
738
:note: This will use the standard layout to find the revisions,
739
any revisions using non-standard branch locations (even
740
if part of the ancestry of valid revisions) won't be
760
743
if mapping is None:
769
752
def set_make_working_trees(self, new_value):
770
753
"""See Repository.set_make_working_trees()."""
771
pass # ignored, nowhere to store it...
754
pass # ignored, nowhere to store it...
773
756
def make_working_trees(self):
774
757
"""See Repository.make_working_trees().
776
Always returns False, as working trees are never created inside
759
Always returns False, as working trees are never created inside
777
760
Subversion repositories.
874
857
ancestry.reverse()
860
def get_known_graph_ancestry(self, keys):
861
"""Get a KnownGraph instance with the ancestry of keys."""
862
# most basic implementation is a loop around get_parent_map
866
this_parent_map = self.get_parent_map(pending)
867
parent_map.update(this_parent_map)
869
map(pending.update, this_parent_map.itervalues())
870
pending = pending.difference(parent_map)
871
kg = graph.KnownGraph(parent_map)
877
874
def has_revisions(self, revision_ids):
879
876
for revision_id in revision_ids:
942
939
raise bzr_errors.InvalidRevisionId(revision_id=revision_id, branch=self)
944
941
revmeta, mapping = self._get_revmeta(revision_id)
946
943
return revmeta.get_revision(mapping)
948
945
def get_revisions(self, revision_ids):
972
969
return revmeta.get_revision_id(mapping)
974
971
def generate_revision_id(self, revnum, path, mapping):
975
"""Generate an unambiguous revision id.
972
"""Generate an unambiguous revision id.
977
974
:param revnum: Subversion revision number.
978
975
:param path: Branch path.
979
976
:param mapping: Mapping to use.
984
981
assert isinstance(revnum, int)
985
982
return self.lookup_foreign_revision_id((self.uuid, path, revnum), mapping)
987
def lookup_bzr_revision_id(self, revid, layout=None, ancestry=None,
984
def lookup_bzr_revision_id(self, revid, layout=None, ancestry=None,
988
985
project=None, foreign_sibling=None):
989
986
"""Parse an existing Subversion-based revision id.
991
988
:param revid: The revision id.
992
:param layout: Optional repository layout to use when searching for
989
:param layout: Optional repository layout to use when searching for
994
991
:raises: NoSuchRevision
995
992
:return: Tuple with foreign revision id and mapping.
1004
1001
if uuid == self.uuid:
1005
1002
return (self.uuid, branch_path, revnum), mapping
1006
1003
# If the UUID doesn't match, this may still be a valid revision
1007
# id; a revision from another SVN repository may be pushed into
1004
# id; a revision from another SVN repository may be pushed into
1009
1006
except bzr_errors.InvalidRevisionId:
1019
1016
return self.revmap.get_branch_revnum(revid, layout, project)
1021
1018
def seen_bzr_revprops(self):
1022
"""Check whether bzr-specific custom revision properties are present on this
1019
"""Check whether bzr-specific custom revision properties are present on this
1039
1036
"""Check whether a signature exists for a particular revision id.
1041
1038
:param revision_id: Revision id for which the signatures should be looked up.
1042
:return: False, as no signatures are stored for revisions in Subversion
1039
:return: False, as no signatures are stored for revisions in Subversion
1053
1050
def get_signature_text(self, revision_id):
1054
1051
"""Return the signature text for a particular revision.
1056
:param revision_id: Id of the revision for which to return the
1053
:param revision_id: Id of the revision for which to return the
1058
1055
:raises NoSuchRevision: Always
1080
1077
This will include branches inside other branches.
1082
1079
from bzrlib.plugins.svn.branch import SvnBranch # avoid circular imports
1083
# All branches use this repository, so the using argument can be
1080
# All branches use this repository, so the using argument can be
1085
1082
if layout is None:
1086
1083
layout = self.get_layout()
1097
1094
return branches
1099
1096
@needs_read_lock
1100
def find_tags_between(self, project, layout, mapping, from_revnum,
1097
def find_tags_between(self, project, layout, mapping, from_revnum,
1101
1098
to_revnum, tags=None):
1102
1099
if tags is None:
1157
1154
if not layout in self._cached_tags:
1158
1155
self._cached_tags[layout] = self.find_tags_between(project=project,
1159
layout=layout, mapping=mapping, from_revnum=0,
1156
layout=layout, mapping=mapping, from_revnum=0,
1160
1157
to_revnum=revnum)
1161
1158
return self._cached_tags[layout]
1163
1160
def find_branchpaths(self, layout, from_revnum, to_revnum, project=None):
1164
"""Find all branch paths that were changed in the specified revision
1161
"""Find all branch paths that were changed in the specified revision
1167
1164
:note: This ignores forbidden paths.
1169
1166
:param revnum: Revision to search for branches.
1170
:return: iterator that returns tuples with (path, revision number,
1171
still exists). The revision number is the revision in which the
1167
:return: iterator that returns tuples with (path, revision number,
1168
still exists). The revision number is the revision in which the
1172
1169
branch last existed.
1174
1171
assert from_revnum >= to_revnum
1196
1193
ret.append((p, created_branches[p], False))
1197
1194
del created_branches[p]
1199
if paths[p][0] in ('A', 'R', 'M'):
1196
if paths[p][0] in ('A', 'R', 'M'):
1200
1197
created_branches[p] = i
1201
1198
elif layout.is_branch_or_tag_parent(p, project):
1202
1199
if paths[p][0] in ('R', 'D'):
1205
1202
if c.startswith(p+"/") and c in created_branches:
1206
1203
ret.append((c, created_branches[c], False))
1207
del created_branches[c]
1204
del created_branches[c]
1208
1205
if paths[p][0] in ('A', 'R') and paths[p][1] is not None:
1237
1234
"""See Repository.get_physical_lock_status()."""
1240
def get_commit_builder(self, branch, parents, config, timestamp=None,
1241
timezone=None, committer=None, revprops=None,
1237
def get_commit_builder(self, branch, parents, config, timestamp=None,
1238
timezone=None, committer=None, revprops=None,
1242
1239
revision_id=None):
1243
1240
"""See Repository.get_commit_builder()."""
1244
1241
from bzrlib.plugins.svn.commit import SvnCommitBuilder
1252
1249
base_foreign_revid, base_mapping = \
1253
1250
self.lookup_bzr_revision_id(parents[0], project=branch.project)
1254
return SvnCommitBuilder(self, branch.get_branch_path(), parents,
1255
config, timestamp, timezone, committer,
1256
revprops, revision_id,
1251
return SvnCommitBuilder(self, branch.get_branch_path(), parents,
1252
config, timestamp, timezone, committer,
1253
revprops, revision_id,
1257
1254
base_foreign_revid, base_mapping,
1258
1255
append_revisions_only=append_revisions_only)
1260
def find_fileprop_paths(self, layout, from_revnum, to_revnum,
1257
def find_fileprop_paths(self, layout, from_revnum, to_revnum,
1261
1258
project=None, check_removed=False):
1262
1259
assert from_revnum >= to_revnum
1263
1260
if not check_removed and to_revnum == 0: