~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/fetch.py

[merge] update from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
88
88
    count_weaves -- number of file weaves copied
89
89
    """
90
90
    def __init__(self, to_branch, from_branch, last_revision=None, pb=None):
91
 
        if to_branch == from_branch:
92
 
            raise Exception("can't fetch from a branch to itself")
 
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))
 
94
        
93
95
        self.to_branch = to_branch
94
 
        self.to_weaves = to_branch.weave_store
95
 
        self.to_control = to_branch.control_weaves
96
96
        self.from_branch = from_branch
97
 
        self.from_weaves = from_branch.weave_store
98
 
        self.from_control = from_branch.control_weaves
 
97
        self._last_revision = last_revision
 
98
        if pb is None:
 
99
            self.pb = bzrlib.ui.ui_factory.progress_bar()
 
100
        else:
 
101
            self.pb = pb
 
102
        self.from_branch.lock_read()
 
103
        try:
 
104
            self.to_branch.lock_write()
 
105
            try:
 
106
                self.__fetch()
 
107
            finally:
 
108
                self.to_branch.unlock()
 
109
        finally:
 
110
            self.from_branch.unlock()
 
111
 
 
112
    def __fetch(self):
 
113
        """Primary worker function.
 
114
 
 
115
        This initialises all the needed variables, and then fetches the 
 
116
        requested revisions, finally clearing the progress bar.
 
117
        """
 
118
        self.to_repository = self.to_branch.repository
 
119
        self.to_weaves = self.to_repository.weave_store
 
120
        self.to_control = self.to_repository.control_weaves
 
121
        self.from_repository = self.from_branch.repository
 
122
        self.from_weaves = self.from_repository.weave_store
 
123
        self.from_control = self.from_repository.control_weaves
99
124
        self.failed_revisions = []
100
125
        self.count_copied = 0
101
126
        self.count_total = 0
102
127
        self.count_weaves = 0
103
128
        self.copied_file_ids = set()
104
129
        self.file_ids_names = {}
105
 
        if pb is None:
106
 
            self.pb = bzrlib.ui.ui_factory.progress_bar()
107
 
        else:
108
 
            self.pb = pb
109
 
        self.from_branch.lock_read()
110
130
        try:
111
 
            self._fetch_revisions(last_revision)
 
131
            revs = self._revids_to_fetch()
 
132
            # nothing to do
 
133
            if revs: 
 
134
                self._fetch_weave_texts(revs)
 
135
                self._fetch_inventory_weave(revs)
 
136
                self._fetch_revision_texts(revs)
 
137
                self.count_copied += len(revs)
112
138
        finally:
113
 
            self.from_branch.unlock()
114
139
            self.pb.clear()
115
140
 
116
 
    def _fetch_revisions(self, last_revision):
117
 
        self.last_revision = self._find_last_revision(last_revision)
118
 
        mutter('fetch up to rev {%s}', self.last_revision)
119
 
        if (self.last_revision is not None and 
120
 
            self.to_branch.has_revision(self.last_revision)):
 
141
    def _revids_to_fetch(self):
 
142
        self._find_last_revision()
 
143
        mutter('fetch up to rev {%s}', self._last_revision)
 
144
        if (self._last_revision is not None and 
 
145
            self.to_repository.has_revision(self._last_revision)):
121
146
            return
122
147
        try:
123
 
            revs_to_fetch = self._compare_ancestries()
 
148
            branch_from_revs = set(self.from_repository.get_ancestry(self._last_revision))
124
149
        except WeaveError:
125
 
            raise InstallFailed([self.last_revision])
126
 
        self._copy_revisions(revs_to_fetch)
127
 
        self.new_ancestry = revs_to_fetch
128
 
 
129
 
    def _find_last_revision(self, last_revision):
 
150
            raise InstallFailed([self._last_revision])
 
151
 
 
152
        self.dest_last_rev = self.to_branch.last_revision()
 
153
        branch_to_revs = set(self.to_repository.get_ancestry(self.dest_last_rev))
 
154
 
 
155
        return branch_from_revs.difference(branch_to_revs)
 
156
 
 
157
    def _fetch_revision_texts(self, revs):
 
158
        self.to_repository.revision_store.copy_multi(
 
159
            self.from_repository.revision_store, revs)
 
160
 
 
161
    def _fetch_weave_texts(self, revs):
 
162
        file_ids = self.from_branch.fileid_involved_by_set(revs)
 
163
        count = 0
 
164
        num_file_ids = len(file_ids)
 
165
        for file_id in file_ids:
 
166
            self.pb.update("merge weave merge", count, num_file_ids)
 
167
            count +=1
 
168
            to_weave = self.to_weaves.get_weave_or_empty(file_id,
 
169
                self.to_branch.get_transaction())
 
170
            from_weave = self.from_weaves.get_weave(file_id,
 
171
                self.from_branch.get_transaction())
 
172
 
 
173
            if to_weave.numversions() > 0:
 
174
                # destination has contents, must merge
 
175
                try:
 
176
                    to_weave.join(from_weave)
 
177
                except errors.WeaveParentMismatch:
 
178
                    to_weave.reweave(from_weave)
 
179
            else:
 
180
                # destination is empty, just replace it
 
181
                to_weave = from_weave.copy()
 
182
 
 
183
            self.to_weaves.put_weave(file_id, to_weave,
 
184
                self.to_branch.get_transaction())
 
185
 
 
186
        self.pb.clear()
 
187
 
 
188
    def _fetch_inventory_weave(self, revs):
 
189
        self.pb.update("inventory fetch", 0, 2)
 
190
        from_weave = self.from_repository.get_inventory_weave()
 
191
        to_weave = self.to_repository.get_inventory_weave()
 
192
        self.pb.update("inventory fetch", 1, 2)
 
193
        to_weave = self.to_control.get_weave('inventory',
 
194
                self.to_branch.get_transaction())
 
195
        self.pb.update("inventory fetch", 2, 2)
 
196
 
 
197
        if to_weave.numversions() > 0:
 
198
            # destination has contents, must merge
 
199
            try:
 
200
                to_weave.join(from_weave, pb=self.pb, msg='merge inventory')
 
201
            except errors.WeaveParentMismatch:
 
202
                to_weave.reweave(from_weave, pb=self.pb, msg='reweave inventory')
 
203
        else:
 
204
            # destination is empty, just replace it
 
205
            to_weave = from_weave.copy()
 
206
 
 
207
        self.to_control.put_weave('inventory', to_weave,
 
208
            self.to_branch.get_transaction())
 
209
 
 
210
        self.pb.clear()
 
211
 
 
212
    def _find_last_revision(self):
130
213
        """Find the limiting source revision.
