~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: wang
  • Date: 2006-10-29 13:41:32 UTC
  • mto: (2104.4.1 wang_65714)
  • mto: This revision was merged to the branch mainline in revision 2109.
  • Revision ID: wang@ubuntu-20061029134132-3d7f4216f20c4aef
Replace python's difflib by patiencediff because the worst case 
performance is cubic for difflib and people commiting large data 
files are often hurt by this. The worst case performance of patience is 
quadratic. Fix bug 65714.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
from cStringIO import StringIO
 
19
 
 
20
from bzrlib.lazy_import import lazy_import
 
21
lazy_import(globals(), """
18
22
from copy import deepcopy
19
 
from cStringIO import StringIO
20
23
from unittest import TestSuite
21
24
from warnings import warn
22
25
 
23
26
import bzrlib
24
27
from bzrlib import (
25
 
        bzrdir, 
26
 
        errors, 
27
 
        lockdir, 
28
 
        osutils, 
29
 
        revision,
 
28
        bzrdir,
 
29
        cache_utf8,
 
30
        errors,
 
31
        lockdir,
 
32
        lockable_files,
 
33
        osutils,
 
34
        revision as _mod_revision,
30
35
        transport,
31
36
        tree,
32
37
        ui,
33
 
        urlutils
 
38
        urlutils,
34
39
        )
35
 
from bzrlib.config import TreeConfig
 
40
from bzrlib.config import BranchConfig, TreeConfig
 
41
from bzrlib.lockable_files import LockableFiles, TransportLock
 
42
""")
 
43
 
36
44
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
 
import bzrlib.errors as errors
38
 
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches, 
39
 
                           HistoryMissing, InvalidRevisionId, 
40
 
                           InvalidRevisionNumber, LockError, NoSuchFile, 
 
45
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
 
46
                           HistoryMissing, InvalidRevisionId,
 
47
                           InvalidRevisionNumber, LockError, NoSuchFile,
41
48
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
42
 
                           NotBranchError, UninitializableFormat, 
43
 
                           UnlistableStore, UnlistableBranch, 
 
49
                           NotBranchError, UninitializableFormat,
 
50
                           UnlistableStore, UnlistableBranch,
44
51
                           )
45
 
from bzrlib.lockable_files import LockableFiles, TransportLock
46
52
from bzrlib.symbol_versioning import (deprecated_function,
47
53
                                      deprecated_method,
48
54
                                      DEPRECATED_PARAMETER,
137
143
        """
138
144
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
139
145
 
 
146
    @deprecated_function(zero_eight)
140
147
    def setup_caching(self, cache_root):
141
148
        """Subclasses that care about caching should override this, and set
142
149
        up cached stores located under cache_root.
 
150
        
 
151
        NOTE: This is unused.
143
152
        """
144
 
        # seems to be unused, 2006-01-13 mbp
145
 
        warn('%s is deprecated' % self.setup_caching)
146
 
        self.cache_root = cache_root
 
153
        pass
147
154
 
148
155
    def get_config(self):
149
 
        return bzrlib.config.BranchConfig(self)
 
156
        return BranchConfig(self)
150
157
 
151
158
    def _get_nick(self):
152
159
        return self.get_config().get_nickname()
157
164
    nick = property(_get_nick, _set_nick)
158
165
 
159
166
    def is_locked(self):
160
 
        raise NotImplementedError('is_locked is abstract')
 
167
        raise NotImplementedError(self.is_locked)
161
168
 
162
169
    def lock_write(self):
163
 
        raise NotImplementedError('lock_write is abstract')
 
170
        raise NotImplementedError(self.lock_write)
164
171
 
165
172
    def lock_read(self):
166
 
        raise NotImplementedError('lock_read is abstract')
 
173
        raise NotImplementedError(self.lock_read)
167
174
 
168
175
    def unlock(self):
169
 
        raise NotImplementedError('unlock is abstract')
 
176
        raise NotImplementedError(self.unlock)
170
177
 
171
178
    def peek_lock_mode(self):
172
179
        """Return lock mode for the Branch: 'r', 'w' or None"""
173
180
        raise NotImplementedError(self.peek_lock_mode)
174
181
 
175
182
    def get_physical_lock_status(self):
176
 
        raise NotImplementedError('get_physical_lock_status is abstract')
 
183
        raise NotImplementedError(self.get_physical_lock_status)
177
184
 
178
185
    def abspath(self, name):
179
186
        """Return absolute filename for something in the branch
181
188
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
182
189
        method and not a tree method.
183
190
        """
184
 
        raise NotImplementedError('abspath is abstract')
 
191
        raise NotImplementedError(self.abspath)
185
192
 
186
193
    def bind(self, other):
187
194
        """Bind the local branch the other branch.
220
227
                    last_revision = from_history[-1]
221
228
                else:
222
229
                    # no history in the source branch
223
 
                    last_revision = revision.NULL_REVISION
 
230
                    last_revision = _mod_revision.NULL_REVISION
224
231
            return self.repository.fetch(from_branch.repository,
225
232
                                         revision_id=last_revision,
226
233
                                         pb=nested_pb)
278
285
 
279
286
    def get_root_id(self):
280
287
        """Return the id of this branches root"""
281
 
        raise NotImplementedError('get_root_id is abstract')
 
288
        raise NotImplementedError(self.get_root_id)
282
289
 
283
290
    def print_file(self, file, revision_id):
284
291
        """Print `file` to stdout."""
285
 
        raise NotImplementedError('print_file is abstract')
 
292
        raise NotImplementedError(self.print_file)
286
293
 
287
294
    def append_revision(self, *revision_ids):
288
 
        raise NotImplementedError('append_revision is abstract')
 
295
        raise NotImplementedError(self.append_revision)
289
296
 
290
297
    def set_revision_history(self, rev_history):
291
 
        raise NotImplementedError('set_revision_history is abstract')
 
298
        raise NotImplementedError(self.set_revision_history)
292
299
 
293
300
    def revision_history(self):
294
301
        """Return sequence of revision hashes on to this branch."""
295
 
        raise NotImplementedError('revision_history is abstract')
 
302
        raise NotImplementedError(self.revision_history)
296
303
 
297
304
    def revno(self):
298
305
        """Return current revision number for this branch.
307
314
        raise errors.UpgradeRequired(self.base)
308
315
 
309
316
    def last_revision(self):
310
 
        """Return last patch hash, or None if no history."""
 
317
        """Return last revision id, or None"""
311
318
        ph = self.revision_history()
312
319
        if ph:
313
320
            return ph[-1]
344
351
        :param stop_revision: Updated until the given revision
345
352
        :return: None
346
353
        """
347
 
        raise NotImplementedError('update_revisions is abstract')
 
354
        raise NotImplementedError(self.update_revisions)
348
355
 
349
356
    def revision_id_to_revno(self, revision_id):
350
357
        """Given a revision id, return its revno"""
362
369
            return None
363
370
        if history is None:
364
371
            history = self.revision_history()
365
 
        elif revno <= 0 or revno > len(history):
 
372
        if revno <= 0 or revno > len(history):
366
373
            raise bzrlib.errors.NoSuchRevision(self, revno)
367
374
        return history[revno - 1]
368
375
 
369
376
    def pull(self, source, overwrite=False, stop_revision=None):
370
 
        raise NotImplementedError('pull is abstract')
 
377
        raise NotImplementedError(self.pull)
371
378
 
372
379
    def basis_tree(self):
373
380
        """Return `Tree` object for last revision."""
378
385
 
379
386
        This can change the directory or the filename or both.
380
387
        """
381
 
        raise NotImplementedError('rename_one is abstract')
 
388
        raise NotImplementedError(self.rename_one)
382
389
 
383
390
    def move(self, from_paths, to_name):
384
391
        """Rename files.
394
401
        This returns a list of (from_path, to_path) pairs for each
395
402
        entry that is moved.
396
403
        """
397
 
        raise NotImplementedError('move is abstract')
 
404
        raise NotImplementedError(self.move)
398
405
 
399
406
    def get_parent(self):
400
407
        """Return the parent location of the branch.
403
410
        pattern is that the user can override it by specifying a
404
411
        location.
405
412
        """
406
 
        raise NotImplementedError('get_parent is abstract')
 
413
        raise NotImplementedError(self.get_parent)
407
414
 
408
415
    def get_submit_branch(self):
409
416
        """Return the submit location of the branch.
425
432
 
426
433
    def get_push_location(self):
427
434
        """Return the None or the location to push this branch to."""
428
 
        raise NotImplementedError('get_push_location is abstract')
 
435
        raise NotImplementedError(self.get_push_location)
429
436
 
430
437
    def set_push_location(self, location):
431
438
        """Set a new push location for this branch."""
432
 
        raise NotImplementedError('set_push_location is abstract')
 
439
        raise NotImplementedError(self.set_push_location)
433
440
 
434
441
    def set_parent(self, url):
435
 
        raise NotImplementedError('set_parent is abstract')
 
442
        raise NotImplementedError(self.set_parent)
436
443
 
437
444
    @needs_write_lock
438
445
    def update(self):
579
586
            mainline_parent_id = revision_id
580
587
        return BranchCheckResult(self)
581
588
 
 
589
    def _get_checkout_format(self):
 
590
        """Return the most suitable metadir for a checkout of this branch.
 
591
        Weaves are used if this branch's repostory uses weaves.
 
592
        """
 
593
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
594
            from bzrlib import repository
 
595
            format = bzrdir.BzrDirMetaFormat1()
 
596
            format.repository_format = repository.RepositoryFormat7()
 
597
        else:
 
598
            format = self.repository.bzrdir.cloning_metadir()
 
599
        return format
 
600
 
582
601
    def create_checkout(self, to_location, revision_id=None, 
583
602
                        lightweight=False):
584
603
        """Create a checkout of a branch.
589
608
        produce a bound branch (heavyweight checkout)
590
609
        :return: The tree of the created checkout
591
610
        """
 
611
        t = transport.get_transport(to_location)
 
612
        try:
 
613
            t.mkdir('.')
 
614
        except errors.FileExists:
 
615
            pass
592
616
        if lightweight:
593
 
            t = transport.get_transport(to_location)
594
 
            try:
595
 
                t.mkdir('.')
596
 
            except errors.FileExists:
597
 
                pass
598
617
            checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
599
618
            BranchReferenceFormat().initialize(checkout, self)
600
619
        else:
 
620
            format = self._get_checkout_format()
601
621
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
602
 
                to_location, force_new_tree=False)
 
622
                to_location, force_new_tree=False, format=format)
603
623
            checkout = checkout_branch.bzrdir
604
624
            checkout_branch.bind(self)
605
 
            if revision_id is not None:
606
 
                rh = checkout_branch.revision_history()
607
 
                new_rh = rh[:rh.index(revision_id) + 1]
608
 
                checkout_branch.set_revision_history(new_rh)
 
625
            # pull up to the specified revision_id to set the initial 
 
626
            # branch tip correctly, and seed it with history.
 
627
            checkout_branch.pull(self, stop_revision=revision_id)
609
628
        return checkout.create_workingtree(revision_id)
610
629
 
611
630
 
715
734
        utf8_files = [('revision-history', ''),
716
735
                      ('branch-name', ''),
717
736
                      ]
718
 
        control_files = LockableFiles(branch_transport, 'branch-lock',
719
 
                                      TransportLock)
 
737
        control_files = lockable_files.LockableFiles(branch_transport,
 
738
                             'branch-lock', lockable_files.TransportLock)
720
739
        control_files.create_lock()
721
740
        control_files.lock_write()
722
741
        try:
776
795
        utf8_files = [('revision-history', ''),
777
796
                      ('branch-name', ''),
778
797
                      ]
779
 
        control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
 
798
        control_files = lockable_files.LockableFiles(branch_transport, 'lock',
 
799
                                                     lockdir.LockDir)
780
800
        control_files.create_lock()
781
801
        control_files.lock_write()
782
802
        control_files.put_utf8('format', self.get_format_string())
801
821
            format = BranchFormat.find_format(a_bzrdir)
802
822
            assert format.__class__ == self.__class__
803
823
        transport = a_bzrdir.get_branch_transport(None)
804
 
        control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
 
824
        control_files = lockable_files.LockableFiles(transport, 'lock',
 
825
                                                     lockdir.LockDir)
805
826
        return BzrBranch5(_format=self,
806
827
                          _control_files=control_files,
807
828
                          a_bzrdir=a_bzrdir,
838
859
            raise errors.UninitializableFormat(self)
839
860
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
840
861
        branch_transport = a_bzrdir.get_branch_transport(self)
841
 
        # FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
842
 
        branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
843
 
        branch_transport.put('format', StringIO(self.get_format_string()))
 
862
        branch_transport.put_bytes('location',
 
863
            target_branch.bzrdir.root_transport.base)
 
864
        branch_transport.put_bytes('format', self.get_format_string())
844
865
        return self.open(a_bzrdir, _found=True)
845
866
 
846
867
    def __init__(self):
957
978
 
958
979
    __repr__ = __str__
959
980
 
960
 
    def __del__(self):
961
 
        # TODO: It might be best to do this somewhere else,
962
 
        # but it is nice for a Branch object to automatically
963
 
        # cache it's information.
964
 
        # Alternatively, we could have the Transport objects cache requests
965
 
        # See the earlier discussion about how major objects (like Branch)
966
 
        # should never expect their __del__ function to run.
967
 
        # XXX: cache_root seems to be unused, 2006-01-13 mbp
968
 
        if hasattr(self, 'cache_root') and self.cache_root is not None:
969
 
            try:
970
 
                osutils.rmtree(self.cache_root)
971
 
            except:
972
 
                pass
973
 
            self.cache_root = None
974
 
 
975
981
    def _get_base(self):
976
982
        return self._base
977
983
 
1100
1106
        transaction = self.get_transaction()
1101
1107
        history = transaction.map.find_revision_history()
1102
1108
        if history is not None:
1103
 
            mutter("cache hit for revision-history in %s", self)
 
1109
            # mutter("cache hit for revision-history in %s", self)
1104
1110
            return list(history)
1105
 
        history = [l.rstrip('\r\n') for l in
1106
 
                self.control_files.get_utf8('revision-history').readlines()]
 
1111
        decode_utf8 = cache_utf8.decode
 
1112
        history = [decode_utf8(l.rstrip('\r\n')) for l in
 
1113
                self.control_files.get('revision-history').readlines()]
1107
1114
        transaction.map.add_revision_history(history)
1108
1115
        # this call is disabled because revision_history is 
1109
1116
        # not really an object yet, and the transaction is for objects.
1129
1136
        # make a new revision history from the graph
1130
1137
        current_rev_id = revision_id
1131
1138
        new_history = []
1132
 
        while current_rev_id not in (None, revision.NULL_REVISION):
 
1139
        while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1133
1140
            new_history.append(current_rev_id)
1134
1141
            current_rev_id_parents = stop_graph[current_rev_id]
1135
1142
            try:
1185
1192
        try:
1186
1193
            old_count = len(self.revision_history())
1187
1194
            try:
1188
 
                self.update_revisions(source,stop_revision)
 
1195
                self.update_revisions(source, stop_revision)
1189
1196
            except DivergedBranches:
1190
1197
                if not overwrite:
1191
1198
                    raise
1246
1253
                        "use bzrlib.urlutils.escape")
