~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/fetch.py

  • Committer: Martin Pool
  • Date: 2006-03-06 11:20:10 UTC
  • mfrom: (1593 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1611.
  • Revision ID: mbp@sourcefrog.net-20060306112010-17c0170dde5d1eea
[merge] large merge to sync with bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
16
 
17
 
from copy import copy
18
 
import os
19
 
from cStringIO import StringIO
20
 
 
21
 
import bzrlib
22
 
import bzrlib.errors as errors
23
 
from bzrlib.errors import (InstallFailed, NoSuchRevision, WeaveError,
24
 
                           MissingText)
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
30
17
 
31
18
"""Copying of history from one branch to another.
32
19
 
43
30
memory until we've updated all of the files referenced.
44
31
"""
45
32
 
 
33
import bzrlib
 
34
import bzrlib.errors as errors
 
35
from bzrlib.errors import (InstallFailed, NoSuchRevision, WeaveError,
 
36
                           MissingText)
 
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 *
 
42
 
 
43
 
46
44
# TODO: Avoid repeatedly opening weaves so many times.
47
45
 
48
46
# XXX: This doesn't handle ghost (not present in branch) revisions at
61
59
#   and add in all file versions
62
60
 
63
61
 
64
 
 
 
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
68
67
 
69
 
 
70
 
 
71
 
class Fetcher(object):
72
 
    """Pull revisions and texts from one branch to another.
73
 
 
74
 
    This doesn't update the destination's history; that can be done
75
 
    separately if desired.  
76
 
 
77
 
    revision_limit
78
 
        If set, pull only up to this revision_id.
79
 
 
80
 
    After running:
81
 
 
82
 
    last_revision -- if last_revision
83
 
        is given it will be that, otherwise the last revision of
84
 
        from_branch
85
 
 
 
68
fetch = greedy_fetch
 
69
 
 
70
 
 
71
class RepoFetcher(object):
 
72
    """Pull revisions and texts from one repository to another.
 
73
 
 
74
    last_revision
 
75
        if set, try to limit to the data this revision references.
 
76
 
 
77
    after running:
86
78
    count_copied -- number of revisions copied
87
79
 
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().
89
82
    """
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))
94
 
        
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):
 
84
        # result variables.
 
85
        self.failed_revisions = []
 
86
        self.count_copied = 0
 
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)
 
91
            return
 
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
98
96
        if pb is None:
99
97
            self.pb = bzrlib.ui.ui_factory.progress_bar()
100
98
        else:
101
99
            self.pb = pb
102
 
        self.from_branch.lock_read()
 
100
        self.from_repository.lock_read()
103
101
        try:
104
 
            self.to_branch.lock_write()
 
102
            self.to_repository.lock_write()
105
103
            try:
106
104
                self.__fetch()
107
105
            finally:
108
 
                self.to_branch.unlock()
 
106
                self.to_repository.unlock()
109
107
        finally:
110
 
            self.from_branch.unlock()
 
108
            self.from_repository.unlock()
111
109
 
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.
117
115
        """
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 = {}
130
122
        try:
131
123
            revs = self._revids_to_fetch()
139
131
            self.pb.clear()
140
132
 
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
 
138
            return None
 
139
        if (self._last_revision != None and
145
140
            self.to_repository.has_revision(self._last_revision)):
146
 
            return
 
141
            return None
 
142
            
147
143
        try:
148
 
            branch_from_revs = set(self.from_repository.get_ancestry(self._last_revision))
149
 
        except WeaveError:
 
144
            return self.to_repository.missing_revision_ids(self.from_repository,
 
145
                                                           self._last_revision)
 
146
        except errors.NoSuchRevision:
150
147
            raise InstallFailed([self._last_revision])
151
148
 
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
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,
 
152
            revs,
 
153
            pb=self.pb)
 
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.
 
157
        # so we just-do-it.
 
158
        # FIXME: the generic code path should not need this, if it truely is
 
159
        # generic.
 
160
        reconciler = RepoReconciler(self.to_repository)
 
161
        reconciler.reconcile()
160
162
 
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)
163
165
        count = 0
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)
167
169
            count +=1
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())
172
174
 
173
175
            if to_weave.numversions() > 0:
174
176
                # destination has contents, must merge
181
183
                to_weave = from_weave.copy()
182
184
 
183
185
            self.to_weaves.put_weave(file_id, to_weave,
184
 
                self.to_branch.get_transaction())
185
 
 
 
186
                self.to_repository.get_transaction())
186
187
        self.pb.clear()
187
188
 
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)
196
197
 
197
 
        if to_weave.numversions() > 0:
 
198
        if self.to_inventory_weave.numversions() > 0:
198
199
            # destination has contents, must merge
199
200
            try:
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')
203
204
        else:
204
205
            # destination is empty, just replace it
205
 
            to_weave = from_weave.copy()
 
206
            self.to_inventory_weave = from_weave.copy()
206
207
 
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())
209
211
 
210
212
        self.pb.clear()
211
213
 
212
 
    def _find_last_revision(self):
213
 
        """Find the limiting source revision.
214
 
 
215
 
        Every ancestor of that revision will be merged across.
216
 
 
217
 
        Returns the revision_id, or returns None if there's no history
218
 
        in the source branch."""
219
 
        if self._last_revision:
220
 
            return
221
 
        self.pb.update('get source history')
222
 
        from_history = self.from_branch.revision_history()
223
 
        self.pb.update('get destination history')
224
 
        if from_history:
225
 
            self._last_revision = from_history[-1]
226
 
        else:
227
 
            # no history in the source branch
228
 
            self._last_revision = None
229
 
 
230
 
fetch = Fetcher
 
214
 
 
215
class Fetcher(object):
 
216
    """Backwards compatability glue for branch.fetch()."""
 
217
 
 
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)