~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-31 16:12:57 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060731161257-91a231523255332c
new official bzr.ico

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
from warnings import warn
22
22
 
23
23
import bzrlib
24
 
from bzrlib import (
25
 
        bzrdir,
26
 
        cache_utf8,
27
 
        errors,
28
 
        lockdir,
29
 
        osutils,
30
 
        revision,
31
 
        transport,
32
 
        tree,
33
 
        ui,
34
 
        urlutils,
35
 
        )
 
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
 
25
        tree, \
 
26
        ui, \
 
27
        urlutils
36
28
from bzrlib.config import TreeConfig
37
29
from bzrlib.decorators import needs_read_lock, needs_write_lock
38
30
import bzrlib.errors as errors
138
130
        """
139
131
        return bzrdir.BzrDir.create_standalone_workingtree(base).branch
140
132
 
141
 
    @deprecated_function(zero_eight)
142
133
    def setup_caching(self, cache_root):
143
134
        """Subclasses that care about caching should override this, and set
144
135
        up cached stores located under cache_root.
145
 
        
146
 
        NOTE: This is unused.
147
136
        """
148
 
        pass
 
137
        # seems to be unused, 2006-01-13 mbp
 
138
        warn('%s is deprecated' % self.setup_caching)
 
139
        self.cache_root = cache_root
149
140
 
150
141
    def get_config(self):
151
142
        return bzrlib.config.BranchConfig(self)
159
150
    nick = property(_get_nick, _set_nick)
160
151
 
161
152
    def is_locked(self):
162
 
        raise NotImplementedError(self.is_locked)
 
153
        raise NotImplementedError('is_locked is abstract')
163
154
 
164
155
    def lock_write(self):
165
 
        raise NotImplementedError(self.lock_write)
 
156
        raise NotImplementedError('lock_write is abstract')
166
157
 
167
158
    def lock_read(self):
168
 
        raise NotImplementedError(self.lock_read)
 
159
        raise NotImplementedError('lock_read is abstract')
169
160
 
170
161
    def unlock(self):
171
 
        raise NotImplementedError(self.unlock)
 
162
        raise NotImplementedError('unlock is abstract')
172
163
 
173
164
    def peek_lock_mode(self):
174
165
        """Return lock mode for the Branch: 'r', 'w' or None"""
175
166
        raise NotImplementedError(self.peek_lock_mode)
176
167
 
177
168
    def get_physical_lock_status(self):
178
 
        raise NotImplementedError(self.get_physical_lock_status)
 
169
        raise NotImplementedError('get_physical_lock_status is abstract')
179
170
 
180
171
    def abspath(self, name):
181
172
        """Return absolute filename for something in the branch
183
174
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
184
175
        method and not a tree method.
185
176
        """
186
 
        raise NotImplementedError(self.abspath)
 
177
        raise NotImplementedError('abspath is abstract')
187
178
 
188
179
    def bind(self, other):
189
180
        """Bind the local branch the other branch.
280
271
 
281
272
    def get_root_id(self):
282
273
        """Return the id of this branches root"""
283
 
        raise NotImplementedError(self.get_root_id)
 
274
        raise NotImplementedError('get_root_id is abstract')
284
275
 
285
276
    def print_file(self, file, revision_id):
286
277
        """Print `file` to stdout."""
287
 
        raise NotImplementedError(self.print_file)
 
278
        raise NotImplementedError('print_file is abstract')
288
279
 
289
280
    def append_revision(self, *revision_ids):
290
 
        raise NotImplementedError(self.append_revision)
 
281
        raise NotImplementedError('append_revision is abstract')
291
282
 
292
283
    def set_revision_history(self, rev_history):
293
 
        raise NotImplementedError(self.set_revision_history)
 
284
        raise NotImplementedError('set_revision_history is abstract')
294
285
 
295
286
    def revision_history(self):
296
287
        """Return sequence of revision hashes on to this branch."""
297
 
        raise NotImplementedError(self.revision_history)
 
288
        raise NotImplementedError('revision_history is abstract')
298
289
 
299
290
    def revno(self):
