~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-07-11 06:13:18 UTC
  • mfrom: (unknown (missing))
  • Revision ID: mbp@sourcefrog.net-20050711061318-80557a9f045b1f38
- merge john's revision-naming code

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
 
import sys
19
 
import os
 
18
import sys, os
20
19
 
21
20
import bzrlib
22
21
from bzrlib.trace import mutter, note
23
 
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
24
 
     splitpath, \
 
22
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, splitpath, \
25
23
     sha_file, appendpath, file_kind
26
 
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
27
 
import bzrlib.errors
28
 
from bzrlib.textui import show_status
29
 
from bzrlib.revision import Revision
30
 
from bzrlib.xml import unpack_xml
31
 
from bzrlib.delta import compare_trees
32
 
from bzrlib.tree import EmptyTree, RevisionTree
33
 
        
 
24
from bzrlib.errors import BzrError
 
25
 
34
26
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
35
27
## TODO: Maybe include checks for common corruption of newlines, etc?
36
28
 
37
29
 
38
 
# TODO: Some operations like log might retrieve the same revisions
39
 
# repeatedly to calculate deltas.  We could perhaps have a weakref
40
 
# cache in memory to make this faster.
41
 
 
42
 
# TODO: please move the revision-string syntax stuff out of the branch
43
 
# object; it's clutter
44
 
 
45
30
 
46
31
def find_branch(f, **args):
47
32
    if f and (f.startswith('http://') or f.startswith('https://')):
133
118
        Exception.__init__(self, "These branches have diverged.")
134
119
 
135
120
 
 
121
class NoSuchRevision(BzrError):
 
122
    def __init__(self, branch, revision):
 
123
        self.branch = branch
 
124
        self.revision = revision
 
125
        msg = "Branch %s has no revision %d" % (branch, revision)
 
126
        BzrError.__init__(self, msg)
 
127
 
 
128
 
136
129
######################################################################
137
130
# branch objects
138
131
 
319
312
            self.controlfile(f, 'w').write('')
320
313
        mutter('created control directory in ' + self.base)
321
314
 
322
 
        # if we want per-tree root ids then this is the place to set
323
 
        # them; they're not needed for now and so ommitted for
324
 
        # simplicity.
325
315
        pack_xml(Inventory(), self.controlfile('inventory','w'))
326
316
 
327
317
 
343
333
                           ['use a different bzr version',
344
334
                            'or remove the .bzr directory and "bzr init" again'])
345
335
 
346
 
    def get_root_id(self):
347
 
        """Return the id of this branches root"""
348
 
        inv = self.read_working_inventory()
349
 
        return inv.root.file_id
350
336
 
351
 
    def set_root_id(self, file_id):
352
 
        inv = self.read_working_inventory()
353
 
        orig_root_id = inv.root.file_id
354
 
        del inv._byid[inv.root.file_id]
355
 
        inv.root.file_id = file_id
356
 
        inv._byid[inv.root.file_id] = inv.root
357
 
        for fid in inv:
358
 
            entry = inv[fid]
359
 
            if entry.parent_id in (None, orig_root_id):
360
 
                entry.parent_id = inv.root.file_id
361
 
        self._write_inventory(inv)
362
337
 
363
338
    def read_working_inventory(self):
364
339
        """Read the working inventory."""
371
346
            # ElementTree does its own conversion from UTF-8, so open in
372
347
            # binary.
373
348
            inv = unpack_xml(Inventory,
374
 
                             self.controlfile('inventory', 'rb'))
 
349
                                  self.controlfile('inventory', 'rb'))
375
350
            mutter("loaded inventory of %d items in %f"
376
351
                   % (len(inv), time() - before))
377
352
            return inv
432
407
              add all non-ignored children.  Perhaps do that in a
433
408
              higher-level method.
434
409
        """
 
410
        from bzrlib.textui import show_status
435
411
        # TODO: Re-adding a file that is removed in the working copy
436
412
        # should probably put it back with the previous ID.
437
413
        if isinstance(files, basestring):
510
486
        is the opposite of add.  Removing it is consistent with most
511
487
        other tools.  Maybe an option.
512
488
        """
 
489
        from bzrlib.textui import show_status
513
490
        ## TODO: Normalize names
