101
101
self.count_total = 0
102
102
self.count_weaves = 0
103
103
self.copied_file_ids = set()
104
self.file_ids_names = {}
105
106
self.pb = bzrlib.ui.ui_factory.progress_bar()
108
109
self.from_branch.lock_read()
110
self._fetch_revisions(last_revision)
111
revs = self._revids_to_fetch(last_revision)
114
self._fetch_weave_texts(revs)
115
self._fetch_inventory_weave(revs)
116
self._fetch_revision_texts(revs)
117
self.count_copied += len(revs)
112
119
self.from_branch.unlock()
115
def _fetch_revisions(self, last_revision):
122
def _revids_to_fetch(self, last_revision):
116
123
self.last_revision = self._find_last_revision(last_revision)
117
124
mutter('fetch up to rev {%s}', self.last_revision)
118
125
if (self.last_revision is not None and
119
126
self.to_branch.has_revision(self.last_revision)):
122
revs_to_fetch = self._compare_ancestries()
129
branch_from_revs = set(self.from_branch.get_ancestry(self.last_revision))
123
130
except WeaveError:
124
131
raise InstallFailed([self.last_revision])
125
self._copy_revisions(revs_to_fetch)
126
self.new_ancestry = revs_to_fetch
133
self.dest_last_rev = self.to_branch.last_revision()
134
branch_to_revs = set(self.to_branch.get_ancestry(self.dest_last_rev))
136
return branch_from_revs.difference(branch_to_revs)
138
def _fetch_revision_texts(self, revs):
139
self.to_branch.revision_store.copy_multi(
140
self.from_branch.revision_store, revs)
142
def _fetch_weave_texts(self, revs):
143
file_ids = self.from_branch.fileid_involved_by_set(revs)
145
num_file_ids = len(file_ids)
146
for file_id in file_ids:
147
self.pb.update("merge weave merge", count, num_file_ids)
149
to_weave = self.to_weaves.get_weave_or_empty(file_id,
150
self.to_branch.get_transaction())
151
from_weave = self.from_weaves.get_weave(file_id,
152
self.from_branch.get_transaction())
154
if to_weave.numversions() > 0:
155
# destination has contents, must merge
157
to_weave.join(from_weave)
158
except errors.WeaveParentMismatch:
159
to_weave.reweave(from_weave)
161
# destination is empty, just replace it
162
to_weave = from_weave.copy()
164
self.to_weaves.put_weave(file_id, to_weave,
165
self.to_branch.get_transaction())
169
def _fetch_inventory_weave(self, revs):
170
self.pb.update("inventory merge", 0, 1)
172
from_weave = self.from_control.get_weave('inventory',
173
self.from_branch.get_transaction())
174
to_weave = self.to_control.get_weave('inventory',
175
self.to_branch.get_transaction())
177
if to_weave.numversions() > 0:
178
# destination has contents, must merge
180
to_weave.join(from_weave)
181
except errors.WeaveParentMismatch:
182
to_weave.reweave(from_weave)
184
# destination is empty, just replace it
185
to_weave = from_weave.copy()
187
self.to_control.put_weave('inventory', to_weave,
188
self.to_branch.get_transaction())
128
192
def _find_last_revision(self, last_revision):
129
193
"""Find the limiting source revision.
141
205
return from_history[-1]
143
207
return None # no history in the source branch
146
def _compare_ancestries(self):
147
"""Get a list of revisions that must be copied.
149
That is, every revision that's in the ancestry of the source
150
branch and not in the destination branch."""
151
self.pb.update('get source ancestry')
152
self.from_ancestry = self.from_branch.get_ancestry(self.last_revision)
154
dest_last_rev = self.to_branch.last_revision()
155
self.pb.update('get destination ancestry')
157
dest_ancestry = self.to_branch.get_ancestry(dest_last_rev)
160
ss = set(dest_ancestry)
162
for rev_id in self.from_ancestry:
164
to_fetch.append(rev_id)
165
mutter('need to get revision {%s}', rev_id)
166
mutter('need to get %d revisions in total', len(to_fetch))
167
self.count_total = len(to_fetch)
170
def _copy_revisions(self, revs_to_fetch):
172
for rev_id in revs_to_fetch:
176
if self.to_branch.has_revision(rev_id):
178
self.pb.update('fetch revision', i, self.count_total)
179
self._copy_one_revision(rev_id)
180
self.count_copied += 1
183
def _copy_one_revision(self, rev_id):
184
"""Copy revision and everything referenced by it."""
185
mutter('copying revision {%s}', rev_id)
186
rev_xml = self.from_branch.get_revision_xml(rev_id)
187
inv_xml = self.from_branch.get_inventory_xml(rev_id)
188
rev = serializer_v5.read_revision_from_string(rev_xml)
189
inv = serializer_v5.read_inventory_from_string(inv_xml)
190
assert rev.revision_id == rev_id
191
assert rev.inventory_sha1 == sha_string(inv_xml)
192
mutter(' commiter %s, %d parents',
195
self._copy_new_texts(rev_id, inv)
196
parents = rev.parent_ids
197
new_parents = copy(parents)
198
for parent in parents:
199
if not self.to_branch.has_revision(parent):
200
new_parents.pop(new_parents.index(parent))
201
self._copy_inventory(rev_id, inv_xml, new_parents)
202
self.to_branch.revision_store.add(StringIO(rev_xml), rev_id)
203
mutter('copied revision %s', rev_id)
205
def _copy_inventory(self, rev_id, inv_xml, parent_ids):
206
self.to_control.add_text('inventory', rev_id,
207
split_lines(inv_xml), parent_ids,
208
self.to_branch.get_transaction())
210
def _copy_new_texts(self, rev_id, inv):
211
"""Copy any new texts occuring in this revision."""
212
# TODO: Rather than writing out weaves every time, hold them
213
# in memory until everything's done? But this way is nicer
214
# if it's interrupted.
215
for path, ie in inv.iter_entries():
216
self._copy_one_weave(rev_id, ie.file_id, ie.revision)
218
def _copy_one_weave(self, rev_id, file_id, text_revision):
219
"""Copy one file weave, esuring the result contains text_revision."""
220
to_weave = self.to_weaves.get_weave_or_empty(file_id,
221
self.to_branch.get_transaction())
222
if text_revision in to_weave:
224
from_weave = self.from_weaves.get_weave(file_id,
225
self.from_branch.get_transaction())
226
if text_revision not in from_weave:
227
raise MissingText(self.from_branch, text_revision, file_id)
228
mutter('copy file {%s} modified in {%s}', file_id, rev_id)
230
to_weave.join(from_weave)
231
except errors.WeaveParentMismatch:
232
to_weave.reweave(from_weave)
233
self.to_weaves.put_weave(file_id, to_weave,
234
self.to_branch.get_transaction())
235
self.count_weaves += 1
236
self.copied_file_ids.add(file_id)
237
mutter('copied file {%s}', file_id)