300
291
        """Return current revision number for this branch.
309
300
        raise errors.UpgradeRequired(self.base)
310
301
 
311
302
    def last_revision(self):
312
 
        """Return last revision id, or None"""
 
303
        """Return last patch hash, or None if no history."""
313
304
        ph = self.revision_history()
314
305
        if ph:
315
306
            return ph[-1]
346
337
        :param stop_revision: Updated until the given revision
347
338
        :return: None
348
339
        """
349
 
        raise NotImplementedError(self.update_revisions)
 
340
        raise NotImplementedError('update_revisions is abstract')
350
341
 
351
342
    def revision_id_to_revno(self, revision_id):
352
343
        """Given a revision id, return its revno"""
364
355
            return None
365
356
        if history is None:
366
357
            history = self.revision_history()
367
 
        if revno <= 0 or revno > len(history):
 
358
        elif revno <= 0 or revno > len(history):
368
359
            raise bzrlib.errors.NoSuchRevision(self, revno)
369
360
        return history[revno - 1]
370
361
 
371
362
    def pull(self, source, overwrite=False, stop_revision=None):
372
 
        raise NotImplementedError(self.pull)
 
363
        raise NotImplementedError('pull is abstract')
373
364
 
374
365
    def basis_tree(self):
375
366
        """Return `Tree` object for last revision."""
380
371
 
381
372
        This can change the directory or the filename or both.
382
373
        """
383
 
        raise NotImplementedError(self.rename_one)
 
374
        raise NotImplementedError('rename_one is abstract')
384
375
 
385
376
    def move(self, from_paths, to_name):
386
377
        """Rename files.
396
387
        This returns a list of (from_path, to_path) pairs for each
397
388
        entry that is moved.
398
389
        """
399
 
        raise NotImplementedError(self.move)
 
390
        raise NotImplementedError('move is abstract')
400
391
 
401
392
    def get_parent(self):
402
393
        """Return the parent location of the branch.
405
396
        pattern is that the user can override it by specifying a
406
397
        location.
407
398
        """
408
 
        raise NotImplementedError(self.get_parent)
 
399
        raise NotImplementedError('get_parent is abstract')
409
400
 
410
401
    def get_submit_branch(self):
411
402
        """Return the submit location of the branch.
427
418
 
428
419
    def get_push_location(self):
429
420
        """Return the None or the location to push this branch to."""
430
 
        raise NotImplementedError(self.get_push_location)
 
421
        raise NotImplementedError('get_push_location is abstract')
431
422
 
432
423
    def set_push_location(self, location):
433
424
        """Set a new push location for this branch."""
434
 
        raise NotImplementedError(self.set_push_location)
 
425
        raise NotImplementedError('set_push_location is abstract')
435
426
 
436
427
    def set_parent(self, url):
437
 
        raise NotImplementedError(self.set_parent)
 
428
        raise NotImplementedError('set_parent is abstract')
438
429
 
439
430
    @needs_write_lock
440
431
    def update(self):
543
534
                rev = self.repository.get_revision(revision_id)
544
535
                new_history = rev.get_history(self.repository)[1:]
545
536
        destination.set_revision_history(new_history)
546
 
        try:
547
 
            parent = self.get_parent()
548
 
        except errors.InaccessibleParent, e:
549
 
            mutter('parent was not accessible to copy: %s', e)
550
 
        else:
551
 
            if parent:
552
 
                destination.set_parent(parent)
 
537
        parent = self.get_parent()
 
538
        if parent:
 
539
            destination.set_parent(parent)
553
540
 
554
541
    @needs_read_lock
555
542
    def check(self):
581
568
            mainline_parent_id = revision_id
582
569
        return BranchCheckResult(self)
583
570
 
584
 
    def _get_checkout_format(self):
585
 
        """Return the most suitable metadir for a checkout of this branch.
586
 
        Weaves are used if this branch's repostory uses weaves.
587
 
        """
588
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
589
 
            from bzrlib import repository
590
 
            format = bzrdir.BzrDirMetaFormat1()
591
 
            format.repository_format = repository.RepositoryFormat7()
592
 
        else:
