124
125
to_file.write('No revisions to pull.\n')
126
127
if self.new_revmeta is None:
127
self.new_revmeta, _ = self.source_branch.repository._get_revmeta(self.new_revid)
128
to_file.write('Now on revision %d (svn revno: %d).\n' %
129
(self.new_revno, self.new_revmeta.metarev.revnum))
128
self.new_revmeta, _ = (
129
self.source_branch.repository._get_revmeta(
132
'Now on revision %d (svn revno: %d).\n' %
133
(self.new_revno, self.new_revmeta.metarev.revnum))
130
134
self._show_tag_conficts(to_file)
186
190
"""Maps to a Branch in a Subversion repository """
188
192
def __init__(self, repository, controldir, branch_path, mapping,
189
revnum=None, project=None, _skip_check=False):
193
revnum=None, project=None, _skip_check=False):
190
194
"""Instantiate a new SvnBranch.
192
196
:param repository: SvnRepository this branch is part of.
204
208
if not isinstance(branch_path, text_type):
205
209
raise TypeError(branch_path)
206
210
self._branch_path = branch_path.strip(u"/")
207
self.base = urlutils.join(self.repository.base, urlutils.escape(self._branch_path)).rstrip("/")
211
self.base = urlutils.join(
212
self.repository.base, urlutils.escape(self._branch_path)
208
214
super(SvnBranch, self).__init__(mapping)
209
215
self._lock_mode = None
210
216
self._lock_count = 0
346
352
:return: Tuple with foreign revision id and mapping
347
353
:raises NoSuchRevision: If the revision id was not found.
349
return self.repository.lookup_bzr_revision_id(revid,
350
ancestry=(self.get_branch_path(), self.get_revnum()),
355
return self.repository.lookup_bzr_revision_id(
356
revid, ancestry=(self.get_branch_path(), self.get_revnum()),
351
357
project=self.project)
353
359
def _create_lightweight_checkout(self, to_location, revision_id=None):
373
379
transport = get_transport(to_location)
374
380
transport.ensure_base()
375
381
to_path = transport.local_abspath(".")
376
svn_url, readonly = bzr_to_svn_url(urlutils.join(self.repository.base, bp))
382
svn_url, readonly = bzr_to_svn_url(
383
urlutils.join(self.repository.base, bp))
377
384
wc.ensure_adm(to_path.encode("utf-8"), uuid,
378
385
svn_url, bzr_to_svn_url(self.repository.base)[0], revnum)
379
386
with wc.Adm(None, to_path.encode("utf-8"), write_lock=True) as adm:
482
489
rev = self.repository.get_revision(revid)
483
490
except NoSuchRevision:
484
raise NotImplementedError("set_last_revision_info can't add ghosts")
491
raise NotImplementedError(
492
"set_last_revision_info can't add ghosts")
485
493
interrepo = InterToSvnRepository(self.repository, self.repository)
487
495
base_revid = rev.parent_ids[0]
488
496
except IndexError:
489
497
base_foreign_info = None, None
491
base_foreign_info = self.lookup_bzr_revision_id(rev.parent_ids[0])
499
base_foreign_info = self.lookup_bzr_revision_id(
492
501
interrepo.push_single_revision(
493
502
self.get_branch_path(), self.get_config_stack(), rev,
494
503
push_metadata=True, root_action=("replace", self.get_revnum()),
558
assert revmeta.get_revno(mapping) == revno, "Expected %d, was (%r,%r) %d" % (revno, revmeta, mapping, revmeta.get_revno(mapping))
567
if revmeta.get_revno(mapping) != revno:
568
raise AssertionError(
569
"Expected %d, was (%r,%r) %d" % (
570
revno, revmeta, mapping,
571
revmeta.get_revno(mapping)))
559
572
return revmeta.get_revision_id(mapping)
561
574
raise AssertionError
563
576
def _gen_revision_history(self):
564
577
"""Generate the revision history from last revision."""
565
history = [revmeta.get_revision_id(mapping) for revmeta, hidden, mapping in self._revision_meta_history() if not hidden]
579
revmeta.get_revision_id(mapping)
580
for revmeta, hidden, mapping in self._revision_meta_history()
566
582
history.reverse()
569
585
def last_revision(self):
570
586
"""See Branch.last_revision()."""
571
587
with self.lock_read():
572
# Shortcut for finding the tip. This avoids expensive generation time
588
# Shortcut for finding the tip. This avoids expensive generation
589
# time on large branches.
574
590
if self._cached_last_revid is not None:
575
591
return self._cached_last_revid
576
592
last_revmeta, mapping = self.last_revmeta(skip_hidden=True)
587
603
return (self.layout.push_merged_revisions(self.project) and
588
604
self.get_config_stack().get('push_merged_revisions'))
590
def import_last_revision_info(self, source_repo, revno, revid, lossy=False):
606
def import_last_revision_info(
607
self, source_repo, revno, revid, lossy=False):
591
608
interrepo = InterToSvnRepository(source_repo, self.repository)
592
609
base_revmeta, base_mapping = self.last_revmeta(skip_hidden=False)
593
revidmap = interrepo.push_todo(self.last_revision(),
594
base_revmeta.metarev.get_foreign_revid(),
595
base_mapping, revid, self.layout,
596
self.project, self.get_branch_path(), self.get_config_stack(),
597
push_merged=self.get_push_merged_revisions(),
598
overwrite=False, push_metadata=not lossy,
599
append_revisions_only=True)
610
revidmap = interrepo.push_todo(
611
self.last_revision(), base_revmeta.metarev.get_foreign_revid(),
612
base_mapping, revid, self.layout, self.project,
613
self.get_branch_path(), self.get_config_stack(),
614
push_merged=self.get_push_merged_revisions(), overwrite=False,
615
push_metadata=not lossy, append_revisions_only=True)
600
616
return (revno, revidmap[revid][0])
602
def import_last_revision_info_and_tags(self, source, revno, revid,
604
(revno, revid) = self.import_last_revision_info(source.repository,
605
revno, revid, lossy=lossy)
618
def import_last_revision_info_and_tags(
619
self, source, revno, revid, lossy=False):
620
(revno, revid) = self.import_last_revision_info(
621
source.repository, revno, revid, lossy=lossy)
606
622
self.tags.merge_to(source.tags, overwrite=False)
607
623
return (revno, revid)
609
def generate_revision_history(self, revision_id, last_rev=None,
625
def generate_revision_history(
626
self, revision_id, last_rev=None, other_branch=None):
611
627
"""Create a new revision history that will finish with revision_id.
613
629
:param revision_id: the new tip to use.
711
727
return InterBranch.get(self, target)._basic_push(
712
728
overwrite, stop_revision)
730
def reconcile(self, thorough=True):
731
"""Make sure the data stored in this branch is consistent.
733
:return: A `ReconcileResult` object.
735
from ...reconcile import ReconcileResult
736
return ReconcileResult()
715
739
class SvnBranchFormat(BranchFormat):
716
740
"""Branch format for Subversion Branches."""
781
805
return [(SvnBranchFormat(), branch_format_registry.get_default())]
783
807
def fetch(self, stop_revision=None, fetch_tags=None, find_ghosts=False,
784
limit=None, exclude_non_mainline=None):
808
limit=None, exclude_non_mainline=None, lossy=False):
785
809
"""See InterBranch.fetch."""
786
810
# we fetch here so that we don't process data twice in the
787
811
# common case of having something to pull, and so that the
806
830
d = resolve_tags_svn_ancestry(self.source, tag_revmetas)
807
831
for name, (revmeta, mapping, revid) in d.items():
808
832
todo.append((revmeta, mapping))
809
self._fetch_revmetas(todo, find_ghosts=find_ghosts, limit=limit,
810
exclude_non_mainline=exclude_non_mainline)
833
self._fetch_revmetas(
834
todo, find_ghosts=find_ghosts, limit=limit,
835
exclude_non_mainline=exclude_non_mainline)
812
def _fetch_revmetas(self, revmetas, find_ghosts=False, limit=None,
838
self, revmetas, find_ghosts=False, limit=None,
813
839
exclude_non_mainline=None):
814
interrepo = InterFromSvnToInventoryRepository(self.source.repository,
815
self.target.repository)
840
interrepo = InterFromSvnToInventoryRepository(
841
self.source.repository, self.target.repository)
816
842
revisionfinder = interrepo.get_revision_finder()
817
843
for revmeta, mapping in revmetas:
818
revisionfinder.find_until(revmeta.metarev.get_foreign_revid(),
819
mapping, find_ghosts=find_ghosts,
844
revisionfinder.find_until(
845
revmeta.metarev.get_foreign_revid(), mapping,
846
find_ghosts=find_ghosts,
820
847
exclude_non_mainline=exclude_non_mainline)
821
interrepo.fetch(needed=revisionfinder.get_missing(limit=limit),
849
needed=revisionfinder.get_missing(limit=limit),
822
850
project=self.source.project, mapping=self.source.mapping)
824
852
def _update_revisions(self, stop_revision=None, overwrite=False,
825
graph=None, fetch_tags=None,
826
fetch_non_mainline=None):
853
graph=None, fetch_tags=None,
854
fetch_non_mainline=None):
827
855
"See InterBranch.update_revisions."""
828
856
with self.source.lock_read():
829
857
if stop_revision is None:
853
881
self.target.generate_revision_history(stop_revision)
854
882
return self.target.last_revision_info()
856
def _basic_push(self, overwrite=False, stop_revision=None,
885
self, overwrite=False, stop_revision=None,
857
886
fetch_non_mainline=False):
858
887
result = BranchPushResult()
859
888
result.source_branch = self.source
879
908
result.tag_conflicts = tag_ret
881
def _basic_pull(self, stop_revision, overwrite, run_hooks,
882
_override_hook_target, _hook_master,
883
fetch_non_mainline=None):
884
self.target.lock_write()
911
self, stop_revision, overwrite, run_hooks,
912
_override_hook_target, _hook_master,
913
fetch_non_mainline=None):
914
with self.target.lock_write():
886
915
result = SubversionSourcePullResult()
887
916
result.source_branch = self.source
888
917
if _override_hook_target is None:
938
965
raise LocalRequiresBoundBranch()
939
966
master_branch = None
940
967
source_is_master = False
941
self.source.lock_read()
943
# bound_location comes from a config file, some care has to be
944
# taken to relate it to source.user_url
945
normalized = urlutils.normalize_url(bound_location)
947
relpath = self.source.user_transport.relpath(normalized)
948
source_is_master = (relpath == '')
949
except (PathNotChild, urlutils.InvalidURL):
950
source_is_master = False
951
if not local and bound_location and not source_is_master:
952
# not pulling from master, so we need to update master.
953
master_branch = self.target.get_master_branch(possible_transports)
954
master_branch.lock_write()
958
# pull from source into master.
959
master_branch.pull(self.source, overwrite, stop_revision,
960
run_hooks=False, fetch_non_mainline=fetch_non_mainline)
961
result = self._basic_pull(stop_revision, overwrite, run_hooks,
962
_override_hook_target, _hook_master=master_branch,
963
fetch_non_mainline=fetch_non_mainline)
968
master_branch.unlock()
968
with cleanup.ExitStack() as es:
969
es.enter_context(self.source.lock_read())
971
# bound_location comes from a config file, some care has to be
972
# taken to relate it to source.user_url
973
normalized = urlutils.normalize_url(bound_location)
975
relpath = self.source.user_transport.relpath(normalized)
976
source_is_master = (relpath == '')
977
except (PathNotChild, urlutils.InvalidURL):
978
source_is_master = False
979
if not local and bound_location and not source_is_master:
980
# not pulling from master, so we need to update master.
981
master_branch = self.target.get_master_branch(
983
es.enter_context(master_branch.lock_write())
984
# pull from source into master.
986
self.source, overwrite, stop_revision,
987
run_hooks=False, fetch_non_mainline=fetch_non_mainline)
988
return self._basic_pull(
989
stop_revision, overwrite, run_hooks,
990
_override_hook_target, _hook_master=master_branch,
991
fetch_non_mainline=fetch_non_mainline)
972
994
def is_compatible(self, source, target):
1017
1040
def _push(self, stop_revision, overwrite, push_metadata):
1018
1041
old_last_revid = self.target.last_revision()
1019
1042
if old_last_revid == stop_revision:
1020
return (old_last_revid, { old_last_revid: (old_last_revid, None) })
1043
return (old_last_revid, {old_last_revid: (old_last_revid, None)})
1021
1044
push_merged = self.target.get_push_merged_revisions()
1022
1045
interrepo = InterToSvnRepository(
1023
1046
self.source.repository, self.target.repository)
1024
base_revmeta, base_mapping = self.target.last_revmeta(skip_hidden=False)
1047
base_revmeta, base_mapping = self.target.last_revmeta(
1026
revidmap = interrepo.push_branch(self.target.get_branch_path(),
1027
self.target.get_config_stack(), old_last_revid,
1028
base_revmeta.metarev.get_foreign_revid(),
1030
stop_revision=stop_revision, overwrite=overwrite,
1031
push_metadata=push_metadata, push_merged=push_merged,
1032
layout=self.target.layout, project=self.target.project)
1050
revidmap = interrepo.push_branch(
1051
self.target.get_branch_path(),
1052
self.target.get_config_stack(), old_last_revid,
1053
base_revmeta.metarev.get_foreign_revid(),
1054
base_mapping, stop_revision=stop_revision, overwrite=overwrite,
1055
push_metadata=push_metadata, push_merged=push_merged,
1056
layout=self.target.layout, project=self.target.project)
1033
1057
except SubversionBranchDiverged as e:
1034
1058
if self._target_is_empty(interrepo.get_graph(), e.target_revid):
1035
1059
raise PushToEmptyBranch(self.target, self.source)
1036
1060
raise DivergedBranches(self.target, self.source)
1037
1061
return (old_last_revid, revidmap)
1039
def _update_revisions(self, stop_revision=None, overwrite=False,
1040
lossy=False, fetch_non_mainline=False):
1063
def _update_revisions(
1064
self, stop_revision=None, overwrite=False, lossy=False,
1065
fetch_non_mainline=False):
1041
1066
"""Push derivatives of the revisions missing from target from source
1057
1082
dict([(k, v[0]) for (k, v) in revid_map.items()]))
1059
1084
def fetch(self, stop_revision=None, fetch_tags=None, find_ghosts=False,
1060
limit=None, exclude_non_mainline=None):
1085
limit=None, exclude_non_mainline=None, lossy=False):
1061
1086
"""Fetch into a subversion repository."""
1062
1087
# FIXME: Handle fetch_tags
1063
1088
# FIXME: Handle find_ghosts
1065
1090
self.source.repository, self.target.repository)
1066
1091
if stop_revision is None:
1067
1092
stop_revision = self.source.last_revision()
1068
interrepo.fetch(revision_id=stop_revision, limit=limit,
1094
revision_id=stop_revision, limit=limit,
1069
1095
exclude_non_mainline=exclude_non_mainline)
1071
1097
def update_tags(self, result, overwrite=False):
1076
1102
result.tag_conflicts = ret
1078
1104
def _basic_push(self, overwrite=False, stop_revision=None, lossy=False,
1079
fetch_non_mainline=False):
1105
fetch_non_mainline=False):
1080
1106
"""Basic implementation of push without bound branches or hooks.
1082
1108
Must be called with source read locked and target write locked.
1084
1110
if lossy and isinstance(self.source, SvnBranch):
1085
1111
raise LossyPushToSameVCS(self.source, self.target)
1086
return self._update_revisions(stop_revision, overwrite=overwrite,
1087
lossy=lossy, fetch_non_mainline=fetch_non_mainline)
1112
return self._update_revisions(
1113
stop_revision, overwrite=overwrite, lossy=lossy,
1114
fetch_non_mainline=fetch_non_mainline)
1089
1116
def push(self, overwrite=False, stop_revision=None,
1090
lossy=False, _override_hook_source_branch=None,
1091
fetch_non_mainline=None):
1117
lossy=False, _override_hook_source_branch=None,
1118
fetch_non_mainline=None):
1092
1119
"""See InterBranch.push()."""
1093
1120
result = SubversionTargetBranchPushResult()
1094
1121
result.target_branch = self.target
1118
1145
result.master_branch = self.target
1119
1146
with self.source.lock_read(), self.target.lock_write():
1120
1147
(result.old_revid, result.new_revid, result.revidmap) = \
1121
self._update_revisions(stop_revision, overwrite,
1122
fetch_non_mainline=fetch_non_mainline)
1148
self._update_revisions(
1149
stop_revision, overwrite,
1150
fetch_non_mainline=fetch_non_mainline)
1123
1151
self.update_tags(result, overwrite)
1125
1153
for hook in Branch.hooks['post_pull']: