~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

[merge] Abstract branch renaming from jelmer

Show diffs side-by-side

added added

removed removed

Lines of Context:
111
111
        """Open a branch which may be of an old format.
112
112
        
113
113
        Only local branches are supported."""
114
 
        return _Branch(get_transport(base), relax_version_check=True)
 
114
        return BzrBranch(get_transport(base), relax_version_check=True)
115
115
        
116
116
    @staticmethod
117
117
    def open(base):
118
118
        """Open an existing branch, rooted at 'base' (url)"""
119
119
        t = get_transport(base)
120
120
        mutter("trying to open %r with transport %r", base, t)
121
 
        return _Branch(t)
 
121
        return BzrBranch(t)
122
122
 
123
123
    @staticmethod
124
124
    def open_containing(url):
133
133
        t = get_transport(url)
134
134
        while True:
135
135
            try:
136
 
                return _Branch(t), t.relpath(url)
 
136
                return BzrBranch(t), t.relpath(url)
137
137
            except NotBranchError:
138
138
                pass
139
139
            new_t = t.clone('..')
146
146
    def initialize(base):
147
147
        """Create a new branch, rooted at 'base' (url)"""
148
148
        t = get_transport(base)
149
 
        return _Branch(t, init=True)
 
149
        return BzrBranch(t, init=True)
150
150
 
151
151
    def setup_caching(self, cache_root):
152
152
        """Subclasses that care about caching should override this, and set
154
154
        """
155
155
        self.cache_root = cache_root
156
156
 
157
 
 
158
 
class _Branch(Branch):
 
157
    def push_stores(self, branch_to):
 
158
        """Copy the content of this branches store to branch_to."""
 
159
        raise NotImplementedError('push_stores is abstract')
 
160
 
 
161
    def get_transaction(self):
 
162
        """Return the current active transaction.
 
163
 
 
164
        If no transaction is active, this returns a passthrough object
 
165
        for which all data is immediately flushed and no caching happens.
 
166
        """
 
167
        raise NotImplementedError('get_transaction is abstract')
 
168
 
 
169
    def lock_write(self):
 
170
        raise NotImplementedError('lock_write is abstract')
 
171
        
 
172
    def lock_read(self):
 
173
        raise NotImplementedError('lock_read is abstract')
 
174
 
 
175
    def unlock(self):
 
176
        raise NotImplementedError('unlock is abstract')
 
177
 
 
178
    def abspath(self, name):
 
179
        """Return absolute filename for something in the branch
 
180
        
 
181
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
 
182
        method and not a tree method.
 
183
        """
 
184
        raise NotImplementedError('abspath is abstract')
 
185
 
 
186
    def controlfilename(self, file_or_path):
 
187
        """Return location relative to branch."""
 
188
        raise NotImplementedError('controlfilename is abstract')
 
189
 
 
190
    def controlfile(self, file_or_path, mode='r'):
 
191
        """Open a control file for this branch.
 
192
 
 
193
        There are two classes of file in the control directory: text
 
194
        and binary.  binary files are untranslated byte streams.  Text
 
195
        control files are stored with Unix newlines and in UTF-8, even
 
196
        if the platform or locale defaults are different.
 
197
 
 
198
        Controlfiles should almost never be opened in write mode but
 
199
        rather should be atomically copied and replaced using atomicfile.
 
200
        """
 
201
        raise NotImplementedError('controlfile is abstract')
 
202
 
 
203
    def put_controlfile(self, path, f, encode=True):
 
204
        """Write an entry as a controlfile.
 
205
 
 
206
        :param path: The path to put the file, relative to the .bzr control
 
207
                     directory
 
208
        :param f: A file-like or string object whose contents should be copied.
 
209
        :param encode:  If true, encode the contents as utf-8
 
210
        """
 
211
        raise NotImplementedError('put_controlfile is abstract')
 
212
 
 
213
    def put_controlfiles(self, files, encode=True):
 
214
        """Write several entries as controlfiles.
 
215
 
 
216
        :param files: A list of [(path, file)] pairs, where the path is the directory
 
217
                      underneath the bzr control directory
 
218
        :param encode:  If true, encode the contents as utf-8
 
219
        """
 
220
        raise NotImplementedError('put_controlfiles is abstract')
 
221
 
 
222
    def get_root_id(self):
 
223
        """Return the id of this branches root"""
 
224
        raise NotImplementedError('get_root_id is abstract')
 
225
 
 
226
    def set_root_id(self, file_id):
 
227
        raise NotImplementedError('set_root_id is abstract')
 
228
 
 
229
    def add(self, files, ids=None):
 
230
        """Make files versioned.
 
231
 
 
232
        Note that the command line normally calls smart_add instead,
 
233
        which can automatically recurse.
 
234
 
 
235
        This puts the files in the Added state, so that they will be
 
236
        recorded by the next commit.
 
237
 
 
238
        files
 
239
            List of paths to add, relative to the base of the tree.
 
240
 
 
241
        ids
 
242
            If set, use these instead of automatically generated ids.
 
243
            Must be the same length as the list of files, but may
 
244
            contain None for ids that are to be autogenerated.
 
245
 
 
246
        TODO: Perhaps have an option to add the ids even if the files do
 
247
              not (yet) exist.
 
248
 
 
249
        TODO: Perhaps yield the ids and paths as they're added.
 
250
        """
 
251
        raise NotImplementedError('add is abstract')
 
252
 
 
253
    def print_file(self, file, revno):
 
254
        """Print `file` to stdout."""
 
255
        raise NotImplementedError('print_file is abstract')
 
256
 
 
257
    def unknowns(self):
 
258
        """Return all unknown files.
 
259
 
 
260
        These are files in the working directory that are not versioned or
 
261
        control files or ignored.
 
262
        
 
263
        >>> from bzrlib.workingtree import WorkingTree
 
264
        >>> b = ScratchBranch(files=['foo', 'foo~'])
 
265
        >>> map(str, b.unknowns())
 
266
        ['foo']
 
267
        >>> b.add('foo')
 
268
        >>> list(b.unknowns())
 
269
        []
 
270
        >>> WorkingTree(b.base, b).remove('foo')
 
271
        >>> list(b.unknowns())
 
272
        [u'foo']
 
273
        """
 
274
        raise NotImplementedError('unknowns is abstract')
 
275
 
 
276
    def append_revision(self, *revision_ids):
 
277
        raise NotImplementedError('append_revision is abstract')
 
278
 
 
279
    def set_revision_history(self, rev_history):
 
280
        raise NotImplementedError('set_revision_history is abstract')
 
281
 
 
282
    def has_revision(self, revision_id):
 
283
        """True if this branch has a copy of the revision.
 
284
 
 
285
        This does not necessarily imply the revision is merge
 
286
        or on the mainline."""
 
287
        raise NotImplementedError('has_revision is abstract')
 
288
 
 
289
    def get_revision_xml_file(self, revision_id):
 
290
        """Return XML file object for revision object."""
 
291
        raise NotImplementedError('get_revision_xml_file is abstract')
 
292
 
 
293
    def get_revision_xml(self, revision_id):
 
294
        raise NotImplementedError('get_revision_xml is abstract')
 
295
 
 
296
    def get_revision(self, revision_id):
 
297
        """Return the Revision object for a named revision"""
 
298
        raise NotImplementedError('get_revision is abstract')
 
299
 
 
300
    def get_revision_delta(self, revno):
 
301
        """Return the delta for one revision.
 
302
 
 
303
        The delta is relative to its mainline predecessor, or the
 
304
        empty tree for revision 1.
 
305
        """
 
306
        assert isinstance(revno, int)
 
307
        rh = self.revision_history()
 
308
        if not (1 <= revno <= len(rh)):
 
309
            raise InvalidRevisionNumber(revno)
 
310
 
 
311
        # revno is 1-based; list is 0-based
 
312
 
 
313
        new_tree = self.revision_tree(rh[revno-1])
 
314
        if revno == 1:
 
315
            old_tree = EmptyTree()
 
316
        else:
 
317
            old_tree = self.revision_tree(rh[revno-2])
 
318
 
 
319
        return compare_trees(old_tree, new_tree)
 
320
 
 
321
    def get_revision_sha1(self, revision_id):
 
322
        """Hash the stored value of a revision, and return it."""
 
323
        raise NotImplementedError('get_revision_sha1 is abstract')
 
324
 
 
325
    def get_ancestry(self, revision_id):
 
326
        """Return a list of revision-ids integrated by a revision.
 
327
        
 
328
        This currently returns a list, but the ordering is not guaranteed:
 
329
        treat it as a set.
 
330
        """
 
331
        raise NotImplementedError('get_ancestry is abstract')
 
332
 
 
333
    def get_inventory(self, revision_id):
 
334
        """Get Inventory object by hash."""
 
335
        raise NotImplementedError('get_inventory is abstract')
 
336
 
 
337
    def get_inventory_xml(self, revision_id):
 
338
        """Get inventory XML as a file object."""
 
339
        raise NotImplementedError('get_inventory_xml is abstract')
 
340
 
 
341
    def get_inventory_sha1(self, revision_id):
 
342
        """Return the sha1 hash of the inventory entry."""
 
343
        raise NotImplementedError('get_inventory_sha1 is abstract')
 
344
 
 
345
    def get_revision_inventory(self, revision_id):
 
346
        """Return inventory of a past revision."""
 
347
        raise NotImplementedError('get_revision_inventory is abstract')
 
348
 
 
349
    def revision_history(self):
 
350
        """Return sequence of revision hashes on to this branch."""
 
351
        raise NotImplementedError('revision_history is abstract')
 
352
 
 
353
    def revno(self):
 
354
        """Return current revision number for this branch.
 
355
 
 
356
        That is equivalent to the number of revisions committed to
 
357
        this branch.
 
358
        """
 
359
        return len(self.revision_history())
 
360
 
 
361
    def last_revision(self):
 
362
        """Return last patch hash, or None if no history."""
 
363
        ph = self.revision_history()
 
364
        if ph:
 
365
            return ph[-1]
 
366
        else:
 
367
            return None
 
368
 
 
369
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
 
370
        """Return a list of new revisions that would perfectly fit.
 
371
        
 
372
        If self and other have not diverged, return a list of the revisions
 
373
        present in other, but missing from self.
 
374
 
 
375
        >>> from bzrlib.commit import commit
 
376
        >>> bzrlib.trace.silent = True
 
377
        >>> br1 = ScratchBranch()
 
378
        >>> br2 = ScratchBranch()
 
379
        >>> br1.missing_revisions(br2)
 
380
        []
 
381
        >>> commit(br2, "lala!", rev_id="REVISION-ID-1")
 
382
        >>> br1.missing_revisions(br2)
 
383
        [u'REVISION-ID-1']
 
384
        >>> br2.missing_revisions(br1)
 
385
        []
 
386
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1")
 
387
        >>> br1.missing_revisions(br2)
 
388
        []
 
389
        >>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
 
390
        >>> br1.missing_revisions(br2)
 
391
        [u'REVISION-ID-2A']
 
392
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
 
393
        >>> br1.missing_revisions(br2)
 
394
        Traceback (most recent call last):
 
395
        DivergedBranches: These branches have diverged.
 
396
        """
 
397
        self_history = self.revision_history()
 
398
        self_len = len(self_history)
 
399
        other_history = other.revision_history()
 
400
        other_len = len(other_history)
 
401
        common_index = min(self_len, other_len) -1
 
402
        if common_index >= 0 and \
 
403
            self_history[common_index] != other_history[common_index]:
 
404
            raise DivergedBranches(self, other)
 
405
 
 
406
        if stop_revision is None:
 
407
            stop_revision = other_len
 
408
        else:
 
409
            assert isinstance(stop_revision, int)
 
410
            if stop_revision > other_len:
 
411
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
 
412
        return other_history[self_len:stop_revision]
 
413
    
 
414
    def update_revisions(self, other, stop_revision=None):
 
415
        """Pull in new perfect-fit revisions."""
 
416
        raise NotImplementedError('update_revisions is abstract')
 
417
 
 
418
    def pullable_revisions(self, other, stop_revision):
 
419
        raise NotImplementedError('pullable_revisions is abstract')
 
420
        
 
421
    def commit(self, *args, **kw):
 
422
        raise NotImplementedError('commit is abstract')
 
423
    
 
424
    def revision_id_to_revno(self, revision_id):
 
425
        """Given a revision id, return its revno"""
 
426
        if revision_id is None:
 
427
            return 0
 
428
        history = self.revision_history()
 
429
        try:
 
430
            return history.index(revision_id) + 1
 
431
        except ValueError:
 
432
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
 
433
 
 
434
    def get_rev_id(self, revno, history=None):
 
435
        """Find the revision id of the specified revno."""
 
436
        if revno == 0:
 
437
            return None
 
438
        if history is None:
 
439
            history = self.revision_history()
 
440
        elif revno <= 0 or revno > len(history):
 
441
            raise bzrlib.errors.NoSuchRevision(self, revno)
 
442
        return history[revno - 1]
 
443
 
 
444
    def revision_tree(self, revision_id):
 
445
        """Return Tree for a revision on this branch.
 
446
 
 
447
        `revision_id` may be None for the null revision, in which case
 
448
        an `EmptyTree` is returned."""
 
449
        raise NotImplementedError('revision_tree is abstract')
 
450
 
 
451
    def working_tree(self):
 
452
        """Return a `Tree` for the working copy."""
 
453
        raise NotImplementedError('working_tree is abstract')
 
454
 
 
455
    def pull(self, source, overwrite=False):
 
456
        raise NotImplementedError('pull is abstract')
 
457
 
 
458
    def basis_tree(self):
 
459
        """Return `Tree` object for last revision.
 
460
 
 
461
        If there are no revisions yet, return an `EmptyTree`.
 
462
        """
 
463
        return self.revision_tree(self.last_revision())
 
464
 
 
465
    def rename_one(self, from_rel, to_rel):
 
466
        """Rename one file.
 
467
 
 
468
        This can change the directory or the filename or both.
 
469
        """
 
470
        raise NotImplementedError('rename_one is abstract')
 
471
 
 
472
    def move(self, from_paths, to_name):
 
473
        """Rename files.
 
474
 
 
475
        to_name must exist as a versioned directory.
 
476
 
 
477
        If to_name exists and is a directory, the files are moved into
 
478
        it, keeping their old names.  If it is a directory, 
 
479
 
 
480
        Note that to_name is only the last component of the new name;
 
481
        this doesn't change the directory.
 
482
 
 
483
        This returns a list of (from_path, to_path) pairs for each
 
484
        entry that is moved.
 
485
        """
 
486
        raise NotImplementedError('move is abstract')
 
487
 
 
488
    def revert(self, filenames, old_tree=None, backups=True):
 
489
        """Restore selected files to the versions from a previous tree.
 
490
 
 
491
        backups
 
492
            If true (default) backups are made of files before
 
493
            they're renamed.
 
494
        """
 
495
        raise NotImplementedError('revert is abstract')
 
496
 
 
497
    def pending_merges(self):
 
