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
from cStringIO import StringIO
22
import bzrlib.errors as errors
23
from bzrlib.errors import (InstallFailed, NoSuchRevision, WeaveError,
25
from bzrlib.trace import mutter, note, warning
26
from bzrlib.branch import Branch
27
from bzrlib.progress import ProgressBar
28
from bzrlib.xml5 import serializer_v5
29
from bzrlib.osutils import sha_string, split_lines
31
18
"""Copying of history from one branch to another.
43
30
memory until we've updated all of the files referenced.
34
import bzrlib.errors as errors
35
from bzrlib.errors import (InstallFailed, NoSuchRevision, WeaveError,
37
from bzrlib.trace import mutter
38
from bzrlib.progress import ProgressBar
39
from bzrlib.reconcile import RepoReconciler
40
from bzrlib.revision import NULL_REVISION
41
from bzrlib.symbol_versioning import *
46
44
# TODO: Avoid repeatedly opening weaves so many times.
48
46
# XXX: This doesn't handle ghost (not present in branch) revisions at
61
59
# and add in all file versions
62
@deprecated_function(zero_eight)
65
63
def greedy_fetch(to_branch, from_branch, revision=None, pb=None):
64
"""Legacy API, please see branch.fetch(from_branch, last_revision, pb)."""
66
65
f = Fetcher(to_branch, from_branch, revision, pb)
67
66
return f.count_copied, f.failed_revisions
71
class Fetcher(object):
72
"""Pull revisions and texts from one branch to another.
74
This doesn't update the destination's history; that can be done
75
separately if desired.
78
If set, pull only up to this revision_id.
82
last_revision -- if last_revision
83
is given it will be that, otherwise the last revision of
71
class RepoFetcher(object):
72
"""Pull revisions and texts from one repository to another.
75
if set, try to limit to the data this revision references.
86
78
count_copied -- number of revisions copied
88
count_weaves -- number of file weaves copied
80
This should not be used directory, its essential a object to encapsulate
81
the logic in InterRepository.fetch().
90
def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
91
if to_branch.base == from_branch.base:
92
raise Exception("can't fetch from a branch to itself %s, %s" %
93
(from_branch.base, to_branch.base))
95
self.to_branch = to_branch
96
self.from_branch = from_branch
83
def __init__(self, to_repository, from_repository, last_revision=None, pb=None):
85
self.failed_revisions = []
87
if to_repository.control_files._transport.base == from_repository.control_files._transport.base:
88
# check that last_revision is in 'from' and then return a no-operation.
89
if last_revision not in (None, NULL_REVISION):
90
from_repository.get_revision(last_revision)
92
self.to_repository = to_repository
93
self.from_repository = from_repository
94
# must not mutate self._last_revision as its potentially a shared instance
97
95
self._last_revision = last_revision
99
97
self.pb = bzrlib.ui.ui_factory.progress_bar()
102
self.from_branch.lock_read()
100
self.from_repository.lock_read()
104
self.to_branch.lock_write()
102
self.to_repository.lock_write()
108
self.to_branch.unlock()
106
self.to_repository.unlock()
110
self.from_branch.unlock()
108
self.from_repository.unlock()
112
110
def __fetch(self):
113
111
"""Primary worker function.
115
113
This initialises all the needed variables, and then fetches the
116
114
requested revisions, finally clearing the progress bar.
118
self.to_repository = self.to_branch.repository
119
116
self.to_weaves = self.to_repository.weave_store
120
117
self.to_control = self.to_repository.control_weaves
121
self.from_repository = self.from_branch.repository
122
118
self.from_weaves = self.from_repository.weave_store
123
119
self.from_control = self.from_repository.control_weaves
124
self.failed_revisions = []
125
self.count_copied = 0
126
120
self.count_total = 0
127
self.count_weaves = 0
128
self.copied_file_ids = set()
129
121
self.file_ids_names = {}
131
123
revs = self._revids_to_fetch()
141
133
def _revids_to_fetch(self):
142
self._find_last_revision()
134
self.pb.update('get destination history')
143
135
mutter('fetch up to rev {%s}', self._last_revision)
144
if (self._last_revision is not None and
136
if self._last_revision is NULL_REVISION:
137
# explicit limit of no revisions needed
139
if (self._last_revision != None and
145
140
self.to_repository.has_revision(self._last_revision)):
148
branch_from_revs = set(self.from_repository.get_ancestry(self._last_revision))
144
return self.to_repository.missing_revision_ids(self.from_repository,
146
except errors.NoSuchRevision:
150
147
raise InstallFailed([self._last_revision])
152
self.dest_last_rev = self.to_branch.last_revision()
153
branch_to_revs = set(self.to_repository.get_ancestry(self.dest_last_rev))
155
return branch_from_revs.difference(branch_to_revs)
157
149
def _fetch_revision_texts(self, revs):
158
150
self.to_repository.revision_store.copy_multi(
159
self.from_repository.revision_store, revs)
151
self.from_repository.revision_store,
154
# fixup inventory if needed:
155
# this is expensive because we have no inverse index to current ghosts.
156
# but on local disk its a few seconds and sftp push is already insane.
158
# FIXME: the generic code path should not need this, if it truely is
160
reconciler = RepoReconciler(self.to_repository)
161
reconciler.reconcile()
161
163
def _fetch_weave_texts(self, revs):
162
file_ids = self.from_branch.fileid_involved_by_set(revs)
164
file_ids = self.from_repository.fileid_involved_by_set(revs)
164
166
num_file_ids = len(file_ids)
165
167
for file_id in file_ids:
166
self.pb.update("merge weave merge", count, num_file_ids)
168
self.pb.update("merge weaves", count, num_file_ids)
168
170
to_weave = self.to_weaves.get_weave_or_empty(file_id,
169
self.to_branch.get_transaction())
171
self.to_repository.get_transaction())
170
172
from_weave = self.from_weaves.get_weave(file_id,
171
self.from_branch.get_transaction())
173
self.from_repository.get_transaction())
173
175
if to_weave.numversions() > 0:
174
176
# destination has contents, must merge
181
183
to_weave = from_weave.copy()
183
185
self.to_weaves.put_weave(file_id, to_weave,
184
self.to_branch.get_transaction())
186
self.to_repository.get_transaction())
188
189
def _fetch_inventory_weave(self, revs):
189
190
self.pb.update("inventory fetch", 0, 2)
190
191
from_weave = self.from_repository.get_inventory_weave()
191
to_weave = self.to_repository.get_inventory_weave()
192
self.to_inventory_weave = self.to_repository.get_inventory_weave()
192
193
self.pb.update("inventory fetch", 1, 2)
193
to_weave = self.to_control.get_weave('inventory',
194
self.to_branch.get_transaction())
194
self.to_inventory_weave = self.to_control.get_weave('inventory',
195
self.to_repository.get_transaction())
195
196
self.pb.update("inventory fetch", 2, 2)
197
if to_weave.numversions() > 0:
198
if self.to_inventory_weave.numversions() > 0:
198
199
# destination has contents, must merge
200
to_weave.join(from_weave, pb=self.pb, msg='merge inventory')
201
self.to_inventory_weave.join(from_weave, pb=self.pb, msg='merge inventory')
201
202
except errors.WeaveParentMismatch:
202
to_weave.reweave(from_weave, pb=self.pb, msg='reweave inventory')
203
self.to_inventory_weave.reweave(from_weave, pb=self.pb, msg='reweave inventory')
204
205
# destination is empty, just replace it
205
to_weave = from_weave.copy()
206
self.to_inventory_weave = from_weave.copy()
207
self.to_control.put_weave('inventory', to_weave,
208
self.to_branch.get_transaction())
208
# must be written before pulling any revisions
209
self.to_control.put_weave('inventory', self.to_inventory_weave,
210
self.to_repository.get_transaction())
212
def _find_last_revision(self):
213
"""Find the limiting source revision.
215
Every ancestor of that revision will be merged across.
217
Returns the revision_id, or returns None if there's no history
218
in the source branch."""
219
if self._last_revision:
221
self.pb.update('get source history')
222
from_history = self.from_branch.revision_history()
223
self.pb.update('get destination history')
225
self._last_revision = from_history[-1]
227
# no history in the source branch
228
self._last_revision = None
215
class Fetcher(object):
216
"""Backwards compatability glue for branch.fetch()."""
218
@deprecated_method(zero_eight)
219
def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
220
"""Please see branch.fetch()."""
221
to_branch.fetch(from_branch, last_revision, pb)