/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-07-04 12:22:34 UTC
  • Revision ID: mbp@sourcefrog.net-20050704122234-e35f78b10d8a009d
- Clear pending-merge list when committing.

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
30
 
43
31
def find_branch(f, **args):
44
32
    if f and (f.startswith('http://') or f.startswith('https://')):
130
118
        Exception.__init__(self, "These branches have diverged.")
131
119
 
132
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
 
133
129
######################################################################
134
130
# branch objects
135
131
 
154
150
    _lock_count = None
155
151
    _lock = None
156
152
    
157
 
    # Map some sort of prefix into a namespace
158
 
    # stuff like "revno:10", "revid:", etc.
159
 
    # This should match a prefix with a function which accepts
160
 
    REVISION_NAMESPACES = {}
161
 
 
162
153
    def __init__(self, base, init=False, find_root=True):
163
154
        """Create new branch object at a particular location.
164
155
 
316
307
            self.controlfile(f, 'w').write('')
317
308
        mutter('created control directory in ' + self.base)
318
309
 
319
 
        pack_xml(Inventory(gen_root_id()), self.controlfile('inventory','w'))
 
310
        pack_xml(Inventory(), self.controlfile('inventory','w'))
320
311
 
321
312
 
322
313
    def _check_format(self):
337
328
                           ['use a different bzr version',
338
329
                            'or remove the .bzr directory and "bzr init" again'])
339
330
 
340
 
    def get_root_id(self):
341
 
        """Return the id of this branches root"""
342
 
        inv = self.read_working_inventory()
343
 
        return inv.root.file_id
344
331
 
345
 
    def set_root_id(self, file_id):
346
 
        inv = self.read_working_inventory()
347
 
        orig_root_id = inv.root.file_id
348
 
        del inv._byid[inv.root.file_id]
349
 
        inv.root.file_id = file_id
350
 
        inv._byid[inv.root.file_id] = inv.root
351
 
        for fid in inv:
352
 
            entry = inv[fid]
353
 
            if entry.parent_id in (None, orig_root_id):
354
 
                entry.parent_id = inv.root.file_id
355
 
        self._write_inventory(inv)
356
332
 
357
333
    def read_working_inventory(self):
358
334
        """Read the working inventory."""
365
341
            # ElementTree does its own conversion from UTF-8, so open in
366
342
            # binary.
367
343
            inv = unpack_xml(Inventory,
368
 
                             self.controlfile('inventory', 'rb'))
 
344
                                  self.controlfile('inventory', 'rb'))
369
345
            mutter("loaded inventory of %d items in %f"
370
346
                   % (len(inv), time() - before))
371
347
            return inv
426
402
              add all non-ignored children.  Perhaps do that in a
427
403
              higher-level method.
428
404
        """
 
405
        from bzrlib.textui import show_status
429
406
        # TODO: Re-adding a file that is removed in the working copy
430
407
        # should probably put it back with the previous ID.
431
408
        if isinstance(files, basestring):
484
461
            # use inventory as it was in that revision
485
462
            file_id = tree.inventory.path2id(file)
486
463
            if not file_id:
487
 
                raise BzrError("%r is not present in revision %s" % (file, revno))
 
464
                raise BzrError("%r is not present in revision %d" % (file, revno))
488
465
            tree.print_file(file_id)
489
466
        finally:
490
467
            self.unlock()
504
481
        is the opposite of add.  Removing it is consistent with most
505
482
        other tools.  Maybe an option.