498
        """Return a list of pending merges.
 
499
 
 
500
        These are revisions that have been merged into the working
 
501
        directory but not yet committed.
 
502
        """
 
503
        raise NotImplementedError('pending_merges is abstract')
 
504
 
 
505
    def add_pending_merge(self, *revision_ids):
 
506
        # TODO: Perhaps should check at this point that the
 
507
        # history of the revision is actually present?
 
508
        raise NotImplementedError('add_pending_merge is abstract')
 
509
 
 
510
    def set_pending_merges(self, rev_list):
 
511
        raise NotImplementedError('set_pending_merges is abstract')
 
512
 
 
513
    def get_parent(self):
 
514
        """Return the parent location of the branch.
 
515
 
 
516
        This is the default location for push/pull/missing.  The usual
 
517
        pattern is that the user can override it by specifying a
 
518
        location.
 
519
        """
 
520
        raise NotImplementedError('get_parent is abstract')
 
521
 
 
522
    def get_push_location(self):
 
523
        """Return the None or the location to push this branch to."""
 
524
        raise NotImplementedError('get_push_location is abstract')
 
525
 
 
526
    def set_push_location(self, location):
 
527
        """Set a new push location for this branch."""
 
528
        raise NotImplementedError('set_push_location is abstract')
 
529
 
 
530
    def set_parent(self, url):
 
531
        raise NotImplementedError('set_parent is abstract')
 
532
 
 
533
    def check_revno(self, revno):
 
534
        """\
 
535
        Check whether a revno corresponds to any revision.
 
536
        Zero (the NULL revision) is considered valid.
 
537
        """
 
538
        if revno != 0:
 
539
            self.check_real_revno(revno)
 
540
            
 
541
    def check_real_revno(self, revno):
 
542
        """\
 
543
        Check whether a revno corresponds to a real revision.
 
544
        Zero (the NULL revision) is considered invalid
 
545
        """
 
546
        if revno < 1 or revno > self.revno():
 
547
            raise InvalidRevisionNumber(revno)
 
548
        
 
549
    def sign_revision(self, revision_id, gpg_strategy):
 
550
        raise NotImplementedError('sign_revision is abstract')
 
551
 
 
552
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
553
        raise NotImplementedError('store_revision_signature is abstract')
 
554
 
 
555
class BzrBranch(Branch):
159
556
    """A branch stored in the actual filesystem.
160
557
 
161
558
    Note that it's "local" in the context of the filesystem; it doesn't
187
584
    REVISION_NAMESPACES = {}
188
585
 
189
586
    def push_stores(self, branch_to):
190
 
        """Copy the content of this branches store to branch_to."""
 
587
        """See Branch.push_stores."""
191
588
        if (self._branch_format != branch_to._branch_format
192
589
            or self._branch_format != 4):
193
590
            from bzrlib.fetch import greedy_fetch
313
710
        transaction.finish()
314
711
 
315
712
    def get_transaction(self):
316
 
        """Return the current active transaction.
317
 
 
318
 
        If no transaction is active, this returns a passthrough object
319
 
        for which all data is immediately flushed and no caching happens.
320
 
        """
 
713
        """See Branch.get_transaction."""
321
714
        if self._transaction is None:
322
715
            return transactions.PassThroughTransaction()
323
716
        else:
375
768
            self._lock_mode = self._lock_count = None
376
769
 
377
770
    def abspath(self, name):
378
 
        """Return absolute filename for something in the branch
379
 
        
380
 
        XXX: Robert Collins 20051017 what is this used for? why is it a branch
381
 
        method and not a tree method.
382
 
        """
 
771
        """See Branch.abspath."""
383
772
        return self._transport.abspath(name)
384
773
 
385
774
    def _rel_controlfilename(self, file_or_path):
390
779
        return bzrlib.transport.urlescape(bzrlib.BZRDIR + '/' + file_or_path)
391
780
 
392
781
    def controlfilename(self, file_or_path):
393
 
        """Return location relative to branch."""
 
782
        """See Branch.controlfilename."""
394
783
        return self._transport.abspath(self._rel_controlfilename(file_or_path))
395
784
 
396
785
    def controlfile(self, file_or_path, mode='r'):
397
 
        """Open a control file for this branch.
398
 
 
399
 
        There are two classes of file in the control directory: text
400
 
        and binary.  binary files are untranslated byte streams.  Text
401
 
        control files are stored with Unix newlines and in UTF-8, even
402
 
        if the platform or locale defaults are different.
403
 
 
404
 
        Controlfiles should almost never be opened in write mode but
405
 
        rather should be atomically copied and replaced using atomicfile.
406
 
        """
 
786
        """See Branch.controlfile."""
407
787
        import codecs
408
788
 
409
789
        relpath = self._rel_controlfilename(file_or_path)
425
805
            raise BzrError("invalid controlfile mode %r" % mode)
426
806
 
427
807
    def put_controlfile(self, path, f, encode=True):
428
 
        """Write an entry as a controlfile.
429
 
 
430
 
        :param path: The path to put the file, relative to the .bzr control
431
 
                     directory