131
214
 
132
215
        Every ancestor of that revision will be merged across.
133
216
 
134
217
        Returns the revision_id, or returns None if there's no history
135
218
        in the source branch."""
136
 
        if last_revision:
137
 
            return last_revision
 
219
        if self._last_revision:
 
220
            return
138
221
        self.pb.update('get source history')
139
222
        from_history = self.from_branch.revision_history()
140
223
        self.pb.update('get destination history')
141
224
        if from_history:
142
 
            return from_history[-1]
143
 
        else:
144
 
            return None                 # no history in the source branch
145
 
            
146
 
 
147
 
    def _compare_ancestries(self):
148
 
        """Get a list of revisions that must be copied.
149
 
 
150
 
        That is, every revision that's in the ancestry of the source
151
 
        branch and not in the destination branch."""
152
 
        self.pb.update('get source ancestry')
153
 
        self.from_ancestry = self.from_branch.get_ancestry(self.last_revision)
154
 
 
155
 
        dest_last_rev = self.to_branch.last_revision()
156
 
        self.pb.update('get destination ancestry')
157
 
        if dest_last_rev:
158
 
            dest_ancestry = self.to_branch.get_ancestry(dest_last_rev)
159
 
        else:
160
 
            dest_ancestry = []
161
 
        ss = set(dest_ancestry)
162
 
        to_fetch = []
163
 
        for rev_id in self.from_ancestry:
164
 
            if rev_id not in ss:
165
 
                to_fetch.append(rev_id)
166
 
                mutter('need to get revision {%s}', rev_id)
167
 
        mutter('need to get %d revisions in total', len(to_fetch))
168
 
        self.count_total = len(to_fetch)
169
 
        return to_fetch
170
 
 
171
 
    def _copy_revisions(self, revs_to_fetch):
172
 
        i = 0
173
 
        for rev_id in revs_to_fetch:
174
 
            i += 1
175
 
            if rev_id is None:
176
 
                continue
177
 
            if self.to_branch.has_revision(rev_id):
178
 
                continue
179
 
            self.pb.update('copy revision', i, self.count_total)
180
 
            self._copy_one_revision(rev_id)
181
 
            self.count_copied += 1
182
 
 
183
 
 
184
 
    def _copy_one_revision(self, rev_id):
185
 
        """Copy revision and everything referenced by it."""
186
 
        mutter('copying revision {%s}', rev_id)
187
 
        rev_xml = self.from_branch.get_revision_xml(rev_id)
188
 
        inv_xml = self.from_branch.get_inventory_xml(rev_id)
189
 
        rev = serializer_v5.read_revision_from_string(rev_xml)
190
 
        inv = serializer_v5.read_inventory_from_string(inv_xml)
191
 
        assert rev.revision_id == rev_id
192
 
        assert rev.inventory_sha1 == sha_string(inv_xml)
193
 
        mutter('  commiter %s, %d parents',
194
 
               rev.committer,
195
 
               len(rev.parent_ids))
196
 
        self._copy_new_texts(rev_id, inv)
197
 
        parents = rev.parent_ids
198
 
        new_parents = copy(parents)
199
 
        for parent in parents:
200
 
            if not self.to_branch.has_revision(parent):
201
 
                new_parents.pop(new_parents.index(parent))
202
 
        self._copy_inventory(rev_id, inv_xml, new_parents)
203
 
        self.to_branch.revision_store.add(StringIO(rev_xml), rev_id)
204
 
        mutter('copied revision %s', rev_id)
205
 
 
206
 
    def _copy_inventory(self, rev_id, inv_xml, parent_ids):
207
 
        self.to_control.add_text('inventory', rev_id,
208
 
                                split_lines(inv_xml), parent_ids,
209
 
                                self.to_branch.get_transaction())
210
 
 
211
 
    def _copy_new_texts(self, rev_id, inv):
212
 
        """Copy any new texts occuring in this revision."""
213
 
        # TODO: Rather than writing out weaves every time, hold them
214
 
        # in memory until everything's done?  But this way is nicer
215
 
        # if it's interrupted.
216
 
        for path, ie in inv.iter_entries():
217
 
            self._copy_one_weave(rev_id, ie.file_id, ie.revision)
218
 
 
219
 
    def _copy_one_weave(self, rev_id, file_id, text_revision):
220
 
        """Copy one file weave, esuring the result contains text_revision."""
221
 
        # check if the revision is already there
222
 
        if file_id in self.file_ids_names.keys( ) and \
223
 
            text_revision in self.file_ids_names[file_id]:
224
 
                return        
225
 
        to_weave = self.to_weaves.get_weave_or_empty(file_id,
226
 
            self.to_branch.get_transaction())
227
 
        if not file_id in self.file_ids_names.keys( ):
228
 
            self.file_ids_names[file_id] = to_weave.names( )
229
 
        if text_revision in to_weave:
230
 
            return
231
 
        from_weave = self.from_weaves.get_weave(file_id,
232
 
            self.from_branch.get_transaction())
233
 
        if text_revision not in from_weave:
234
 
            raise MissingText(self.from_branch, text_revision, file_id)
235
 
        mutter('copy file {%s} modified in {%s}', file_id, rev_id)
236
 
 
237
 
        if to_weave.numversions() > 0:
238
 
            # destination has contents, must merge
239
 
            try:
240
 
                to_weave.join(from_weave)
241
 
            except errors.WeaveParentMismatch:
242
 
                to_weave.reweave(from_weave)
243
 
        else:
244
 
            # destination is empty, just replace it
245
 
            to_weave = from_weave.copy( )
246
 
        self.to_weaves.put_weave(file_id, to_weave,
247
 
            self.to_branch.get_transaction())
248
 
        self.count_weaves += 1
249
 
        self.copied_file_ids.add(file_id)
250
 
        self.file_ids_names[file_id] = to_weave.names()
251
 
        mutter('copied file {%s}', file_id)
252
 
 
 
225
            self._last_revision = from_history[-1]
 
226
        else:
 
227
            # no history in the source branch
 
228
            self._last_revision = None
253
229
 
254
230
fetch = Fetcher