506
483
        """
 
484
        from bzrlib.textui import show_status
507
485
        ## TODO: Normalize names
508
486
        ## TODO: Remove nested loops; better scalability
509
487
        if isinstance(files, basestring):
538
516
    # FIXME: this doesn't need to be a branch method
539
517
    def set_inventory(self, new_inventory_list):
540
518
        from bzrlib.inventory import Inventory, InventoryEntry
541
 
        inv = Inventory(self.get_root_id())
 
519
        inv = Inventory()
542
520
        for path, file_id, parent, kind in new_inventory_list:
543
521
            name = os.path.basename(path)
544
522
            if name == "":
566
544
        return self.working_tree().unknowns()
567
545
 
568
546
 
569
 
    def append_revision(self, *revision_ids):
 
547
    def append_revision(self, revision_id):
570
548
        from bzrlib.atomicfile import AtomicFile
571
549
 
572
 
        for revision_id in revision_ids:
573
 
            mutter("add {%s} to revision-history" % revision_id)
574
 
 
575
 
        rev_history = self.revision_history()
576
 
        rev_history.extend(revision_ids)
 
550
        mutter("add {%s} to revision-history" % revision_id)
 
551
        rev_history = self.revision_history() + [revision_id]
577
552
 
578
553
        f = AtomicFile(self.controlfilename('revision-history'))
579
554
        try:
584
559
            f.close()
585
560
 
586
561
 
587
 
    def get_revision_xml(self, revision_id):
588
 
        """Return XML file object for revision object."""
589
 
        if not revision_id or not isinstance(revision_id, basestring):
590
 
            raise InvalidRevisionId(revision_id)
591
 
 
592
 
        self.lock_read()
593
 
        try:
594
 
            try:
595
 
                return self.revision_store[revision_id]
596
 
            except IndexError:
597
 
                raise bzrlib.errors.NoSuchRevision(revision_id)
598
 
        finally:
599
 
            self.unlock()
600
 
 
601
 
 
602
562
    def get_revision(self, revision_id):
603
563
        """Return the Revision object for a named revision"""
604
 
        xml_file = self.get_revision_xml(revision_id)
 
564
        from bzrlib.revision import Revision
 
565
        from bzrlib.xml import unpack_xml
605
566
 
 
567
        self.lock_read()
606
568
        try:
607
 
            r = unpack_xml(Revision, xml_file)
608
 
        except SyntaxError, e:
609
 
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
610
 
                                         [revision_id,
611
 
                                          str(e)])
 
569
            if not revision_id or not isinstance(revision_id, basestring):
 
570
                raise ValueError('invalid revision-id: %r' % revision_id)
 
571
            r = unpack_xml(Revision, self.revision_store[revision_id])
 
572
        finally:
 
573
            self.unlock()
612
574
            
613
575
        assert r.revision_id == revision_id
614
576
        return r
615
 
 
616
 
 
617
 
    def get_revision_delta(self, revno):
618
 
        """Return the delta for one revision.
619
 
 
620
 
        The delta is relative to its mainline predecessor, or the
621
 
        empty tree for revision 1.
622
 
        """
623
 
        assert isinstance(revno, int)
624
 
        rh = self.revision_history()
625
 
        if not (1 <= revno <= len(rh)):
626
 
            raise InvalidRevisionNumber(revno)
627
 
 
628
 
        # revno is 1-based; list is 0-based
629
 
 
630
 
        new_tree = self.revision_tree(rh[revno-1])
631
 
        if revno == 1:
632
 
            old_tree = EmptyTree()
633
 
        else:
634
 
            old_tree = self.revision_tree(rh[revno-2])
635
 
 
636
 
        return compare_trees(old_tree, new_tree)
637
 
 
638
577
        
639
578
 
640
579
    def get_revision_sha1(self, revision_id):
645
584
        # the revision, (add signatures/remove signatures) and still
646
585
        # have all hash pointers stay consistent.
647
586
        # But for now, just hash the contents.
648
 
        return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
 
587
        return sha_file(self.revision_store[revision_id])
649
588
 
650
589
 
651
590
    def get_inventory(self, inventory_id):
668
607
 
669
608
    def get_revision_inventory(self, revision_id):
670
609
        """Return inventory of a past revision."""
671
 
        # bzr 0.0.6 imposes the constraint that the inventory_id
672
 
        # must be the same as its revision, so this is trivial.
673
610
        if revision_id == None:
674
611
            from bzrlib.inventory import Inventory
675
 
            return Inventory(self.get_root_id())
 
612
            return Inventory()
676
613
        else:
677
 
            return self.get_inventory(revision_id)
 
614
            return self.get_inventory(self.get_revision(revision_id).inventory_id)
678
615
 
679
616
 
680
617
    def revision_history(self):
735
672
                return r+1, my_history[r]
736
673
        return None, None
737
674
 
 
675
    def enum_history(self, direction):
 
676
        """Return (revno, revision_id) for history of branch.
 
677
 
 
678
        direction
 
679
            'forward' is from earliest to latest
 
680
            'reverse' is from latest to earliest
 
