~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/fetch.py

Merge integration.

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))
93
94
        
94
95
        self.to_branch = to_branch
95
96
        self.from_branch = from_branch
127
128
        self.copied_file_ids = set()
128
129
        self.file_ids_names = {}
129
130
        try:
130
 
            self._fetch_revisions()
 
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)
131
138
        finally:
132
139
            self.pb.clear()
133
140
 
134
 
    def _fetch_revisions(self):
 
141
    def _revids_to_fetch(self):
135
142
        self._find_last_revision()
136
143
        mutter('fetch up to rev {%s}', self._last_revision)
137
144
        if (self._last_revision is not None and 
138
145
            self.to_repository.has_revision(self._last_revision)):
139
146
            return
140
147
        try:
141
 
            revs_to_fetch = self._compare_ancestries()
 
148
            branch_from_revs = set(self.from_repository.get_ancestry(self._last_revision))
142
149
        except WeaveError:
143
150
            raise InstallFailed([self._last_revision])
144
 
        self._copy_revisions(revs_to_fetch)
145
 
        self.new_ancestry = revs_to_fetch
 
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 merge", 0, 1)
 
190
        from_weave = self.from_repository.get_inventory_weave()
 
191
        to_weave = self.to_repository.get_inventory_weave()
 
192
 
 
193
        if to_weave.numversions() > 0:
 
194
            # destination has contents, must merge
 
195
            try:
 
196
                to_weave.join(from_weave)
 
197
            except errors.WeaveParentMismatch:
 
198
                to_weave.reweave(from_weave)
 
199
        else:
 
200
            # destination is empty, just replace it
 
201
            to_weave = from_weave.copy()
 
202
 
 
203
        self.to_control.put_weave('inventory', to_weave,
 
204
            self.to_branch.get_transaction())
 
205
 
 
206
        self.pb.clear()
146
207
 
147
208
    def _find_last_revision(self):
148
209
        """Find the limiting source revision.
162
223
            # no history in the source branch
163
224
            self._last_revision = None
164
225
 
165
 
    def _compare_ancestries(self):
166
 
        """Get a list of revisions that must be copied.
167
 
 
168
 
        That is, every revision that's in the ancestry of the source
169
 
        branch and not in the destination branch."""
170
 
        self.pb.update('get source ancestry')
171
 
        from_repository = self.from_branch.repository
172
 
        self.from_ancestry = from_repository.get_ancestry(self._last_revision)
173
 
 
174
 
        dest_last_rev = self.to_branch.last_revision()
175
 
        self.pb.update('get destination ancestry')
176
 
        if dest_last_rev:
177
 
            to_repository = self.to_branch.repository
178
 
            dest_ancestry = to_repository.get_ancestry(dest_last_rev)
179
 
        else:
180
 
            dest_ancestry = []
181
 
        ss = set(dest_ancestry)
182
 
        to_fetch = []
183
 
        for rev_id in self.from_ancestry:
184
 
            if rev_id not in ss:
185
 
                to_fetch.append(rev_id)
186
 
                mutter('need to get revision {%s}', rev_id)
187
 
        mutter('need to get %d revisions in total', len(to_fetch))
188
 
        self.count_total = len(to_fetch)
189
 
        return to_fetch
190
 
 
191
 
    def _copy_revisions(self, revs_to_fetch):
192
 
        i = 0
193
 
        for rev_id in revs_to_fetch:
194
 
            i += 1
195
 
            if rev_id is None:
196
 
                continue
197
 
            if self.to_repository.has_revision(rev_id):
198
 
                continue
199
 
            self.pb.update('copy revision', i, self.count_total)
200
 
            self._copy_one_revision(rev_id)
201
 
            self.count_copied += 1
202
 
 
203
 
 
204
 
    def _copy_one_revision(self, rev_id):
205
 
        """Copy revision and everything referenced by it."""
206
 
        mutter('copying revision {%s}', rev_id)
207
 
        rev_xml = self.from_repository.get_revision_xml(rev_id)
208
 
        inv_xml = self.from_repository.get_inventory_xml(rev_id)
209
 
        rev = serializer_v5.read_revision_from_string(rev_xml)
210
 
        inv = serializer_v5.read_inventory_from_string(inv_xml)
211
 
        assert rev.revision_id == rev_id
212
 
        assert rev.inventory_sha1 == sha_string(inv_xml)
213
 
        mutter('  commiter %s, %d parents',
214
 
               rev.committer,
215
 
               len(rev.parent_ids))
216
 
        self._copy_new_texts(rev_id, inv)
217
 
        parents = rev.parent_ids
218
 
        new_parents = copy(parents)
219
 
        for parent in parents:
220
 
            if not self.to_repository.has_revision(parent):
221
 
                new_parents.pop(new_parents.index(parent))
222
 
        self._copy_inventory(rev_id, inv_xml, new_parents)
223
 
        self.to_repository.revision_store.add(StringIO(rev_xml), rev_id)
224
 
        mutter('copied revision %s', rev_id)
225
 
 
226
 
    def _copy_inventory(self, rev_id, inv_xml, parent_ids):
227
 
        self.to_control.add_text('inventory', rev_id,
228
 
                                split_lines(inv_xml), parent_ids,
229
 
                                self.to_repository.get_transaction())
230
 
 
231
 
    def _copy_new_texts(self, rev_id, inv):
232
 
        """Copy any new texts occuring in this revision."""
233
 
        # TODO: Rather than writing out weaves every time, hold them
234
 
        # in memory until everything's done?  But this way is nicer
235
 
        # if it's interrupted.
236
 
        for path, ie in inv.iter_entries():
237
 
            self._copy_one_weave(rev_id, ie.file_id, ie.revision)
238
 
 
239
 
    def _copy_one_weave(self, rev_id, file_id, text_revision):
240
 
        """Copy one file weave, esuring the result contains text_revision."""
241
 
        # check if the revision is already there
242
 
        if file_id in self.file_ids_names.keys( ) and \
243
 
            text_revision in self.file_ids_names[file_id]:
244
 
                return        
245
 
        to_weave = self.to_weaves.get_weave_or_empty(file_id,
246
 
            self.to_repository.get_transaction())
247
 
        if not file_id in self.file_ids_names.keys( ):
248
 
            self.file_ids_names[file_id] = to_weave.names( )
249
 
        if text_revision in to_weave:
250
 
            return
251
 
        from_weave = self.from_weaves.get_weave(file_id,
252
 
            self.from_branch.repository.get_transaction())
253
 
        if text_revision not in from_weave:
254
 
            raise MissingText(self.from_branch, text_revision, file_id)
255
 
        mutter('copy file {%s} modified in {%s}', file_id, rev_id)
256
 
 
257
 
        if to_weave.numversions() > 0:
258
 
            # destination has contents, must merge
259
 
            try:
260
 
                to_weave.join(from_weave)
261
 
            except errors.WeaveParentMismatch:
262
 
                to_weave.reweave(from_weave)
263
 
        else:
264
 
            # destination is empty, just replace it
265
 
            to_weave = from_weave.copy( )
266
 
        self.to_weaves.put_weave(file_id, to_weave,
267
 
            self.to_repository.get_transaction())
268
 
        self.count_weaves += 1
269
 
        self.copied_file_ids.add(file_id)
270
 
        self.file_ids_names[file_id] = to_weave.names()
271
 
        mutter('copied file {%s}', file_id)
272
 
 
273
 
 
274
226
fetch = Fetcher