~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: aaron.bentley at utoronto
  • Date: 2005-09-04 02:59:56 UTC
  • mfrom: (1172)
  • mto: (1185.3.4)
  • mto: This revision was merged to the branch mainline in revision 1178.
  • Revision ID: aaron.bentley@utoronto.ca-20050904025956-776ba4f07de97700
Merged mpool's latest changes (~0.0.7)

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
     splitpath, \
25
25
     sha_file, appendpath, file_kind
26
26
 
27
 
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId, \
28
 
     DivergedBranches, NotBranchError
 
27
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId
 
28
import bzrlib.errors
29
29
from bzrlib.textui import show_status
30
30
from bzrlib.revision import Revision
 
31
from bzrlib.xml import unpack_xml
31
32
from bzrlib.delta import compare_trees
32
33
from bzrlib.tree import EmptyTree, RevisionTree
33
 
import bzrlib.xml
34
34
import bzrlib.ui
35
35
 
36
36
 
49
49
 
50
50
def find_branch(f, **args):
51
51
    if f and (f.startswith('http://') or f.startswith('https://')):
52
 
        from bzrlib.remotebranch import RemoteBranch
53
 
        return RemoteBranch(f, **args)
 
52
        import remotebranch 
 
53
        return remotebranch.RemoteBranch(f, **args)
54
54
    else:
55
55
        return Branch(f, **args)
56
56
 
57
57
 
58
58
def find_cached_branch(f, cache_root, **args):
59
 
    from bzrlib.remotebranch import RemoteBranch
 
59
    from remotebranch import RemoteBranch
60
60
    br = find_branch(f, **args)
61
61
    def cacheify(br, store_name):
62
 
        from bzrlib.meta_store import CachedStore
 
62
        from meta_store import CachedStore
63
63
        cache_path = os.path.join(cache_root, store_name)
64
64
        os.mkdir(cache_path)
65
65
        new_store = CachedStore(getattr(br, store_name), cache_path)
94
94
        if tail:
95
95
            s.insert(0, tail)
96
96
    else:
 
97
        from errors import NotBranchError
97
98
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
98
99
 
99
100
    return os.sep.join(s)
127
128
        head, tail = os.path.split(f)
128
129
        if head == f:
129
130
            # reached the root, whatever that may be
130
 
            raise NotBranchError('%s is not in a branch' % orig_f)
 
131
            raise bzrlib.errors.NotBranchError('%s is not in a branch' % orig_f)
131
132
        f = head
132
133
 
133
134
 
134
135
 
 
136
# XXX: move into bzrlib.errors; subclass BzrError    
 
137
class DivergedBranches(Exception):
 
138
    def __init__(self, branch1, branch2):
 
139
        self.branch1 = branch1
 
140
        self.branch2 = branch2
 
141
        Exception.__init__(self, "These branches have diverged.")
 
142
 
135
143
 
136
144
######################################################################
137
145
# branch objects
165
173
    def __init__(self, base, init=False, find_root=True):
