18
18
from cStringIO import StringIO
21
import bzrlib.errors as errors
22
from bzrlib.errors import InstallFailed, NoSuchRevision, WeaveError
21
23
from bzrlib.trace import mutter, note, warning
22
from bzrlib.branch import Branch, INVENTORY_FILEID, ANCESTRY_FILEID
24
from bzrlib.branch import Branch
23
25
from bzrlib.progress import ProgressBar
24
26
from bzrlib.xml5 import serializer_v5
25
27
from bzrlib.osutils import sha_string, split_lines
26
from bzrlib.errors import NoSuchRevision
28
29
"""Copying of history from one branch to another.
83
84
count_copied -- number of revisions copied
85
count_texts -- number of file texts copied
86
count_weaves -- number of file weaves copied
87
88
def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
89
if to_branch == from_branch:
90
raise Exception("can't fetch from a branch to itself")
88
91
self.to_branch = to_branch
89
92
self.to_weaves = to_branch.weave_store
93
self.to_control = to_branch.control_weaves
90
94
self.from_branch = from_branch
91
95
self.from_weaves = from_branch.weave_store
96
self.from_control = from_branch.control_weaves
92
97
self.failed_revisions = []
93
98
self.count_copied = 0
94
99
self.count_total = 0
100
self.count_weaves = 0
101
self.copied_file_ids = set()
97
103
self.pb = bzrlib.ui.ui_factory.progress_bar()
106
self.from_branch.lock_read()
108
self._fetch_revisions(last_revision)
110
self.from_branch.unlock()
113
def _fetch_revisions(self, last_revision):
100
114
self.last_revision = self._find_last_revision(last_revision)
101
115
mutter('fetch up to rev {%s}', self.last_revision)
102
revs_to_fetch = self._compare_ancestries()
116
if (self.last_revision is not None and
117
self.to_branch.has_revision(self.last_revision)):
120
revs_to_fetch = self._compare_ancestries()
122
raise InstallFailed([self.last_revision])
103
123
self._copy_revisions(revs_to_fetch)
104
124
self.new_ancestry = revs_to_fetch
108
126
def _find_last_revision(self, last_revision):
109
127
"""Find the limiting source revision.
174
189
assert rev.inventory_sha1 == sha_string(inv_xml)
175
190
mutter(' commiter %s, %d parents',
178
193
self._copy_new_texts(rev_id, inv)
179
parent_ids = [x.revision_id for x in rev.parents]
180
self._copy_inventory(rev_id, inv_xml, parent_ids)
181
self._copy_ancestry(rev_id, parent_ids)
194
parents = rev.parent_ids
195
for parent in parents:
196
if not self.to_branch.has_revision(parent):
197
parents.pop(parents.index(parent))
198
self._copy_inventory(rev_id, inv_xml, parents)
182
199
self.to_branch.revision_store.add(StringIO(rev_xml), rev_id)
200
mutter('copied revision %s', rev_id)
185
203
def _copy_inventory(self, rev_id, inv_xml, parent_ids):
186
self.to_weaves.add_text(INVENTORY_FILEID, rev_id,
187
split_lines(inv_xml), parent_ids)
190
def _copy_ancestry(self, rev_id, parent_ids):
191
ancestry_lines = self.from_weaves.get_lines(ANCESTRY_FILEID, rev_id)
192
self.to_weaves.add_text(ANCESTRY_FILEID, rev_id, ancestry_lines,
204
self.to_control.add_text('inventory', rev_id,
205
split_lines(inv_xml), parent_ids,
206
self.to_branch.get_transaction())
196
208
def _copy_new_texts(self, rev_id, inv):
197
209
"""Copy any new texts occuring in this revision."""
198
210
# TODO: Rather than writing out weaves every time, hold them
199
211
# in memory until everything's done? But this way is nicer
200
212
# if it's interrupted.
201
213
for path, ie in inv.iter_entries():
202
if ie.kind != 'file':
204
if ie.text_version != rev_id:
214
if ie.revision != rev_id:
206
216
mutter('%s {%s} is changed in this revision',
207
217
path, ie.file_id)
208
self._copy_one_text(rev_id, ie.file_id)
211
def _copy_one_text(self, rev_id, file_id):
212
"""Copy one file text."""
213
mutter('copy text version {%s} of file {%s}',
215
from_weave = self.from_weaves.get_weave(file_id)
216
from_idx = from_weave.lookup(rev_id)
217
from_parents = map(from_weave.idx_to_name, from_weave.parents(from_idx))
218
text_lines = from_weave.get(from_idx)
219
to_weave = self.to_weaves.get_weave_or_empty(file_id)
220
to_parents = map(to_weave.lookup, from_parents)
221
# it's ok to add even if the text is already there
222
to_weave.add(rev_id, to_parents, text_lines)
223
self.to_weaves.put_weave(file_id, to_weave)
224
self.count_texts += 1
218
self._copy_one_weave(rev_id, ie.file_id)
221
def _copy_one_weave(self, rev_id, file_id):
222
"""Copy one file weave."""
223
mutter('copy file {%s} modified in {%s}', file_id, rev_id)
224
if file_id in self.copied_file_ids:
225
mutter('file {%s} already copied', file_id)
227
from_weave = self.from_weaves.get_weave(file_id,
228
self.from_branch.get_transaction())
229
to_weave = self.to_weaves.get_weave_or_empty(file_id,
230
self.to_branch.get_transaction())
232
to_weave.join(from_weave)
233
except errors.WeaveParentMismatch:
234
to_weave.reweave(from_weave)
235
self.to_weaves.put_weave(file_id, to_weave,
236
self.to_branch.get_transaction())
237
self.count_weaves += 1
238
self.copied_file_ids.add(file_id)
239
mutter('copied file {%s}', file_id)