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
19
18
from cStringIO import StringIO
24
23
from bzrlib.progress import ProgressBar
25
24
from bzrlib.xml5 import serializer_v5
26
25
from bzrlib.osutils import sha_string, split_lines
26
from bzrlib.errors import NoSuchRevision
28
28
"""Copying of history from one branch to another.
43
43
# TODO: Avoid repeatedly opening weaves so many times.
45
45
# XXX: This doesn't handle ghost (not present in branch) revisions at
46
# all yet. I'm not sure they really should be supported.
48
# TODO: This doesn't handle revisions which may be present but not
49
# merged into the last revision.
48
51
# - get a list of revisions that need to be pulled in
49
52
# - for each one, pull in that revision file
70
73
def __init__(self, to_branch, from_branch, revision_limit=None, pb=None):
71
74
self.to_branch = to_branch
72
75
self.from_branch = from_branch
73
self.revision_limit = revision_limit
74
76
self.failed_revisions = []
75
77
self.count_copied = 0
77
80
self.pb = bzrlib.ui.ui_factory.progress_bar()
80
self._load_histories()
83
self.revision_limit = self._find_revision_limit(revision_limit)
81
84
revs_to_fetch = self._compare_ancestries()
82
85
self._copy_revisions(revs_to_fetch)
84
def _load_histories(self):
85
"""Load histories of both branches, up to the limit."""
86
self.from_history = self.from_branch.revision_history()
87
self.to_history = self.to_branch.revision_history()
88
if self.revision_limit:
89
assert isinstance(revision_limit, basestring)
91
rev_index = self.from_history.index(revision_limit)
94
if rev_index is not None:
95
self.from_history = self.from_history[:rev_index + 1]
89
def _find_revision_limit(self, revision_limit):
90
"""Find the limiting source revision.
92
Every ancestor of that revision will be merged across.
94
Returns the revision_id, or returns None if there's no history
95
in the source branch."""
96
self.pb.update('get source history')
97
from_history = self.from_branch.revision_history()
98
self.pb.update('get destination history')
100
if revision_limit not in from_history:
101
raise NoSuchRevision(self.from_branch, revision_limit)
97
self.from_history = [revision]
103
return revision_limit
105
return from_history[-1]
107
return None # no history in the source branch
100
110
def _compare_ancestries(self):
103
113
That is, every revision that's in the ancestry of the source
104
114
branch and not in the destination branch."""
105
if self.from_history:
106
self.from_ancestry = self.from_branch.get_ancestry(self.from_history[-1])
108
self.from_ancestry = []
110
self.to_history = self.to_branch.get_ancestry(self.to_history[-1])
113
ss = set(self.to_history)
115
self.pb.update('get source ancestry')
116
self.from_ancestry = self.from_branch.get_ancestry(self.revision_limit)
118
dest_last_rev = self.to_branch.last_patch()
119
self.pb.update('get destination ancestry')
121
dest_ancestry = self.to_branch.get_ancestry(dest_last_rev)
124
ss = set(dest_ancestry)
115
126
for rev_id in self.from_ancestry:
116
127
if rev_id not in ss:
117
128
to_fetch.append(rev_id)
118
129
mutter('need to get revision {%s}', rev_id)
119
130
mutter('need to get %d revisions in total', len(to_fetch))
131
self.count_total = len(to_fetch)
124
136
def _copy_revisions(self, revs_to_fetch):
125
138
for rev_id in revs_to_fetch:
139
self.pb.update('fetch revision', i, self.count_total)
126
140
self._copy_one_revision(rev_id)
129
144
def _copy_one_revision(self, rev_id):