432
 
        :param f: A file-like or string object whose contents should be copied.
433
 
        :param encode:  If true, encode the contents as utf-8
434
 
        """
 
808
        """See Branch.put_controlfile."""
435
809
        self.put_controlfiles([(path, f)], encode=encode)
436
810
 
437
811
    def put_controlfiles(self, files, encode=True):
438
 
        """Write several entries as controlfiles.
439
 
 
440
 
        :param files: A list of [(path, file)] pairs, where the path is the directory
441
 
                      underneath the bzr control directory
442
 
        :param encode:  If true, encode the contents as utf-8
443
 
        """
 
812
        """See Branch.put_controlfiles."""
444
813
        import codecs
445
814
        ctrl_files = []
446
815
        for path, f in files:
517
886
                            ' and "bzr init" again'])
518
887
 
519
888
    def get_root_id(self):
520
 
        """Return the id of this branches root"""
 
889
        """See Branch.get_root_id."""
521
890
        inv = self.get_inventory(self.last_revision())
522
891
        return inv.root.file_id
523
892
 
524
893
    @needs_write_lock
525
894
    def set_root_id(self, file_id):
 
895
        """See Branch.set_root_id."""
526
896
        inv = self.working_tree().read_working_inventory()
527
897
        orig_root_id = inv.root.file_id
528
898
        del inv._byid[inv.root.file_id]
552
922
            
553
923
    @needs_write_lock
554
924
    def add(self, files, ids=None):
555
 
        """Make files versioned.
556
 
 
557
 
        Note that the command line normally calls smart_add instead,
558
 
        which can automatically recurse.
559
 
 
560
 
        This puts the files in the Added state, so that they will be
561
 
        recorded by the next commit.
562
 
 
563
 
        files
564
 
            List of paths to add, relative to the base of the tree.
565
 
 
566
 
        ids
567
 
            If set, use these instead of automatically generated ids.
568
 
            Must be the same length as the list of files, but may
569
 
            contain None for ids that are to be autogenerated.
570
 
 
571
 
        TODO: Perhaps have an option to add the ids even if the files do
572
 
              not (yet) exist.
573
 
 
574
 
        TODO: Perhaps yield the ids and paths as they're added.
575
 
        """
 
925
        """See Branch.add."""
576
926
        # TODO: Re-adding a file that is removed in the working copy
577
927
        # should probably put it back with the previous ID.
578
928
        if isinstance(files, basestring):
618
968
 
619
969
    @needs_read_lock
620
970
    def print_file(self, file, revno):
621
 
        """Print `file` to stdout."""
 
971
        """See Branch.print_file."""
622
972
        tree = self.revision_tree(self.get_rev_id(revno))
623
973
        # use inventory as it was in that revision
624
974
        file_id = tree.inventory.path2id(file)
627
977
        tree.print_file(file_id)
628
978
 
629
979
    def unknowns(self):
630
 
        """Return all unknown files.
631
 
 
632
 
        These are files in the working directory that are not versioned or
633
 
        control files or ignored.
634
 
        
635
 
        >>> from bzrlib.workingtree import WorkingTree
636
 
        >>> b = ScratchBranch(files=['foo', 'foo~'])
637
 
        >>> map(str, b.unknowns())
638
 
        ['foo']
639
 
        >>> b.add('foo')
640
 
        >>> list(b.unknowns())
641
 
        []
642
 
        >>> WorkingTree(b.base, b).remove('foo')
643
 
        >>> list(b.unknowns())
644
 
        [u'foo']
645
 
        """
 
980
        """See Branch.unknowns."""
646
981
        return self.working_tree().unknowns()
647
982
 
648
983
    @needs_write_lock
649
984
    def append_revision(self, *revision_ids):
 
985
        """See Branch.append_revision."""
650
986
        for revision_id in revision_ids:
651
987
            mutter("add {%s} to revision-history" % revision_id)
652
988
        rev_history = self.revision_history()
655
991
 
656
992
    @needs_write_lock
657
993
    def set_revision_history(self, rev_history):
 
994
        """See Branch.set_revision_history."""
658
995
        self.put_controlfile('revision-history', '\n'.join(rev_history))
659
996
 
660
997
    def has_revision(self, revision_id):
661
 
        """True if this branch has a copy of the revision.
662
 
 
663
 
        This does not necessarily imply the revision is merge
664
 
        or on the mainline."""
 
998
        """See Branch.has_revision."""
665
999
        return (revision_id is None
666
1000
                or self.revision_store.has_id(revision_id))
667
1001
 
668
1002
    @needs_read_lock
669
1003
    def get_revision_xml_file(self, revision_id):
670
 
        """Return XML file object for revision object."""
 
1004
        """See Branch.get_revision_xml_file."""
671
1005
        if not revision_id or not isinstance(revision_id, basestring):
672
1006
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
673
1007
        try:
679
1013
    get_revision_xml = get_revision_xml_file
680
1014
 
681
1015
    def get_revision_xml(self, revision_id):
 
1016
        """See Branch.get_revision_xml."""
682
1017
        return self.get_revision_xml_file(revision_id).read()
683
1018
 
684
1019
 
685
1020
    def get_revision(self, revision_id):
686
 
        """Return the Revision object for a named revision"""
 
1021
        """See Branch.get_revision."""
687
1022
        xml_file = self.get_revision_xml_file(revision_id)
688
1023
 
689
1024
        try:
696
1031
        assert r.revision_id == revision_id
697
1032
        return r
698
1033
 
699
 
    def get_revision_delta(self, revno):
700
 
        """Return the delta for one revision.