166
174
        """Create new branch object at a particular location.
167
175
 
168
 
        base -- Base directory for the branch. May be a file:// url.
 
176
        base -- Base directory for the branch.
169
177
        
170
178
        init -- If True, create new control files in a previously
171
179
             unversioned directory.  If False, the branch must already
184
192
        elif find_root:
185
193
            self.base = find_branch_root(base)
186
194
        else:
187
 
            if base.startswith("file://"):
188
 
                base = base[7:]
189
195
            self.base = os.path.realpath(base)
190
196
            if not isdir(self.controlfilename('.')):
 
197
                from errors import NotBranchError
191
198
                raise NotBranchError("not a bzr branch: %s" % quotefn(base),
192
199
                                     ['use "bzr init" to initialize a new working tree',
193
200
                                      'current bzr can only operate from top-of-tree'])
207
214
 
208
215
    def __del__(self):
209
216
        if self._lock_mode or self._lock:
210
 
            from bzrlib.warnings import warn
 
217
            from warnings import warn
211
218
            warn("branch %r was not explicitly unlocked" % self)
212
219
            self._lock.unlock()
213
220
 
 
221
 
214
222
    def lock_write(self):
215
223
        if self._lock_mode:
216
224
            if self._lock_mode != 'w':
217
 
                from bzrlib.errors import LockError
 
225
                from errors import LockError
218
226
                raise LockError("can't upgrade to a write lock from %r" %
219
227
                                self._lock_mode)
220
228
            self._lock_count += 1
240
248
                        
241
249
    def unlock(self):
242
250
        if not self._lock_mode:
243
 
            from bzrlib.errors import LockError
 
251
            from errors import LockError
244
252
            raise LockError('branch %r is not locked' % (self))
245
253
 
246
254
        if self._lock_count > 1:
294
302
 
295
303
    def _make_control(self):
296
304
        from bzrlib.inventory import Inventory
 
305
        from bzrlib.xml import pack_xml
297
306
        
298
307
        os.mkdir(self.controlfilename([]))
299
308
        self.controlfile('README', 'w').write(
312
321
        # if we want per-tree root ids then this is the place to set
313
322
        # them; they're not needed for now and so ommitted for
314
323
        # simplicity.
315
 
        f = self.controlfile('inventory','w')
316
 
        bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
317
 
 
 
324
        pack_xml(Inventory(), self.controlfile('inventory','w'))
318
325
 
319
326
    def _check_format(self):
320
327
        """Check this branch format is supported.
328
335
        # on Windows from Linux and so on.  I think it might be better
329
336
        # to always make all internal files in unix format.
330
337
        fmt = self.controlfile('branch-format', 'r').read()
331
 
        fmt = fmt.replace('\r\n', '\n')
 
338
        fmt.replace('\r\n', '')
332
339
        if fmt != BZR_BRANCH_FORMAT:
