~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-09-12 01:29:49 UTC
  • mto: (1092.2.12) (974.1.76) (1185.8.2)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: mbp@sourcefrog.net-20050912012949-73a539d3f2542173
- patch from mpe to automatically add parent directories

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import sys
19
19
import os
20
 
from cStringIO import StringIO
21
20
 
22
21
import bzrlib
23
22
from bzrlib.trace import mutter, note
25
24
     splitpath, \
26
25
     sha_file, appendpath, file_kind
27
26
 
28
 
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
29
 
                           NoSuchRevision)
 
27
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
 
28
import bzrlib.errors
30
29
from bzrlib.textui import show_status
31
30
from bzrlib.revision import Revision
32
31
from bzrlib.delta import compare_trees
33
32
from bzrlib.tree import EmptyTree, RevisionTree
34
 
from bzrlib.inventory import Inventory
35
 
from bzrlib.weavestore import WeaveStore
36
 
from bzrlib.store import ImmutableStore
37
 
import bzrlib.xml5
 
33
import bzrlib.xml
38
34
import bzrlib.ui
39
35
 
40
36
 
41
 
INVENTORY_FILEID = '__inventory'
42
 
ANCESTRY_FILEID = '__ancestry'
43
 
 
44
 
 
45
 
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
46
 
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
 
37
 
 
38
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
47
39
## TODO: Maybe include checks for common corruption of newlines, etc?
48
40
 
49
41
 
50
42
# TODO: Some operations like log might retrieve the same revisions
51
43
# repeatedly to calculate deltas.  We could perhaps have a weakref
52
 
# cache in memory to make this faster.  In general anything can be
53
 
# cached in memory between lock and unlock operations.
 
44
# cache in memory to make this faster.
54
45
 
55
46
# TODO: please move the revision-string syntax stuff out of the branch
56
47
# object; it's clutter
173
164
    _lock_mode = None
174
165
    _lock_count = None
175
166
    _lock = None
176
 
    _inventory_weave = None
177
167
    
178
168
    # Map some sort of prefix into a namespace
179
169
    # stuff like "revno:10", "revid:", etc.
195
185
        In the test suite, creation of new trees is tested using the
196
186
        `ScratchBranch` class.
197
187
        """
 
188
        from bzrlib.store import ImmutableStore
198
189
        if init:
199
190
            self.base = os.path.realpath(base)
200
191
            self._make_control()
209
200
                                      'current bzr can only operate from top-of-tree'])
210
201
        self._check_format()
211
202
 
212
 
        self.weave_store = WeaveStore(self.controlfilename('weaves'))
 
203
        self.text_store = ImmutableStore(self.controlfilename('text-store'))
213
204
        self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
 
205
        self.inventory_store = ImmutableStore(self.controlfilename('inventory-store'))
214
206
 
215
207
 
216
208
    def __str__(self):
309
301
            raise BzrError("invalid controlfile mode %r" % mode)
310
302
 
311
303
    def _make_control(self):
 
304
        from bzrlib.inventory import Inventory
 
305
        
312
306
        os.mkdir(self.controlfilename([]))
313
307
        self.controlfile('README', 'w').write(
314
308
            "This is a Bazaar-NG control directory.\n"
315
309
            "Do not change any files in this directory.\n")
316
 
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT_5)
317
 
        for d in ('text-store', 'revision-store',
318
 
                  'weaves'):
 
310
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
 
311
        for d in ('text-store', 'inventory-store', 'revision-store'):
319
312
            os.mkdir(self.controlfilename(d))
320
313
        for f in ('revision-history', 'merged-patches',
321
314
                  'pending-merged-patches', 'branch-name',
328
321
        # them; they're not needed for now and so ommitted for
329
322
        # simplicity.
330
323
        f = self.controlfile('inventory','w')
331
 
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), f)
332
 
        
 
324
        bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
333
325
 
334
326
 
335
327
    def _check_format(self):
336
328
        """Check this branch format is supported.
337
329
 
338
 
        The format level is stored, as an integer, in
339
 
        self._branch_format for code that needs to check it later.
 
330
        The current tool only supports the current unstable format.
340
331
 
341
332
        In the future, we might need different in-memory Branch
342
333
        classes to support downlevel branches.  But not yet.