593
 
            format = self.repository.bzrdir.cloning_metadir()
594
 
        return format
595
 
 
596
 
    def create_checkout(self, to_location, revision_id=None, 
597
 
                        lightweight=False):
598
 
        """Create a checkout of a branch.
599
 
        
600
 
        :param to_location: The url to produce the checkout at
601
 
        :param revision_id: The revision to check out
602
 
        :param lightweight: If True, produce a lightweight checkout, otherwise,
603
 
        produce a bound branch (heavyweight checkout)
604
 
        :return: The tree of the created checkout
605
 
        """
606
 
        t = transport.get_transport(to_location)
607
 
        try:
608
 
            t.mkdir('.')
609
 
        except errors.FileExists:
610
 
            pass
611
 
        if lightweight:
612
 
            checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
613
 
            BranchReferenceFormat().initialize(checkout, self)
614
 
        else:
615
 
            format = self._get_checkout_format()
616
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
617
 
                to_location, force_new_tree=False, format=format)
618
 
            checkout = checkout_branch.bzrdir
619
 
            checkout_branch.bind(self)
620
 
            # pull up to the specified revision_id to set the initial 
621
 
            # branch tip correctly, and seed it with history.
622
 
            checkout_branch.pull(self, stop_revision=revision_id)
623
 
        return checkout.create_workingtree(revision_id)
624
 
 
625
571
 
626
572
class BranchFormat(object):
627
573
    """An encapsulation of the initialization and open routines for a format.
852
798
            raise errors.UninitializableFormat(self)
853
799
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
854
800
        branch_transport = a_bzrdir.get_branch_transport(self)
855
 
        branch_transport.put_bytes('location',
856
 
            target_branch.bzrdir.root_transport.base)
857
 
        branch_transport.put_bytes('format', self.get_format_string())
 
801
        # FIXME rbc 20060209 one j-a-ms encoding branch lands this str() cast is not needed.
 
802
        branch_transport.put('location', StringIO(str(target_branch.bzrdir.root_transport.base)))
 
803
        branch_transport.put('format', StringIO(self.get_format_string()))
858
804
        return self.open(a_bzrdir, _found=True)
859
805
 
860
806
    def __init__(self):
971
917
 
972
918
    __repr__ = __str__
973
919
 
 
920
    def __del__(self):
 
921
        # TODO: It might be best to do this somewhere else,
 
922
        # but it is nice for a Branch object to automatically
 
923
        # cache it's information.
 
924
        # Alternatively, we could have the Transport objects cache requests
 
925
        # See the earlier discussion about how major objects (like Branch)
 
926
        # should never expect their __del__ function to run.
 
927
        # XXX: cache_root seems to be unused, 2006-01-13 mbp
 
928
        if hasattr(self, 'cache_root') and self.cache_root is not None:
 
929
            try:
 
930
                osutils.rmtree(self.cache_root)
 
931
            except:
 
932
                pass
 
933
            self.cache_root = None
 
934
 
974
935
    def _get_base(self):
975
936
        return self._base
976
937
 
1099
1060
        transaction = self.get_transaction()
1100
1061
        history = transaction.map.find_revision_history()
1101
1062
        if history is not None:
1102
 
            # mutter("cache hit for revision-history in %s", self)
 
1063
            mutter("cache hit for revision-history in %s", self)
1103
1064
            return list(history)
1104
 
        decode_utf8 = cache_utf8.decode
1105
 
        history = [decode_utf8(l.rstrip('\r\n')) for l in
1106
 
                self.control_files.get('revision-history').readlines()]
 
1065
        history = [l.rstrip('\r\n') for l in
 
1066
                self.control_files.get_utf8('revision-history').readlines()]
1107
1067
        transaction.map.add_revision_history(history)
1108
1068
        # this call is disabled because revision_history is 
1109
1069
        # not really an object yet, and the transaction is for objects.
1185
1145
        try:
1186
1146
            old_count = len(self.revision_history())
1187
1147
            try:
1188
 
                self.update_revisions(source, stop_revision)
 
1148
                self.update_revisions(source,stop_revision)
1189
1149
            except DivergedBranches:
1190
1150
                if not overwrite:
1191
1151
                    raise
1210
1170
            # turn it into a url
1211
1171
            if parent.startswith('/'):
1212
1172
                parent = urlutils.local_path_to_url(parent.decode('utf8'))
1213
 
            try:
1214
 
                return urlutils.join(self.base[:-1], parent)
1215
 
            except errors.InvalidURLJoin, e:
1216
 
                raise errors.InaccessibleParent(parent, self.base)
 
1173
            return urlutils.join(self.base[:-1], parent)
1217
1174
        return None
1218
1175
 
1219
1176
    def get_push_location(self):
1246
1203
                        "use bzrlib.urlutils.escape")
1247
1204
                    
1248
1205
            url = urlutils.relative_url(self.base, url)
1249
 
            self.control_files.put('parent', StringIO(url + '\n'))
 
1206
            self.control_files.put('parent', url + '\n')
1250
1207
 
1251
1208
    @deprecated_function(zero_nine)
1252
1209
    def tree_config(self):
1326
1283
 
1327
1284
    @needs_write_lock
1328
1285
    def bind(self, other):
1329
 
        """Bind this branch to the branch other.
 