514
491
        ## TODO: Remove nested loops; better scalability
515
492
        if isinstance(files, basestring):
544
521
    # FIXME: this doesn't need to be a branch method
545
522
    def set_inventory(self, new_inventory_list):
546
523
        from bzrlib.inventory import Inventory, InventoryEntry
547
 
        inv = Inventory(self.get_root_id())
 
524
        inv = Inventory()
548
525
        for path, file_id, parent, kind in new_inventory_list:
549
526
            name = os.path.basename(path)
550
527
            if name == "":
572
549
        return self.working_tree().unknowns()
573
550
 
574
551
 
575
 
    def append_revision(self, *revision_ids):
 
552
    def append_revision(self, revision_id):
576
553
        from bzrlib.atomicfile import AtomicFile
577
554
 
578
 
        for revision_id in revision_ids:
579
 
            mutter("add {%s} to revision-history" % revision_id)
580
 
 
581
 
        rev_history = self.revision_history()
582
 
        rev_history.extend(revision_ids)
 
555
        mutter("add {%s} to revision-history" % revision_id)
 
556
        rev_history = self.revision_history() + [revision_id]
583
557
 
584
558
        f = AtomicFile(self.controlfilename('revision-history'))
585
559
        try:
590
564
            f.close()
591
565
 
592
566
 
593
 
    def get_revision_xml(self, revision_id):
594
 
        """Return XML file object for revision object."""
595
 
        if not revision_id or not isinstance(revision_id, basestring):
596
 
            raise InvalidRevisionId(revision_id)
597
 
 
598
 
        self.lock_read()
599
 
        try:
600
 
            try:
601
 
                return self.revision_store[revision_id]
602
 
            except IndexError:
603
 
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
604
 
        finally:
605
 
            self.unlock()
606
 
 
607
 
 
608
567
    def get_revision(self, revision_id):
609
568
        """Return the Revision object for a named revision"""
610
 
        xml_file = self.get_revision_xml(revision_id)
 
569
        from bzrlib.revision import Revision
 
570
        from bzrlib.xml import unpack_xml
611
571
 
 
572
        self.lock_read()
612
573
        try:
613
 
            r = unpack_xml(Revision, xml_file)
614
 
        except SyntaxError, e:
615
 
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
616
 
                                         [revision_id,
617
 
                                          str(e)])
 
574
            if not revision_id or not isinstance(revision_id, basestring):
 
575
                raise ValueError('invalid revision-id: %r' % revision_id)
 
576
            r = unpack_xml(Revision, self.revision_store[revision_id])
 
577
        finally:
 
578
            self.unlock()
618
579
            
619
580
        assert r.revision_id == revision_id
620
581
        return r
621
 
 
622
 
 
623
 
    def get_revision_delta(self, revno):
624
 
        """Return the delta for one revision.
625
 
 
626
 
        The delta is relative to its mainline predecessor, or the
627
 
        empty tree for revision 1.
628
 
        """
629
 
        assert isinstance(revno, int)
630
 
        rh = self.revision_history()
631
 
        if not (1 <= revno <= len(rh)):
632
 
            raise InvalidRevisionNumber(revno)
633
 
 
634
 
        # revno is 1-based; list is 0-based
635
 
 
636
 
        new_tree = self.revision_tree(rh[revno-1])
637
 
        if revno == 1:
638
 
            old_tree = EmptyTree()
639
 
        else:
640
 
            old_tree = self.revision_tree(rh[revno-2])
641
 
 
642
 
        return compare_trees(old_tree, new_tree)
643
 
 
644
582
        
645
583
 
646
584
    def get_revision_sha1(self, revision_id):
651
589
        # the revision, (add signatures/remove signatures) and still
652
590
        # have all hash pointers stay consistent.
653
591
        # But for now, just hash the contents.
654
 
        return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
 
592
        return sha_file(self.revision_store[revision_id])
655
593
 
656
594
 
657
595
    def get_inventory(self, inventory_id):
663
601
        from bzrlib.inventory import Inventory
664
602
        from bzrlib.xml import unpack_xml
665
603
 
666
 
        return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
667
 
 
668
 
 
669
 
    def get_inventory_xml(self, inventory_id):
670
 
        """Get inventory XML as a file object."""