681
        """
 
682
        rh = self.revision_history()
 
683
        if direction == 'forward':
 
684
            i = 1
 
685
            for rid in rh:
 
686
                yield i, rid
 
687
                i += 1
 
688
        elif direction == 'reverse':
 
689
            i = len(rh)
 
690
            while i > 0:
 
691
                yield i, rh[i-1]
 
692
                i -= 1
 
693
        else:
 
694
            raise ValueError('invalid history direction', direction)
 
695
 
738
696
 
739
697
    def revno(self):
740
698
        """Return current revision number for this branch.
823
781
        True
824
782
        """
825
783
        from bzrlib.progress import ProgressBar
 
784
        try:
 
785
            set
 
786
        except NameError:
 
787
            from sets import Set as set
826
788
 
827
789
        pb = ProgressBar()
828
790
 
872
834
        commit(self, *args, **kw)
873
835
        
874
836
 
875
 
    def lookup_revision(self, revision):
876
 
        """Return the revision identifier for a given revision information."""
877
 
        revno, info = self.get_revision_info(revision)
878
 
        return info
879
 
 
880
 
    def get_revision_info(self, revision):
881
 
        """Return (revno, revision id) for revision identifier.
882
 
 
883
 
        revision can be an integer, in which case it is assumed to be revno (though
884
 
            this will translate negative values into positive ones)
885
 
        revision can also be a string, in which case it is parsed for something like
886
 
            'date:' or 'revid:' etc.
