1
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
"""Copying of history from one branch to another.
21
21
that has merged into it. As the first step of a merge, pull, or
22
22
branch operation we copy history from the source into the destination
25
The copying is done in a slightly complicated order. We don't want to
26
add a revision to the store until everything it refers to is also
27
stored, so that if a revision is present we can totally recreate it.
28
However, we can't know what files are included in a revision until we
29
read its inventory. So we query the inventory store of the source for
30
the ids we need, and then pull those ids and then return to the inventories.
36
import bzrlib.errors as errors
33
37
from bzrlib.errors import InstallFailed
34
38
from bzrlib.progress import ProgressPhase
35
39
from bzrlib.revision import NULL_REVISION
39
43
from bzrlib.versionedfile import FulltextContentFactory
45
# TODO: Avoid repeatedly opening weaves so many times.
47
# XXX: This doesn't handle ghost (not present in branch) revisions at
48
# all yet. I'm not sure they really should be supported.
50
# NOTE: This doesn't copy revisions which may be present but not
51
# merged into the last revision. I'm not sure we want to do that.
53
# - get a list of revisions that need to be pulled in
54
# - for each one, pull in that revision file
55
# and get the inventory, and store the inventory with right
57
# - and get the ancestry, and store that with right parents too
58
# - and keep a note of all file ids and version seen
59
# - then go through all files; for each one get the weave,
60
# and add in all file versions
42
63
class RepoFetcher(object):
43
64
"""Pull revisions and texts from one repository to another.
67
if set, try to limit to the data this revision references.
45
69
This should not be used directly, it's essential a object to encapsulate
46
70
the logic in InterRepository.fetch().
49
def __init__(self, to_repository, from_repository, last_revision=None,
50
pb=None, find_ghosts=True, fetch_spec=None):
73
def __init__(self, to_repository, from_repository, last_revision=None, pb=None,
51
75
"""Create a repo fetcher.
53
:param last_revision: If set, try to limit to the data this revision
55
77
:param find_ghosts: If True search the entire history for ghosts.
56
78
:param _write_group_acquired_callable: Don't use; this parameter only
57
79
exists to facilitate a hack done in InterPackRepo.fetch. We would
58
80
like to remove this parameter.
59
:param pb: ProgressBar object to use; deprecated and ignored.
60
This method will just create one on top of the stack.
63
symbol_versioning.warn(
64
symbol_versioning.deprecated_in((1, 14, 0))
65
% "pb parameter to RepoFetcher.__init__")
66
# and for simplicity it is in fact ignored
67
82
if to_repository.has_same_location(from_repository):
68
83
# repository.fetch should be taking care of this case.
69
84
raise errors.BzrError('RepoFetcher run '
74
89
self.sink = to_repository._get_sink()
75
90
# must not mutate self._last_revision as its potentially a shared instance
76
91
self._last_revision = last_revision
77
self._fetch_spec = fetch_spec
78
92
self.find_ghosts = find_ghosts
94
self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
95
self.nested_pb = self.pb
79
99
self.from_repository.lock_read()
80
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
81
self.from_repository, self.from_repository._format,
82
self.to_repository, self.to_repository._format)
104
if self.nested_pb is not None:
105
self.nested_pb.finished()
86
107
self.from_repository.unlock()
99
120
# assert not missing
100
121
self.count_total = 0
101
122
self.file_ids_names = {}
102
pb = bzrlib.ui.ui_factory.nested_progress_bar()
103
pb.show_pct = pb.show_count = False
123
pp = ProgressPhase('Transferring', 4, self.pb)
105
pb.update("Finding revisions", 0, 2)
106
126
search = self._revids_to_fetch()
107
127
if search is None:
109
pb.update("Fetching revisions", 1, 2)
110
self._fetch_everything_for_search(search)
129
self._fetch_everything_for_search(search, pp)
114
def _fetch_everything_for_search(self, search):
133
def _fetch_everything_for_search(self, search, pp):
115
134
"""Fetch all data for the given set of revisions."""
116
135
# The first phase is "file". We pass the progress bar for it directly
117
136
# into item_keys_introduced_by, which has more information about how
126
145
raise errors.IncompatibleRepositories(
127
146
self.from_repository, self.to_repository,
128
147
"different rich-root support")
129
pb = bzrlib.ui.ui_factory.nested_progress_bar()
148
self.pb = bzrlib.ui.ui_factory.nested_progress_bar()
131
pb.update("Get stream source")
132
150
source = self.from_repository._get_source(
133
151
self.to_repository._format)
134
152
stream = source.get_stream(search)
135
153
from_format = self.from_repository._format
136
pb.update("Inserting stream")
137
154
resume_tokens, missing_keys = self.sink.insert_stream(
138
155
stream, from_format, [])
140
pb.update("Missing keys")
141
157
stream = source.get_stream_for_missing_keys(missing_keys)
142
pb.update("Inserting missing keys")
143
158
resume_tokens, missing_keys = self.sink.insert_stream(
144
159
stream, from_format, resume_tokens)
162
177
If no revisions need to be fetched, then this just returns None.
164
if self._fetch_spec is not None:
165
return self._fetch_spec
166
179
mutter('fetch up to rev {%s}', self._last_revision)
167
180
if self._last_revision is NULL_REVISION:
168
181
# explicit limit of no revisions needed