701
 
 
702
 
        The delta is relative to its mainline predecessor, or the
703
 
        empty tree for revision 1.
704
 
        """
705
 
        assert isinstance(revno, int)
706
 
        rh = self.revision_history()
707
 
        if not (1 <= revno <= len(rh)):
708
 
            raise InvalidRevisionNumber(revno)
709
 
 
710
 
        # revno is 1-based; list is 0-based
711
 
 
712
 
        new_tree = self.revision_tree(rh[revno-1])
713
 
        if revno == 1:
714
 
            old_tree = EmptyTree()
715
 
        else:
716
 
            old_tree = self.revision_tree(rh[revno-2])
717
 
 
718
 
        return compare_trees(old_tree, new_tree)
719
 
 
720
1034
    def get_revision_sha1(self, revision_id):
721
 
        """Hash the stored value of a revision, and return it."""
 
1035
        """See Branch.get_revision_sha1."""
722
1036
        # In the future, revision entries will be signed. At that
723
1037
        # point, it is probably best *not* to include the signature
724
1038
        # in the revision hash. Because that lets you re-sign
728
1042
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
729
1043
 
730
1044
    def get_ancestry(self, revision_id):
731
 
        """Return a list of revision-ids integrated by a revision.
732
 
        
733
 
        This currently returns a list, but the ordering is not guaranteed:
734
 
        treat it as a set.
735
 
        """
 
1045
        """See Branch.get_ancestry."""
736
1046
        if revision_id is None:
737
1047
            return [None]
738
 
        w = self.get_inventory_weave()
 
1048
        w = self._get_inventory_weave()
739
1049
        return [None] + map(w.idx_to_name,
740
1050
                            w.inclusions([w.lookup(revision_id)]))
741
1051
 
742
 
    def get_inventory_weave(self):
 
1052
    def _get_inventory_weave(self):
743
1053
        return self.control_weaves.get_weave('inventory',
744
1054
                                             self.get_transaction())
745
1055
 
746
1056
    def get_inventory(self, revision_id):
747
 
        """Get Inventory object by hash."""
 
1057
        """See Branch.get_inventory."""
748
1058
        xml = self.get_inventory_xml(revision_id)
749
1059
        return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
750
1060
 
751
1061
    def get_inventory_xml(self, revision_id):
752
 
        """Get inventory XML as a file object."""
 
1062
        """See Branch.get_inventory_xml."""
753
1063
        try:
754
1064
            assert isinstance(revision_id, basestring), type(revision_id)
755
 
            iw = self.get_inventory_weave()
 
1065
            iw = self._get_inventory_weave()
756
1066
            return iw.get_text(iw.lookup(revision_id))
757
1067
        except IndexError:
758
1068
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
759
1069
 
760
1070
    def get_inventory_sha1(self, revision_id):
761
 
        """Return the sha1 hash of the inventory entry
762
 
        """
 
1071
        """See Branch.get_inventory_sha1."""
763
1072
        return self.get_revision(revision_id).inventory_sha1
764
1073
 
765
1074
    def get_revision_inventory(self, revision_id):
766
 
        """Return inventory of a past revision."""
 
1075
        """See Branch.get_revision_inventory."""
767
1076
        # TODO: Unify this with get_inventory()
768
1077
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
769
1078
        # must be the same as its revision, so this is trivial.
779
1088
 
780
1089
    @needs_read_lock
781
1090
    def revision_history(self):
782
 
        """Return sequence of revision hashes on to this branch."""
 
1091
        """See Branch.revision_history."""
783
1092
        transaction = self.get_transaction()
784
1093
        history = transaction.map.find_revision_history()
785
1094
        if history is not None:
793
1102
        # transaction.register_clean(history, precious=True)
794
1103
        return list(history)
795
1104
 
796
 
    def revno(self):
797
 
        """Return current revision number for this branch.
798
 
 
799
 
        That is equivalent to the number of revisions committed to
800
 
        this branch.
801
 
        """
802
 
        return len(self.revision_history())
803
 
 
804
 
    def last_revision(self):
805
 
        """Return last patch hash, or None if no history.
806
 
        """
807
 
        ph = self.revision_history()
808
 
        if ph:
809
 
            return ph[-1]
810
 
        else:
811
 
            return None
812
 
 
813
 
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
814
 
        """Return a list of new revisions that would perfectly fit.
815
 
        
816
 
        If self and other have not diverged, return a list of the revisions
817
 
        present in other, but missing from self.
818
 
 
819
 
        >>> from bzrlib.commit import commit
820
 
        >>> bzrlib.trace.silent = True
821
 
        >>> br1 = ScratchBranch()
