~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/fetch.py

Merged mailine

Show diffs side-by-side

added added

removed removed

Lines of Context:
108
108
            self.pb = pb
109
109
        self.from_branch.lock_read()
110
110
        try:
111
 
            self._fetch_revisions(last_revision)
 
111
            revs = self._revids_to_fetch(last_revision)
 
112
            # nothing to do
 
113
            if revs: 
 
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
118
        finally:
113
119
            self.from_branch.unlock()
114
120
            self.pb.clear()
115
121
 
116
 
    def _fetch_revisions(self, last_revision):
 
122
    def _revids_to_fetch(self, last_revision):
117
123
        self.last_revision = self._find_last_revision(last_revision)
118
124
        mutter('fetch up to rev {%s}', self.last_revision)
119
125
        if (self.last_revision is not None and 
120
126
            self.to_branch.has_revision(self.last_revision)):
121
127
            return
122
128
        try:
123
 
            revs_to_fetch = self._compare_ancestries()
 
129
            branch_from_revs = set(self.from_branch.get_ancestry(self.last_revision))
124
130
        except WeaveError:
125
131
            raise InstallFailed([self.last_revision])
126
 
        self._copy_revisions(revs_to_fetch)
127
 
        self.new_ancestry = revs_to_fetch
 
132
 
 
133
        self.dest_last_rev = self.to_branch.last_revision()
 
134
        branch_to_revs = set(self.to_branch.get_ancestry(self.dest_last_rev))
 
135
 
 
136
        return branch_from_revs.difference(branch_to_revs)
 
137
 
 
138
    def _fetch_revision_texts(self, revs):
 
139
        self.to_branch.revision_store.copy_multi(
 
140
            self.from_branch.revision_store, revs)
 
141
 
 
142
    def _fetch_weave_texts(self, revs):
 
143
        file_ids = self.from_branch.fileid_involved_by_set(revs)
 
144
        count = 0
 
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)
 
148
            count +=1
 
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())
 
153
 
 
154
            if to_weave.numversions() > 0:
 
155
                # destination has contents, must merge
 
156
                try:
 
157
                    to_weave.join(from_weave)
 
158
                except errors.WeaveParentMismatch:
 
159
                    to_weave.reweave(from_weave)
 
160
            else:
 
161
                # destination is empty, just replace it
 
162
                to_weave = from_weave.copy()
 
163
 
 
164
            self.to_weaves.put_weave(file_id, to_weave,
 
165
                self.to_branch.get_transaction())
 
166
 
 
167
        self.pb.clear()
 
168
 
 
169
    def _fetch_inventory_weave(self, revs):
 
170
        self.pb.update("inventory merge", 0, 1)
 
171
 
 
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())
 
176
 
 
177
        if to_weave.numversions() > 0:
 
178
            # destination has contents, must merge
 
179
            try:
 
180
                to_weave.join(from_weave)
 
181
            except errors.WeaveParentMismatch:
 
182
                to_weave.reweave(from_weave)
 
183
        else:
 
184
            # destination is empty, just replace it
 
185
            to_weave = from_weave.copy()
 
186
 
 
187
        self.to_control.put_weave('inventory', to_weave,
 
188
            self.to_branch.get_transaction())
 
189
 
 
190
        self.pb.clear()
128
191
 
129
192
    def _find_last_revision(self, last_revision):
130
193
        """Find the limiting source revision.
142
205
            return from_history[-1]
143
206
        else:
144
207
            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
 
 
253
208
 
254
209
fetch = Fetcher