887
 
        """
888
 
        if revision is None:
889
 
            return 0, None
890
 
        revno = None
891
 
        try:# Convert to int if possible
892
 
            revision = int(revision)
893
 
        except ValueError:
894
 
            pass
895
 
        revs = self.revision_history()
896
 
        if isinstance(revision, int):
897
 
            if revision == 0:
898
 
                return 0, None
899
 
            # Mabye we should do this first, but we don't need it if revision == 0
900
 
            if revision < 0:
901
 
                revno = len(revs) + revision + 1
902
 
            else:
903
 
                revno = revision
904
 
        elif isinstance(revision, basestring):
905
 
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
906
 
                if revision.startswith(prefix):
907
 
                    revno = func(self, revs, revision)
908
 
                    break
909
 
            else:
910
 
                raise BzrError('No namespace registered for string: %r' % revision)
911
 
 
912
 
        if revno is None or revno <= 0 or revno > len(revs):
913
 
            raise BzrError("no such revision %s" % revision)
914
 
        return revno, revs[revno-1]
915
 
 
916
 
    def _namespace_revno(self, revs, revision):
917
 
        """Lookup a revision by revision number"""
918
 
        assert revision.startswith('revno:')
919
 
        try:
920
 
            return int(revision[6:])
921
 
        except ValueError:
922
 
            return None
923
 
    REVISION_NAMESPACES['revno:'] = _namespace_revno
924
 
 
925
 
    def _namespace_revid(self, revs, revision):
926
 
        assert revision.startswith('revid:')
927
 
        try:
928
 
            return revs.index(revision[6:]) + 1
929
 
        except ValueError:
930
 
            return None
931
 
    REVISION_NAMESPACES['revid:'] = _namespace_revid
932
 
 
933
 
    def _namespace_last(self, revs, revision):
934
 
        assert revision.startswith('last:')
935
 
        try:
936
 
            offset = int(revision[5:])
937
 
        except ValueError:
938
 
            return None
939
 
        else:
940
 
            if offset <= 0:
941
 
                raise BzrError('You must supply a positive value for --revision last:XXX')
942
 
            return len(revs) - offset + 1
943
 
    REVISION_NAMESPACES['last:'] = _namespace_last
944
 
 
945
 
    def _namespace_tag(self, revs, revision):
946
 
        assert revision.startswith('tag:')
947
 
        raise BzrError('tag: namespace registered, but not implemented.')
948
 
    REVISION_NAMESPACES['tag:'] = _namespace_tag
949
 
 
950
 
    def _namespace_date(self, revs, revision):
951
 
        assert revision.startswith('date:')
952
 
        import datetime
953
 
        # Spec for date revisions:
954
 
        #   date:value
955
 
        #   value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
956
 
        #   it can also start with a '+/-/='. '+' says match the first
957
 
        #   entry after the given date. '-' is match the first entry before the date
958
 
        #   '=' is match the first entry after, but still on the given date.
959
 
        #
960
 
        #   +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
961
 
        #   -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
962
 
        #   =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
963
 
        #       May 13th, 2005 at 0:00
964
 
        #
965
 
        #   So the proper way of saying 'give me all entries for today' is:
966
 
        #       -r {date:+today}:{date:-tomorrow}
967
 
        #   The default is '=' when not supplied
968
 
        val = revision[5:]
969
 
        match_style = '='
970
 
        if val[:1] in ('+', '-', '='):
971
 
            match_style = val[:1]
972
 
            val = val[1:]
973
 
 
974
 
        today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
975
 
        if val.lower() == 'yesterday':
976
 
            dt = today - datetime.timedelta(days=1)
977
 
        elif val.lower() == 'today':
978
 
            dt = today
979
 
        elif val.lower() == 'tomorrow':
980
 
            dt = today + datetime.timedelta(days=1)
981
 
        else:
982
 
            import re
983
 
            # This should be done outside the function to avoid recompiling it.
984
 
            _date_re = re.compile(
985
 
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
986
 
                    r'(,|T)?\s*'
987
 
                    r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
988
 
                )
989
 
            m = _date_re.match(val)
990
 
            if not m or (not m.group('date') and not m.group('time')):
991
 
                raise BzrError('Invalid revision date %r' % revision)
992
 
 
993
 
            if m.group('date'):
994
 
                year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
995
 
            else:
996
 
                year, month, day = today.year, today.month, today.day
997
 
            if m.group('time'):
998
 
                hour = int(m.group('hour'))
999
 
                minute = int(m.group('minute'))
1000
 
                if m.group('second'):
1001
 
                    second = int(m.group('second'))
1002
 
                else:
1003
 
                    second = 0
1004
 
            else:
1005
 
                hour, minute, second = 0,0,0
1006
 
 
1007
 
            dt = datetime.datetime(year=year, month=month, day=day,
1008
 
                    hour=hour, minute=minute, second=second)
1009
 
        first = dt
1010
 
        last = None
1011
 
        reversed = False
1012
 
        if match_style == '-':
1013
 
            reversed = True
1014
 
        elif match_style == '=':
1015
 
            last = dt + datetime.timedelta(days=1)
1016
 
 
1017
 
        if reversed:
1018
 
            for i in range(len(revs)-1, -1, -1):
1019
 
                r = self.get_revision(revs[i])
1020
 
                # TODO: Handle timezone.
1021
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1022
 
                if first >= dt and (last is None or dt >= last):
1023
 
                    return i+1
1024
 
        else:
1025
 
            for i in range(len(revs)):
1026
 
                r = self.get_revision(revs[i])
1027
 
                # TODO: Handle timezone.
1028
 
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1029
 
                if first <= dt and (last is None or dt <= last):
1030
 
                    return i+1
1031
 
    REVISION_NAMESPACES['date:'] = _namespace_date
 
837
    def lookup_revision(self, revno):
 
838
        """Return revision hash for revision number."""
 
839
        if revno == 0:
 
840
            return None
 
841
 
 
842
        try:
 
843
            # list is 0-based; revisions are 1-based
 
844
            return self.revision_history()[revno-1]
 
845
        except IndexError:
 
846
            raise BzrError("no such revision %s" % revno)
 
847
 
1032
848
 
1033
849
    def revision_tree(self, revision_id):
1034
850
        """Return Tree for a revision on this branch.
1035
851
 
1036
852
        `revision_id` may be None for the null revision, in which case
1037
853
        an `EmptyTree` is returned."""
 
854
        from bzrlib.tree import EmptyTree, RevisionTree
1038
855
        # TODO: refactor this to use an existing revision object
1039
856
        # so we don't need to read it in twice.
1040
857
        if revision_id == None:
1055
872
 
1056
873
        If there are no revisions yet, return an `EmptyTree`.
1057
874
        """
 
875
        from bzrlib.tree import EmptyTree, RevisionTree
1058
876
        r = self.last_patch()
1059
877
        if r == None:
1060
878
            return EmptyTree()
1380
1198
 
1381
1199
    s = hexlify(rand_bytes(8))
1382
1200
    return '-'.join((name, compact_date(time()), s))
1383
 
 
1384
 
 
1385
 
def gen_root_id():
1386
 
    """Return a new tree-root file id."""
1387
 
    return gen_file_id('TREE_ROOT')
1388