343
334
        """
 
335
        # This ignores newlines so that we can open branches created
 
336
        # on Windows from Linux and so on.  I think it might be better
 
337
        # to always make all internal files in unix format.
344
338
        fmt = self.controlfile('branch-format', 'r').read()
345
 
        if fmt == BZR_BRANCH_FORMAT_5:
346
 
            self._branch_format = 5
347
 
        else:
348
 
            raise BzrError('sorry, branch format "%s" not supported; ' 
349
 
                           'use a different bzr version, '
350
 
                           'or run "bzr upgrade", '
351
 
                           'or remove the .bzr directory and "bzr init" again'
352
 
                           % fmt.rstrip('\n\r'))
 
339
        fmt = fmt.replace('\r\n', '\n')
 
340
        if fmt != BZR_BRANCH_FORMAT:
 
341
            raise BzrError('sorry, branch format %r not supported' % fmt,
 
342
                           ['use a different bzr version',
 
343
                            'or remove the .bzr directory and "bzr init" again'])
353
344
 
354
345
    def get_root_id(self):
355
346
        """Return the id of this branches root"""
370
361
 
371
362
    def read_working_inventory(self):
372
363
        """Read the working inventory."""
 
364
        from bzrlib.inventory import Inventory
373
365
        self.lock_read()
374
366
        try:
375
367
            # ElementTree does its own conversion from UTF-8, so open in
376
368
            # binary.
377
369
            f = self.controlfile('inventory', 'rb')
378
 
            return bzrlib.xml5.serializer_v5.read_inventory(f)
 
370
            return bzrlib.xml.serializer_v4.read_inventory(f)
379
371
        finally:
380
372
            self.unlock()
381
373
            
392
384
        try:
393
385
            f = AtomicFile(self.controlfilename('inventory'), 'wb')
394
386
            try:
395
 
                bzrlib.xml5.serializer_v5.write_inventory(inv, f)
 
387
                bzrlib.xml.serializer_v4.write_inventory(inv, f)
396
388
                f.commit()
397
389
            finally:
398
390
                f.close()
607
599
        xml_file = self.get_revision_xml_file(revision_id)
608
600
 
609
601
        try:
610
 
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
 
602
            r = bzrlib.xml.serializer_v4.read_revision(xml_file)
611
603
        except SyntaxError, e:
612
604
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
613
605
                                         [revision_id,
651
643
        return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
652
644
 
653
645
 
654
 
    def get_ancestry(self, revision_id):
655
 
        """Return a list of revision-ids integrated by a revision.
656
 
        """
657
 
        w = self.weave_store.get_weave(ANCESTRY_FILEID)
658
 
        # strip newlines
659
 
        return [l[:-1] for l in w.get_iter(w.lookup(revision_id))]
660
 
 
661
 
 
662
 
    def get_inventory_weave(self):
663
 
        return self.weave_store.get_weave(INVENTORY_FILEID)
664
 
 
665
 
 
666
 
    def get_inventory(self, revision_id):
667
 
        """Get Inventory object by hash."""
668
 
        # FIXME: The text gets passed around a lot coming from the weave.
669
 
        f = StringIO(self.get_inventory_xml(revision_id))
670
 
        return bzrlib.xml5.serializer_v5.read_inventory(f)
671
 
 
672
 
 
673
 
    def get_inventory_xml(self, revision_id):
 
646
    def get_inventory(self, inventory_id):
 
647
        """Get Inventory object by hash.
 
648
 
 
649
        TODO: Perhaps for this and similar methods, take a revision
 
650
               parameter which can be either an integer revno or a
 
651
               string hash."""
 
652
        from bzrlib.inventory import Inventory
 
653
 
 
654
        f = self.get_inventory_xml_file(inventory_id)
 
655
        return bzrlib.xml.serializer_v4.read_inventory(f)
 
656
 
 
657
 
 
658
    def get_inventory_xml(self, inventory_id):
674
659
        """Get inventory XML as a file object."""
675
 
        try:
676
 
            assert isinstance(revision_id, basestring), type(revision_id)
677
 
            iw = self.get_inventory_weave()
678
 
            return iw.get_text(iw.lookup(revision_id))
679
 
        except IndexError:
680
 
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
681
 
 
682
 
 
683
 
    def get_inventory_sha1(self, revision_id):
 
660
        return self.inventory_store[inventory_id]
 
661
 
 
662
    get_inventory_xml_file = get_inventory_xml
 
663
            
 
664
 
 
665
    def get_inventory_sha1(self, inventory_id):
684
666
        """Return the sha1 hash of the inventory entry