671
 
        return self.inventory_store[inventory_id]
 
604
        return unpack_xml(Inventory, self.inventory_store[inventory_id])
672
605
            
673
606
 
674
607
    def get_inventory_sha1(self, inventory_id):
675
608
        """Return the sha1 hash of the inventory entry
676
609
        """
677
 
        return sha_file(self.get_inventory_xml(inventory_id))
 
610
        return sha_file(self.inventory_store[inventory_id])
678
611
 
679
612
 
680
613
    def get_revision_inventory(self, revision_id):
683
616
        # must be the same as its revision, so this is trivial.
684
617
        if revision_id == None:
685
618
            from bzrlib.inventory import Inventory
686
 
            return Inventory(self.get_root_id())
 
619
            return Inventory()
687
620
        else:
688
621
            return self.get_inventory(revision_id)
689
622
 
746
679
                return r+1, my_history[r]
747
680
        return None, None
748
681
 
 
682
    def enum_history(self, direction):
 
683
        """Return (revno, revision_id) for history of branch.
 
684
 
 
685
        direction
 
686
            'forward' is from earliest to latest
 
687
            'reverse' is from latest to earliest
 
688
        """
 
689
        rh = self.revision_history()
 
690
        if direction == 'forward':
 
691
            i = 1
 
692
            for rid in rh:
 
693
                yield i, rid
 
694
                i += 1
 
695
        elif direction == 'reverse':
 
696
            i = len(rh)
 
697
            while i > 0:
 
698
                yield i, rh[i-1]
 
699
                i -= 1
 
700
        else:
 
701
            raise ValueError('invalid history direction', direction)
 
702
 
749
703
 
750
704
    def revno(self):
751
705
        """Return current revision number for this branch.
766
720
            return None
767
721
 
768
722
 
769
 
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
 
723
    def missing_revisions(self, other, stop_revision=None):
770
724
        """
771
725
        If self and other have not diverged, return a list of the revisions
772
726
        present in other, but missing from self.
810
764
        return other_history[self_len:stop_revision]
811
765
 
812
766
 
813
 
    def update_revisions(self, other, stop_revision=None, revision_ids=None):
 
767
    def update_revisions(self, other, stop_revision=None):
814
768
        """Pull in all new revisions from other branch.
815
769
        
816
770
        >>> from bzrlib.commit import commit
834
788
        True
835
789
        """
836
790
        from bzrlib.progress import ProgressBar
 
791
        try:
 
792
            set
 
793
        except NameError:
 
794
            from sets import Set as set
837
795
 
838
796
        pb = ProgressBar()
839
797
 
840
798
        pb.update('comparing histories')
841
 
        if revision_ids is None:
842
 
            revision_ids = self.missing_revisions(other, stop_revision)
 
799
        revision_ids = self.missing_revisions(other, stop_revision)
843
800
 
844
801
        if hasattr(other.revision_store, "prefetch"):
845
802
            other.revision_store.prefetch(revision_ids)
991
948
        elif val.lower() == 'tomorrow':
992
949
            dt = today + datetime.timedelta(days=1)
993
950
        else:
994
 
            import re
995
951
            # This should be done outside the function to avoid recompiling it.
996
952
            _date_re = re.compile(
997
953
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
1047
1003
 
1048
1004
        `revision_id` may be None for the null revision, in which case
1049
1005
        an `EmptyTree` is returned."""
 
1006
        from bzrlib.tree import EmptyTree, RevisionTree
1050
1007
        # TODO: refactor this to use an existing revision object
1051
1008
        # so we don't need to read it in twice.
1052
1009
        if revision_id == None:
1067
1024
 
1068
1025
        If there are no revisions yet, return an `EmptyTree`.
1069
1026
        """
 
1027
        from bzrlib.tree import EmptyTree, RevisionTree
1070
1028
        r = self.last_patch()
1071
1029
        if r == None:
1072
1030
            return EmptyTree()
1392
1350
 
1393
1351
    s = hexlify(rand_bytes(8))
1394
1352
    return '-'.join((name, compact_date(time()), s))
1395
 
 
1396
 
 
1397
 
def gen_root_id():
1398
 
    """Return a new tree-root file id."""
1399
 
    return gen_file_id('TREE_ROOT')
1400