1247
1254
                    
1248
1255
            url = urlutils.relative_url(self.base, url)
1249
 
            self.control_files.put('parent', url + '\n')
 
1256
            self.control_files.put('parent', StringIO(url + '\n'))
1250
1257
 
1251
1258
    @deprecated_function(zero_nine)
1252
1259
    def tree_config(self):
1326
1333
 
1327
1334
    @needs_write_lock
1328
1335
    def bind(self, other):
1329
 
        """Bind the local branch the other branch.
 
1336
        """Bind this branch to the branch other.
1330
1337
 
 
1338
        This does not push or pull data between the branches, though it does
 
1339
        check for divergence to raise an error when the branches are not
 
1340
        either the same, or one a prefix of the other. That behaviour may not
 
1341
        be useful, so that check may be removed in future.
 
1342
        
1331
1343
        :param other: The branch to bind to
1332
1344
        :type other: Branch
1333
1345
        """
1338
1350
        #       but binding itself may not be.
1339
1351
        #       Since we *have* to check at commit time, we don't
1340
1352
        #       *need* to check here
1341
 
        self.pull(other)
1342
 
 
1343
 
        # we are now equal to or a suffix of other.
1344
 
 
1345
 
        # Since we have 'pulled' from the remote location,
1346
 
        # now we should try to pull in the opposite direction
1347
 
        # in case the local tree has more revisions than the
1348
 
        # remote one.
1349
 
        # There may be a different check you could do here
1350
 
        # rather than actually trying to install revisions remotely.
1351
 
        # TODO: capture an exception which indicates the remote branch
1352
 
        #       is not writable. 
1353
 
        #       If it is up-to-date, this probably should not be a failure
1354
 
        
1355
 
        # lock other for write so the revision-history syncing cannot race
1356
 
        other.lock_write()
1357
 
        try:
1358
 
            other.pull(self)
1359
 
            # if this does not error, other now has the same last rev we do
1360
 
            # it can only error if the pull from other was concurrent with
1361
 
            # a commit to other from someone else.
1362
 
 
1363
 
            # until we ditch revision-history, we need to sync them up:
1364
 
            self.set_revision_history(other.revision_history())
1365
 
            # now other and self are up to date with each other and have the
1366
 
            # same revision-history.
1367
 
        finally:
1368
 
            other.unlock()
1369
 
 
 
1353
 
 
1354
        # we want to raise diverged if:
 
1355
        # last_rev is not in the other_last_rev history, AND
 
1356
        # other_last_rev is not in our history, and do it without pulling
 
1357
        # history around
 
1358
        last_rev = self.last_revision()
 
1359
        if last_rev is not None:
 
1360
            other.lock_read()
 
1361
            try:
 
1362
                other_last_rev = other.last_revision()
 
1363
                if other_last_rev is not None:
 
1364
                    # neither branch is new, we have to do some work to
 
1365
                    # ascertain diversion.
 
1366
                    remote_graph = other.repository.get_revision_graph(
 
1367
                        other_last_rev)
 
1368
                    local_graph = self.repository.get_revision_graph(last_rev)
 
1369
                    if (last_rev not in remote_graph and
 
1370
                        other_last_rev not in local_graph):
 
1371
                        raise errors.DivergedBranches(self, other)
 
1372
            finally:
 
1373
                other.unlock()
1370
1374
        self.set_bound_location(other.base)
1371
1375
 
1372
1376
    @needs_write_lock