333
340
            raise BzrError('sorry, branch format %r not supported' % fmt,
334
341
                           ['use a different bzr version',
354
361
    def read_working_inventory(self):
355
362
        """Read the working inventory."""
356
363
        from bzrlib.inventory import Inventory
 
364
        from bzrlib.xml import unpack_xml
 
365
        from time import time
 
366
        before = time()
357
367
        self.lock_read()
358
368
        try:
359
369
            # ElementTree does its own conversion from UTF-8, so open in
360
370
            # binary.
361
 
            f = self.controlfile('inventory', 'rb')
362
 
            return bzrlib.xml.serializer_v4.read_inventory(f)
 
371
            inv = unpack_xml(Inventory,
 
372
                             self.controlfile('inventory', 'rb'))
 
373
            mutter("loaded inventory of %d items in %f"
 
374
                   % (len(inv), time() - before))
 
375
            return inv
363
376
        finally:
364
377
            self.unlock()
365
378
            
371
384
        will be committed to the next revision.
372
385
        """
373
386
        from bzrlib.atomicfile import AtomicFile
 
387
        from bzrlib.xml import pack_xml
374
388
        
375
389
        self.lock_write()
376
390
        try:
377
391
            f = AtomicFile(self.controlfilename('inventory'), 'wb')
378
392
            try:
379
 
                bzrlib.xml.serializer_v4.write_inventory(inv, f)
 
393
                pack_xml(inv, f)
380
394
                f.commit()
381
395
            finally:
382
396
                f.close()
567
581
            f.close()
568
582
 
569
583
 
570
 
    def get_revision_xml_file(self, revision_id):
 
584
    def get_revision_xml(self, revision_id):
571
585
        """Return XML file object for revision object."""
572
586
        if not revision_id or not isinstance(revision_id, basestring):
573
587
            raise InvalidRevisionId(revision_id)
576
590
        try:
577
591
            try:
578
592
                return self.revision_store[revision_id]
579
 
            except (IndexError, KeyError):
 
593
            except IndexError:
580
594
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
581
595
        finally:
582
596
            self.unlock()
583
597
 
584
598
 
585
 
    #deprecated
586
 
    get_revision_xml = get_revision_xml_file
587
 
 
588
 
 
589
599
    def get_revision(self, revision_id):
590
600
        """Return the Revision object for a named revision"""
591
 
        xml_file = self.get_revision_xml_file(revision_id)
 
601
        xml_file = self.get_revision_xml(revision_id)
592
602
 
593
603
        try:
594
 
            r = bzrlib.xml.serializer_v4.read_revision(xml_file)
 
604
            r = unpack_xml(Revision, xml_file)
595
605
        except SyntaxError, e:
596
606
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
597
607
                                         [revision_id,
642
652
               parameter which can be either an integer revno or a
643
653
               string hash."""
644
654
        from bzrlib.inventory import Inventory
 
655
        from bzrlib.xml import unpack_xml
645
656
 
646
 
        f = self.get_inventory_xml_file(inventory_id)
647
 
        return bzrlib.xml.serializer_v4.read_inventory(f)
 
657
        return unpack_xml(Inventory, self.get_inventory_xml(inventory_id))
648
658
 
649
659
 
650
660
    def get_inventory_xml(self, inventory_id):
651
661
        """Get inventory XML as a file object."""
652
662
        return self.inventory_store[inventory_id]
653
 
 
654
 
    get_inventory_xml_file = get_inventory_xml
655
663
            
656
664
 
657
665
    def get_inventory_sha1(self, inventory_id):
687
695
 
688
696
    def common_ancestor(self, other, self_revno=None, other_revno=None):
689
697
        """
690
 
        >>> from bzrlib.commit import commit
 
698
        >>> import commit
691
699
        >>> sb = ScratchBranch(files=['foo', 'foo~'])
692
700
        >>> sb.common_ancestor(sb) == (None, None)
693
701
        True
694
 
        >>> commit(sb, "Committing first revision", verbose=False)
 
702
        >>> commit.commit(sb, "Committing first revision", verbose=False)
695
703
        >>> sb.common_ancestor(sb)[0]
696
704
        1
697
705
        >>> clone = sb.clone()
698
 
        >>> commit(sb, "Committing second revision", verbose=False)
 
706
        >>> commit.commit(sb, "Committing second revision", verbose=False)
699
707
        >>> sb.common_ancestor(sb)[0]
700
708
        2
701
709
        >>> sb.common_ancestor(clone)[0]
702
710
        1
703
 
        >>> commit(clone, "Committing divergent second revision", 
 
711
        >>> commit.commit(clone, "Committing divergent second revision", 
704
712
        ...               verbose=False)
705
713
        >>> sb.common_ancestor(clone)[0]
706
714
        1
797
805
        """Pull in all new revisions from other branch.
798
806
        """
799
807
        from bzrlib.fetch import greedy_fetch
800
 
        from bzrlib.revision import get_intervening_revisions
801
808
 
802
809
        pb = bzrlib.ui.ui_factory.progress_bar()
803
810
        pb.update('comparing histories')
804
 
        if stop_revision is None:
805
 
            other_revision = other.last_patch()
 
811
 
 
812
        revision_ids = self.missing_revisions(other, stop_revision)
 
813
 
 
814
        if len(revision_ids) > 0:
 
815
            count = greedy_fetch(self, other, revision_ids[-1], pb)[0]
806
816
        else:
807
 
            other_revision = other.lookup_revision(stop_revision)
808
 
        count = greedy_fetch(self, other, other_revision, pb)[0]
809
 
        try:
810
 
            revision_ids = self.missing_revisions(other, stop_revision)
811
 
        except DivergedBranches, e:
812
 
            try:
813
 
                revision_ids = get_intervening_revisions(self.last_patch(), 
814
 
                                                         other_revision, self)
815
 
                assert self.last_patch() not in revision_ids
816
 
            except bzrlib.errors.NotAncestor:
817
 
                raise e
818
 
 
 
817
            count = 0
819
818
        self.append_revision(*revision_ids)
 
819
        ## note("Added %d revisions." % count)
820
820
        pb.clear()
821
821
 
822
822
    def install_revisions(self, other, revision_ids, pb):
823
823
        if hasattr(other.revision_store, "prefetch"):
824
824
            other.revision_store.prefetch(revision_ids)
825
825
        if hasattr(other.inventory_store, "prefetch"):
826
 
            inventory_ids = []
827
 
            for rev_id in revision_ids:
828
 
                try:
829
 
                    revision = other.get_revision(rev_id).inventory_id
830
 
                    inventory_ids.append(revision)
831
 
                except bzrlib.errors.NoSuchRevision:
832
 
                    pass
 
826
            inventory_ids = [other.get_revision(r).inventory_id
 
827
                             for r in revision_ids]
833
828
            other.inventory_store.prefetch(inventory_ids)
834
829
 
835
830
        if pb is None:
1083
1078
                    return (i+1,)
1084
1079
    REVISION_NAMESPACES['date:'] = _namespace_date
1085
1080
 
1086
 
 
1087
 
    def _namespace_ancestor(self, revs, revision):
1088
 
        from revision import common_ancestor, MultipleRevisionSources
1089
 
        other_branch = find_branch(_trim_namespace('ancestor', revision))
1090
 
        revision_a = self.last_patch()
1091
 
        revision_b = other_branch.last_patch()
1092
 
        for r, b in ((revision_a, self), (revision_b, other_branch)):
1093
 
            if r is None:
1094
 
                raise bzrlib.errors.NoCommits(b)
1095
 
        revision_source = MultipleRevisionSources(self, other_branch)
1096
 
        result = common_ancestor(revision_a, revision_b, revision_source)
1097
 
        try:
1098
 
            revno = self.revision_id_to_revno(result)
1099
 
        except bzrlib.errors.NoSuchRevision:
1100
 
            revno = None
1101
 
        return revno,result
1102
 
        
1103
 
 
1104
 
    REVISION_NAMESPACES['ancestor:'] = _namespace_ancestor
1105
 
 
1106
1081
    def revision_tree(self, revision_id):
1107
1082
        """Return Tree for a revision on this branch.
1108
1083
 
1119
1094
 
1120
1095
    def working_tree(self):
1121
1096
        """Return a `Tree` for the working copy."""
1122
 
        from bzrlib.workingtree import WorkingTree
 
1097
        from workingtree import WorkingTree
1123
1098
        return WorkingTree(self.base, self.read_working_inventory())
1124
1099
 
1125
1100
 
1373
1348
        finally:
1374
1349
            self.unlock()
1375
1350
 
1376
 
    def check_revno(self, revno):
1377
 
        """\
1378
 
        Check whether a revno corresponds to any revision.
1379
 
        Zero (the NULL revision) is considered valid.
1380
 
        """
1381
 
        if revno != 0:
1382
 
            self.check_real_revno(revno)
1383
 
            
1384
 
    def check_real_revno(self, revno):
1385
 
        """\
1386
 
        Check whether a revno corresponds to a real revision.
1387
 
        Zero (the NULL revision) is considered invalid
1388
 
        """
1389
 
        if revno < 1 or revno > self.revno():
1390
 
            raise InvalidRevisionNumber(revno)
1391
 
        
1392
1351
        
1393
1352
 
1394
1353
 
1516
1475
    return gen_file_id('TREE_ROOT')
1517
1476
 
1518
1477
 
 
1478
def pull_loc(branch):
 
1479
    # TODO: Should perhaps just make attribute be 'base' in
 
1480
    # RemoteBranch and Branch?
 
1481
    if hasattr(branch, "baseurl"):
 
1482
        return branch.baseurl
 
1483
    else:
 
1484
        return branch.base
 
1485
 
 
1486
 
1519
1487
def copy_branch(branch_from, to_location, revision=None):
1520
1488
    """Copy branch_from into the existing directory to_location.
1521
1489
 
1527
1495
        The name of a local directory that exists but is empty.
1528
1496
    """
1529
1497
    from bzrlib.merge import merge
 
1498
    from bzrlib.branch import Branch
1530
1499
 
1531
1500
    assert isinstance(branch_from, Branch)
1532
1501
    assert isinstance(to_location, basestring)
1540
1509
    br_to.update_revisions(branch_from, stop_revision=revno)
1541
1510
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1542
1511
          check_clean=False, ignore_zero=True)
1543
 
    br_to.set_parent(branch_from.base)
1544
 
    return br_to
 
1512
    
 
1513
    from_location = pull_loc(branch_from)
 
1514
    br_to.set_parent(pull_loc(branch_from))
1545
1515
 
1546
 
def _trim_namespace(namespace, spec):
1547
 
    full_namespace = namespace + ':'
1548
 
    assert spec.startswith(full_namespace)
1549
 
    return spec[len(full_namespace):]