822
 
        >>> br2 = ScratchBranch()
823
 
        >>> br1.missing_revisions(br2)
824
 
        []
825
 
        >>> commit(br2, "lala!", rev_id="REVISION-ID-1")
826
 
        >>> br1.missing_revisions(br2)
827
 
        [u'REVISION-ID-1']
828
 
        >>> br2.missing_revisions(br1)
829
 
        []
830
 
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1")
831
 
        >>> br1.missing_revisions(br2)
832
 
        []
833
 
        >>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
834
 
        >>> br1.missing_revisions(br2)
835
 
        [u'REVISION-ID-2A']
836
 
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
837
 
        >>> br1.missing_revisions(br2)
838
 
        Traceback (most recent call last):
839
 
        DivergedBranches: These branches have diverged.
840
 
        """
841
 
        self_history = self.revision_history()
842
 
        self_len = len(self_history)
843
 
        other_history = other.revision_history()
844
 
        other_len = len(other_history)
845
 
        common_index = min(self_len, other_len) -1
846
 
        if common_index >= 0 and \
847
 
            self_history[common_index] != other_history[common_index]:
848
 
            raise DivergedBranches(self, other)
849
 
 
850
 
        if stop_revision is None:
851
 
            stop_revision = other_len
852
 
        else:
853
 
            assert isinstance(stop_revision, int)
854
 
            if stop_revision > other_len:
855
 
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
856
 
        return other_history[self_len:stop_revision]
857
 
 
858
1105
    def update_revisions(self, other, stop_revision=None):
859
 
        """Pull in new perfect-fit revisions."""
 
1106
        """See Branch.update_revisions."""
860
1107
        from bzrlib.fetch import greedy_fetch
861
1108
        if stop_revision is None:
862
1109
            stop_revision = other.last_revision()
871
1118
            self.append_revision(*pullable_revs)
872
1119
 
873
1120
    def pullable_revisions(self, other, stop_revision):
 
1121
        """See Branch.pullable_revisions."""
874
1122
        other_revno = other.revision_id_to_revno(stop_revision)
875
1123
        try:
876
1124
            return self.missing_revisions(other, other_revno)
887
1135
                    raise e
888
1136
        
889
1137
    def commit(self, *args, **kw):
 
1138
        """See Branch.commit."""
890
1139
        from bzrlib.commit import Commit
891
1140
        Commit().commit(self, *args, **kw)
892
1141
    
893
 
    def revision_id_to_revno(self, revision_id):
894
 
        """Given a revision id, return its revno"""
895
 
        if revision_id is None:
896
 
            return 0
897
 
        history = self.revision_history()
898
 
        try:
899
 
            return history.index(revision_id) + 1
900
 
        except ValueError:
901
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
902
 
 
903
 
    def get_rev_id(self, revno, history=None):
904
 
        """Find the revision id of the specified revno."""
905
 
        if revno == 0:
906
 
            return None
907
 
        if history is None:
908
 
            history = self.revision_history()
909
 
        elif revno <= 0 or revno > len(history):
910
 
            raise bzrlib.errors.NoSuchRevision(self, revno)
911
 
        return history[revno - 1]
912
 
 
913
1142
    def revision_tree(self, revision_id):
914
 
        """Return Tree for a revision on this branch.
915
 
 
916
 
        `revision_id` may be None for the null revision, in which case
917
 
        an `EmptyTree` is returned."""
 
1143
        """See Branch.revision_tree."""
918
1144
        # TODO: refactor this to use an existing revision object
919
1145
        # so we don't need to read it in twice.
920
1146
        if revision_id == None or revision_id == NULL_REVISION:
924
1150
            return RevisionTree(self.weave_store, inv, revision_id)
925
1151
 
926
1152
    def working_tree(self):
927
 
        """Return a `Tree` for the working copy."""
 
1153
        """See Branch.working_tree."""
928
1154
        from bzrlib.workingtree import WorkingTree
929
1155
        # TODO: In the future, perhaps WorkingTree should utilize Transport
930
1156
        # RobertCollins 20051003 - I don't think it should - working trees are
937
1163
 
938
1164
    @needs_write_lock
939
1165
    def pull(self, source, overwrite=False):
 
1166
        """See Branch.pull."""
940
1167
        source.lock_read()
941
1168
        try:
942
1169
            try:
948
1175
        finally:
949
1176
            source.unlock()
950
1177
 
951
 
    def basis_tree(self):
952
 
        """Return `Tree` object for last revision.
953
 
 
954
 
        If there are no revisions yet, return an `EmptyTree`.
955
 
        """
956
 
        return self.revision_tree(self.last_revision())
957
 
 
958
1178
    @needs_write_lock
959
1179
    def rename_one(self, from_rel, to_rel):
960
 
        """Rename one file.
961
 
 
962
 
        This can change the directory or the filename or both.
963
 
        """
 
1180
        """See Branch.rename_one."""
964
1181
        tree = self.working_tree()
965
1182
        inv = tree.inventory
966
1183
        if not tree.has_filename(from_rel):
1002
1219
 
1003
1220
    @needs_write_lock
1004
1221
    def move(self, from_paths, to_name):
1005
 
        """Rename files.