1286
        """Bind the local branch the other branch.
1330
1287
 
1331
 
        This does not push or pull data between the branches, though it does
1332
 
        check for divergence to raise an error when the branches are not
1333
 
        either the same, or one a prefix of the other. That behaviour may not
1334
 
        be useful, so that check may be removed in future.
1335
 
        
1336
1288
        :param other: The branch to bind to
1337
1289
        :type other: Branch
1338
1290
        """
1343
1295
        #       but binding itself may not be.
1344
1296
        #       Since we *have* to check at commit time, we don't
1345
1297
        #       *need* to check here
1346
 
 
1347
 
        # we want to raise diverged if:
1348
 
        # last_rev is not in the other_last_rev history, AND
1349
 
        # other_last_rev is not in our history, and do it without pulling
1350
 
        # history around
1351
 
        last_rev = self.last_revision()
1352
 
        if last_rev is not None:
1353
 
            other.lock_read()
1354
 
            try:
1355
 
                other_last_rev = other.last_revision()
1356
 
                if other_last_rev is not None:
1357
 
                    # neither branch is new, we have to do some work to
1358
 
                    # ascertain diversion.
1359
 
                    remote_graph = other.repository.get_revision_graph(
1360
 
                        other_last_rev)
1361
 
                    local_graph = self.repository.get_revision_graph(last_rev)
1362
 
                    if (last_rev not in remote_graph and
1363
 
                        other_last_rev not in local_graph):
1364
 
                        raise errors.DivergedBranches(self, other)
1365
 
            finally:
1366
 
                other.unlock()
 
1298
        self.pull(other)
 
1299
 
 
1300
        # we are now equal to or a suffix of other.
 
1301
 
 
1302
        # Since we have 'pulled' from the remote location,
 
1303
        # now we should try to pull in the opposite direction
 
1304
        # in case the local tree has more revisions than the
 
1305
        # remote one.
 
1306
        # There may be a different check you could do here
 
1307
        # rather than actually trying to install revisions remotely.
 
1308
        # TODO: capture an exception which indicates the remote branch
 
1309
        #       is not writable. 
 
1310
        #       If it is up-to-date, this probably should not be a failure
 
1311
        
 
1312
        # lock other for write so the revision-history syncing cannot race
 
1313
        other.lock_write()
 
1314
        try:
 
1315
            other.pull(self)
 
1316
            # if this does not error, other now has the same last rev we do
 
1317
            # it can only error if the pull from other was concurrent with
 
1318
            # a commit to other from someone else.
 
1319
 
 
1320
            # until we ditch revision-history, we need to sync them up:
 
1321
            self.set_revision_history(other.revision_history())
 
1322
            # now other and self are up to date with each other and have the
 
1323
            # same revision-history.
 
1324
        finally:
 
1325
            other.unlock()
 
1326
 
1367
1327
        self.set_bound_location(other.base)
1368
1328
 
1369
1329
    @needs_write_lock