685
667
        """
686
 
        return self.get_revision(revision_id).inventory_sha1
 
668
        return sha_file(self.get_inventory_xml(inventory_id))
687
669
 
688
670
 
689
671
    def get_revision_inventory(self, revision_id):
690
672
        """Return inventory of a past revision."""
691
 
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
 
673
        # bzr 0.0.6 imposes the constraint that the inventory_id
692
674
        # must be the same as its revision, so this is trivial.
693
675
        if revision_id == None:
 
676
            from bzrlib.inventory import Inventory
694
677
            return Inventory(self.get_root_id())
695
678
        else:
696
679
            return self.get_inventory(revision_id)
716
699
        >>> sb = ScratchBranch(files=['foo', 'foo~'])
717
700
        >>> sb.common_ancestor(sb) == (None, None)
718
701
        True
719
 
        >>> commit.commit(sb, "Committing first revision")
 
702
        >>> commit.commit(sb, "Committing first revision", verbose=False)
720
703
        >>> sb.common_ancestor(sb)[0]
721
704
        1
722
705
        >>> clone = sb.clone()
723
 
        >>> commit.commit(sb, "Committing second revision")
 
706
        >>> commit.commit(sb, "Committing second revision", verbose=False)
724
707
        >>> sb.common_ancestor(sb)[0]
725
708
        2
726
709
        >>> sb.common_ancestor(clone)[0]
727
710
        1
728
 
        >>> commit.commit(clone, "Committing divergent second revision")
 
711
        >>> commit.commit(clone, "Committing divergent second revision", 
 
712
        ...               verbose=False)
729
713
        >>> sb.common_ancestor(clone)[0]
730
714
        1
731
715
        >>> sb.common_ancestor(clone) == clone.common_ancestor(sb)
835
819
        ## note("Added %d revisions." % count)
836
820
        pb.clear()
837
821
 
 
822
    def install_revisions(self, other, revision_ids, pb):
 
823
        if hasattr(other.revision_store, "prefetch"):
 
824
            other.revision_store.prefetch(revision_ids)
 
825
        if hasattr(other.inventory_store, "prefetch"):
 
826
            inventory_ids = [other.get_revision(r).inventory_id
 
827
                             for r in revision_ids]
 
828
            other.inventory_store.prefetch(inventory_ids)
 
829
 
 
830
        if pb is None:
 
831
            pb = bzrlib.ui.ui_factory.progress_bar()
 
832
                
 
833
        revisions = []
 
834
        needed_texts = set()
 
835
        i = 0
 
836
 
 
837
        failures = set()
 
838
        for i, rev_id in enumerate(revision_ids):
 
839
            pb.update('fetching revision', i+1, len(revision_ids))
 
840
            try:
 
841
                rev = other.get_revision(rev_id)
 
842
            except bzrlib.errors.NoSuchRevision:
 
843
                failures.add(rev_id)
 
844
                continue
 
845
 
 
846
            revisions.append(rev)
 
847
            inv = other.get_inventory(str(rev.inventory_id))
 
848
            for key, entry in inv.iter_entries():
 
849
                if entry.text_id is None:
 
850
                    continue
 
851
                if entry.text_id not in self.text_store:
 
852
                    needed_texts.add(entry.text_id)
 
853
 
 
854
        pb.clear()
 
855
                    
 
856
        count, cp_fail = self.text_store.copy_multi(other.text_store, 
 
857
                                                    needed_texts)
 
858
        #print "Added %d texts." % count 
 
859
        inventory_ids = [ f.inventory_id for f in revisions ]
 
860
        count, cp_fail = self.inventory_store.copy_multi(other.inventory_store, 
 
861
                                                         inventory_ids)
 
862
        #print "Added %d inventories." % count 
 
863
        revision_ids = [ f.revision_id for f in revisions]
 
864
 
 
865
        count, cp_fail = self.revision_store.copy_multi(other.revision_store, 
 
866
                                                          revision_ids,
 
867
                                                          permit_failure=True)
 
868
        assert len(cp_fail) == 0 
 
869
        return count, failures
 
870
       
838
871
 
839
872
    def commit(self, *args, **kw):
840
 
        from bzrlib.commit import Commit
841
 
        Commit().commit(self, *args, **kw)
 
873
        from bzrlib.commit import commit
 
874
        commit(self, *args, **kw)
842
875
        
843
876
 
844
877
    def lookup_revision(self, revision):
1056
1089
            return EmptyTree()
1057
1090
        else:
1058
1091
            inv = self.get_revision_inventory(revision_id)
1059
 
            return RevisionTree(self.weave_store, inv, revision_id)
 
1092
            return RevisionTree(self.text_store, inv)
1060
1093
 
1061
1094
 
1062
1095
    def working_tree(self):
1070
1103
 
1071
1104
        If there are no revisions yet, return an `EmptyTree`.
1072
1105
        """
1073
 
        return self.revision_tree(self.last_patch())
 
1106
        r = self.last_patch()
 
1107
        if r == None:
 
1108
            return EmptyTree()
 
1109
        else:
 
1110
            return RevisionTree(self.text_store, self.get_revision_inventory(r))
 
1111
 
1074
1112
 
1075
1113
 
1076
1114
    def rename_one(self, from_rel, to_rel):