1006
 
 
1007
 
        to_name must exist as a versioned directory.
1008
 
 
1009
 
        If to_name exists and is a directory, the files are moved into
1010
 
        it, keeping their old names.  If it is a directory, 
1011
 
 
1012
 
        Note that to_name is only the last component of the new name;
1013
 
        this doesn't change the directory.
1014
 
 
1015
 
        This returns a list of (from_path, to_path) pairs for each
1016
 
        entry that is moved.
1017
 
        """
 
1222
        """See Branch.move."""
1018
1223
        result = []
1019
1224
        ## TODO: Option to move IDs only
1020
1225
        assert not isinstance(from_paths, basestring)
1067
1272
 
1068
1273
 
1069
1274
    def revert(self, filenames, old_tree=None, backups=True):
1070
 
        """Restore selected files to the versions from a previous tree.
1071
 
 
1072
 
        backups
1073
 
            If true (default) backups are made of files before
1074
 
            they're renamed.
1075
 
        """
 
1275
        """See Branch.revert."""
1076
1276
        from bzrlib.atomicfile import AtomicFile
1077
1277
        from bzrlib.osutils import backup_file
1078
1278
        
1110
1310
 
1111
1311
 
1112
1312
    def pending_merges(self):
1113
 
        """Return a list of pending merges.
1114
 
 
1115
 
        These are revisions that have been merged into the working
1116
 
        directory but not yet committed.
1117
 
        """
 
1313
        """See Branch.pending_merges."""
1118
1314
        cfn = self._rel_controlfilename('pending-merges')
1119
1315
        if not self._transport.has(cfn):
1120
1316
            return []
1125
1321
 
1126
1322
 
1127
1323
    def add_pending_merge(self, *revision_ids):
 
1324
        """See Branch.add_pending_merge."""
1128
1325
        # TODO: Perhaps should check at this point that the
1129
1326
        # history of the revision is actually present?
1130
1327
        p = self.pending_merges()
1139
1336
 
1140
1337
    @needs_write_lock
1141
1338
    def set_pending_merges(self, rev_list):
 
1339
        """See Branch.set_pending_merges."""
1142
1340
        self.put_controlfile('pending-merges', '\n'.join(rev_list))
1143
1341
 
1144
1342
    def get_parent(self):
1145
 
        """Return the parent location of the branch.
1146
 
 
1147
 
        This is the default location for push/pull/missing.  The usual
1148
 
        pattern is that the user can override it by specifying a
1149
 
        location.
1150
 
        """
 
1343
        """See Branch.get_parent."""
1151
1344
        import errno
1152
1345
        _locs = ['parent', 'pull', 'x-pull']
1153
1346
        for l in _locs:
1159
1352
        return None
1160
1353
 
1161
1354
    def get_push_location(self):
1162
 
        """Return the None or the location to push this branch to."""
 
1355
        """See Branch.get_push_location."""
1163
1356
        config = bzrlib.config.BranchConfig(self)
1164
1357
        push_loc = config.get_user_option('push_location')
1165
1358
        return push_loc
1166
1359
 
1167
1360
    def set_push_location(self, location):
1168
 
        """Set a new push location for this branch."""
 
1361
        """See Branch.set_push_location."""
1169
1362
        config = bzrlib.config.LocationConfig(self.base)
1170
1363
        config.set_user_option('push_location', location)
1171
1364
 
1172
1365
    @needs_write_lock
1173
1366
    def set_parent(self, url):
 
1367
        """See Branch.set_parent."""
1174
1368
        # TODO: Maybe delete old location files?
1175
1369
        from bzrlib.atomicfile import AtomicFile
1176
1370
        f = AtomicFile(self.controlfilename('parent'))
1180
1374
        finally:
1181
1375
            f.close()
1182
1376
 
1183
 
    def check_revno(self, revno):
1184
 
        """\
1185
 
        Check whether a revno corresponds to any revision.
1186
 
        Zero (the NULL revision) is considered valid.
1187
 
        """
1188
 
        if revno != 0:
1189
 
            self.check_real_revno(revno)
1190
 
            
1191
 
    def check_real_revno(self, revno):
1192
 
        """\
1193
 
        Check whether a revno corresponds to a real revision.
1194
 
        Zero (the NULL revision) is considered invalid
1195
 
        """
1196
 
        if revno < 1 or revno > self.revno():
1197
 
            raise InvalidRevisionNumber(revno)
1198
 
        
1199
1377
    def sign_revision(self, revision_id, gpg_strategy):
 
1378
        """See Branch.sign_revision."""
1200
1379
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
1201
1380
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1202
1381
 
1203
1382
    @needs_write_lock
1204
1383
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
 
1384
        """See Branch.store_revision_signature."""
1205
1385
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
1206
1386
                                revision_id, "sig")
1207
1387
 
1208
1388
 
1209
 
class ScratchBranch(_Branch):
 
1389
class ScratchBranch(BzrBranch):
1210
1390
    """Special test class: a branch that cleans up after itself.
1211
1391
 
1212
1392
    >>> b = ScratchBranch()