~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Aaron Bentley
  • Date: 2005-09-29 21:07:17 UTC
  • mfrom: (1393.1.6)
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1419.
  • Revision ID: abentley@panoramicfeedback.com-20050929210717-cd73981590f17017
Merged the weave changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import sys
19
19
import os
 
20
import errno
 
21
from warnings import warn
 
22
 
20
23
 
21
24
import bzrlib
22
25
from bzrlib.trace import mutter, note
23
 
from bzrlib.osutils import isdir, quotefn, compact_date, rand_bytes, \
24
 
     splitpath, \
25
 
     sha_file, appendpath, file_kind
26
 
 
27
 
from bzrlib.store import copy_all
28
 
from bzrlib.errors import BzrError, InvalidRevisionNumber, InvalidRevisionId, \
29
 
     DivergedBranches, NotBranchError, UnlistableStore, UnlistableBranch
 
26
from bzrlib.osutils import (isdir, quotefn, compact_date, rand_bytes, 
 
27
                            rename, splitpath, sha_file, appendpath, 
 
28
                            file_kind)
 
29
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
 
30
                           NoSuchRevision, HistoryMissing, NotBranchError,
 
31
                           DivergedBranches, LockError, UnlistableStore,
 
32
                           UnlistableBranch)
30
33
from bzrlib.textui import show_status
31
 
from bzrlib.revision import Revision, is_ancestor
 
34
from bzrlib.revision import Revision, validate_revision_id, is_ancestor
32
35
from bzrlib.delta import compare_trees
33
36
from bzrlib.tree import EmptyTree, RevisionTree
34
 
import bzrlib.xml
 
37
from bzrlib.inventory import Inventory
 
38
from bzrlib.weavestore import WeaveStore
 
39
from bzrlib.store import copy_all, ImmutableStore
 
40
import bzrlib.xml5
35
41
import bzrlib.ui
36
42
 
37
43
 
38
 
 
39
 
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
 
44
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
 
45
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
40
46
## TODO: Maybe include checks for common corruption of newlines, etc?
41
47
 
42
48
 
43
49
# TODO: Some operations like log might retrieve the same revisions
44
50
# repeatedly to calculate deltas.  We could perhaps have a weakref
45
 
# cache in memory to make this faster.
 
51
# cache in memory to make this faster.  In general anything can be
 
52
# cached in memory between lock and unlock operations.
46
53
 
47
54
def find_branch(*ignored, **ignored_too):
48
55
    # XXX: leave this here for about one release, then remove it
125
132
        raise NotImplementedError('The Branch class is abstract')
126
133
 
127
134
    @staticmethod
 
135
    def open_downlevel(base):
 
136
        """Open a branch which may be of an old format.
 
137
        
 
138
        Only local branches are supported."""
 
139
        return LocalBranch(base, find_root=False, relax_version_check=True)
 
140
        
 
141
    @staticmethod
128
142
    def open(base):
129
143
        """Open an existing branch, rooted at 'base' (url)"""
130
144
        if base and (base.startswith('http://') or base.startswith('https://')):
135
149
 
136
150
    @staticmethod
137
151
    def open_containing(url):
138
 
        """Open an existing branch, containing url (search upwards for the root)
 
152
        """Open an existing branch which contains url.
 
153
        
 
154
        This probes for a branch at url, and searches upwards from there.
139
155
        """
140
156
        if url and (url.startswith('http://') or url.startswith('https://')):
141
157
            from bzrlib.remotebranch import RemoteBranch
182
198
    _lock_mode = None
183
199
    _lock_count = None
184
200
    _lock = None
185
 
 
186
 
    def __init__(self, base, init=False, find_root=True):
 
201
    _inventory_weave = None
 
202
    
 
203
    # Map some sort of prefix into a namespace
 
204
    # stuff like "revno:10", "revid:", etc.
 
205
    # This should match a prefix with a function which accepts
 
206
    REVISION_NAMESPACES = {}
 
207
 
 
208
    def push_stores(self, branch_to):
 
209
        """Copy the content of this branches store to branch_to."""
 
210
        if (self._branch_format != branch_to._branch_format
 
211
            or self._branch_format != 4):
 
212
            from bzrlib.fetch import greedy_fetch
 
213
            mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
 
214
                   self, self._branch_format, branch_to, branch_to._branch_format)
 
215
            greedy_fetch(to_branch=branch_to, from_branch=self,
 
216
                         revision=self.last_revision())
 
217
            return
 
218
 
 
219
        store_pairs = ((self.text_store,      branch_to.text_store),
 
220
                       (self.inventory_store, branch_to.inventory_store),
 
221
                       (self.revision_store,  branch_to.revision_store))
 
222
        try:
 
223
            for from_store, to_store in store_pairs: 
 
224
                copy_all(from_store, to_store)
 
225
        except UnlistableStore:
 
226
            raise UnlistableBranch(from_store)
 
227
 
 
228
    def __init__(self, base, init=False, find_root=True,
 
229
                 relax_version_check=False):
187
230
        """Create new branch object at a particular location.
188
231
 
189
232
        base -- Base directory for the branch. May be a file:// url.
195
238
        find_root -- If true and init is false, find the root of the
196
239
             existing branch containing base.
197
240
 
 
241
        relax_version_check -- If true, the usual check for the branch
 
242
            version is not applied.  This is intended only for
 
243
            upgrade/recovery type use; it's not guaranteed that
 
244
            all operations will work on old format branches.
 
245
 
198
246
        In the test suite, creation of new trees is tested using the
199
247
        `ScratchBranch` class.
200
248
        """
201
 
        from bzrlib.store import ImmutableStore
202
249
        if init:
203
250
            self.base = os.path.realpath(base)
204
251
            self._make_control()
209
256
                base = base[7:]
210
257
            self.base = os.path.realpath(base)
211
258
            if not isdir(self.controlfilename('.')):
212
 
                raise NotBranchError("not a bzr branch: %s" % quotefn(base),
213
 
                                     ['use "bzr init" to initialize a new working tree',
214
 
                                      'current bzr can only operate from top-of-tree'])
215
 
        self._check_format()
216
 
 
217
 
        self.text_store = ImmutableStore(self.controlfilename('text-store'))
218
 
        self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
219
 
        self.inventory_store = ImmutableStore(self.controlfilename('inventory-store'))
 
259
                raise NotBranchError('not a bzr branch: %s' % quotefn(base),
 
260
                                     ['use "bzr init" to initialize a '
 
261
                                      'new working tree'])
 
262
        self._check_format(relax_version_check)
 
263
        cfn = self.controlfilename
 
264
        if self._branch_format == 4:
 
265
            self.inventory_store = ImmutableStore(cfn('inventory-store'))
 
266
            self.text_store = ImmutableStore(cfn('text-store'))
 
267
        elif self._branch_format == 5:
 
268
            self.control_weaves = WeaveStore(cfn([]))
 
269
            self.weave_store = WeaveStore(cfn('weaves'))
 
270
            if init:
 
271
                # FIXME: Unify with make_control_files
 
272
                self.control_weaves.put_empty_weave('inventory')
 
273
                self.control_weaves.put_empty_weave('ancestry')
 
274
        self.revision_store = ImmutableStore(cfn('revision-store'))
220
275
 
221
276
 
222
277
    def __str__(self):
228
283
 
229
284
    def __del__(self):
230
285
        if self._lock_mode or self._lock:
231
 
            from bzrlib.warnings import warn
 
286
            # XXX: This should show something every time, and be suitable for
 
287
            # headless operation and embedding
232
288
            warn("branch %r was not explicitly unlocked" % self)
233
289
            self._lock.unlock()
234
290
 
235
291
    def lock_write(self):
236
292
        if self._lock_mode:
237
293
            if self._lock_mode != 'w':
238
 
                from bzrlib.errors import LockError
239
294
                raise LockError("can't upgrade to a write lock from %r" %
240
295
                                self._lock_mode)
241
296
            self._lock_count += 1
261
316
                        
262
317
    def unlock(self):
263
318
        if not self._lock_mode:
264
 
            from bzrlib.errors import LockError
265
319
            raise LockError('branch %r is not locked' % (self))
266
320
 
267
321
        if self._lock_count > 1:
314
368
            raise BzrError("invalid controlfile mode %r" % mode)
315
369
 
316
370
    def _make_control(self):
317
 
        from bzrlib.inventory import Inventory
318
 
        
319
371
        os.mkdir(self.controlfilename([]))
320
372
        self.controlfile('README', 'w').write(
321
373
            "This is a Bazaar-NG control directory.\n"
322
374
            "Do not change any files in this directory.\n")
323
 
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
324
 
        for d in ('text-store', 'inventory-store', 'revision-store'):
 
375
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT_5)
 
376
        for d in ('text-store', 'revision-store',
 
377
                  'weaves'):
325
378
            os.mkdir(self.controlfilename(d))
326
 
        for f in ('revision-history', 'merged-patches',
327
 
                  'pending-merged-patches', 'branch-name',
 
379
        for f in ('revision-history',
 
380
                  'branch-name',
328
381
                  'branch-lock',
329
382
                  'pending-merges'):
330
383
            self.controlfile(f, 'w').write('')
334
387
        # them; they're not needed for now and so ommitted for
335
388
        # simplicity.
336
389
        f = self.controlfile('inventory','w')
337
 
        bzrlib.xml.serializer_v4.write_inventory(Inventory(), f)
338
 
 
339
 
 
340
 
    def _check_format(self):
 
390
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), f)
 
391
 
 
392
 
 
393
    def _check_format(self, relax_version_check):
341
394
        """Check this branch format is supported.
342
395
 
343
 
        The current tool only supports the current unstable format.
 
396
        The format level is stored, as an integer, in
 
397
        self._branch_format for code that needs to check it later.
344
398
 
345
399
        In the future, we might need different in-memory Branch
346
400
        classes to support downlevel branches.  But not yet.
347
401
        """
348
 
        # This ignores newlines so that we can open branches created
349
 
        # on Windows from Linux and so on.  I think it might be better
350
 
        # to always make all internal files in unix format.
351
 
        fmt = self.controlfile('branch-format', 'r').read()
352
 
        fmt = fmt.replace('\r\n', '\n')
353
 
        if fmt != BZR_BRANCH_FORMAT:
 
402
        try:
 
403
            fmt = self.controlfile('branch-format', 'r').read()
 
404
        except IOError, e:
 
405
            if e.errno == errno.ENOENT:
 
406
                raise NotBranchError(self.base)
 
407
            else:
 
408
                raise
 
409
 
 
410
        if fmt == BZR_BRANCH_FORMAT_5:
 
411
            self._branch_format = 5
 
412
        elif fmt == BZR_BRANCH_FORMAT_4:
 
413
            self._branch_format = 4
 
414
 
 
415
        if (not relax_version_check
 
416
            and self._branch_format != 5):
354
417
            raise BzrError('sorry, branch format %r not supported' % fmt,
355
418
                           ['use a different bzr version',
356
419
                            'or remove the .bzr directory and "bzr init" again'])
374
437
 
375
438
    def read_working_inventory(self):
376
439
        """Read the working inventory."""
377
 
        from bzrlib.inventory import Inventory
378
440
        self.lock_read()
379
441
        try:
380
442
            # ElementTree does its own conversion from UTF-8, so open in
381
443
            # binary.
382
444
            f = self.controlfile('inventory', 'rb')
383
 
            return bzrlib.xml.serializer_v4.read_inventory(f)
 
445
            return bzrlib.xml5.serializer_v5.read_inventory(f)
384
446
        finally:
385
447
            self.unlock()
386
448
            
397
459
        try:
398
460
            f = AtomicFile(self.controlfilename('inventory'), 'wb')
399
461
            try:
400
 
                bzrlib.xml.serializer_v4.write_inventory(inv, f)
 
462
                bzrlib.xml5.serializer_v5.write_inventory(inv, f)
401
463
                f.commit()
402
464
            finally:
403
465
                f.close()
588
650
            f.close()
589
651
 
590
652
 
 
653
    def has_revision(self, revision_id):
 
654
        """True if this branch has a copy of the revision.
 
655
 
 
656
        This does not necessarily imply the revision is merge
 
657
        or on the mainline."""
 
658
        return (revision_id is None
 
659
                or revision_id in self.revision_store)
 
660
 
 
661
 
591
662
    def get_revision_xml_file(self, revision_id):
592
663
        """Return XML file object for revision object."""
593
664
        if not revision_id or not isinstance(revision_id, basestring):
603
674
            self.unlock()
604
675
 
605
676
 
606
 
    #deprecated
607
 
    get_revision_xml = get_revision_xml_file
 
677
    def get_revision_xml(self, revision_id):
 
678
        return self.get_revision_xml_file(revision_id).read()
608
679
 
609
680
 
610
681
    def get_revision(self, revision_id):
612
683
        xml_file = self.get_revision_xml_file(revision_id)
613
684
 
614
685
        try:
615
 
            r = bzrlib.xml.serializer_v4.read_revision(xml_file)
 
686
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
616
687
        except SyntaxError, e:
617
688
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
618
689
                                         [revision_id,
643
714
 
644
715
        return compare_trees(old_tree, new_tree)
645
716
 
646
 
        
647
717
 
648
718
    def get_revision_sha1(self, revision_id):
649
719
        """Hash the stored value of a revision, and return it."""
650
 
        # In the future, revision entries will be signed. At that
651
 
        # point, it is probably best *not* to include the signature
652
 
        # in the revision hash. Because that lets you re-sign
653
 
        # the revision, (add signatures/remove signatures) and still
654
 
        # have all hash pointers stay consistent.
655
 
        # But for now, just hash the contents.
656
 
        return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
657
 
 
658
 
 
659
 
    def get_inventory(self, inventory_id):
660
 
        """Get Inventory object by hash.
661
 
 
662
 
        TODO: Perhaps for this and similar methods, take a revision
663
 
               parameter which can be either an integer revno or a
664
 
               string hash."""
665
 
        from bzrlib.inventory import Inventory
666
 
 
667
 
        f = self.get_inventory_xml_file(inventory_id)
668
 
        return bzrlib.xml.serializer_v4.read_inventory(f)
669
 
 
670
 
 
671
 
    def get_inventory_xml(self, inventory_id):
 
720
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
 
721
 
 
722
 
 
723
    def _get_ancestry_weave(self):
 
724
        return self.control_weaves.get_weave('ancestry')
 
725
        
 
726
 
 
727
    def get_ancestry(self, revision_id):
 
728
        """Return a list of revision-ids integrated by a revision.
 
729
        """
 
730
        # strip newlines
 
731
        if revision_id is None:
 
732
            return [None]
 
733
        w = self._get_ancestry_weave()
 
734
        return [None] + [l[:-1] for l in w.get_iter(w.lookup(revision_id))]
 
735
 
 
736
 
 
737
    def get_inventory_weave(self):
 
738
        return self.control_weaves.get_weave('inventory')
 
739
 
 
740
 
 
741
    def get_inventory(self, revision_id):
 
742
        """Get Inventory object by hash."""
 
743
        xml = self.get_inventory_xml(revision_id)
 
744
        return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
 
745
 
 
746
 
 
747
    def get_inventory_xml(self, revision_id):
672
748
        """Get inventory XML as a file object."""
673
 
        return self.inventory_store[inventory_id]
674
 
 
675
 
    get_inventory_xml_file = get_inventory_xml
676
 
            
677
 
 
678
 
    def get_inventory_sha1(self, inventory_id):
 
749
        try:
 
750
            assert isinstance(revision_id, basestring), type(revision_id)
 
751
            iw = self.get_inventory_weave()
 
752
            return iw.get_text(iw.lookup(revision_id))
 
753
        except IndexError:
 
754
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
 
755
 
 
756
 
 
757
    def get_inventory_sha1(self, revision_id):
679
758
        """Return the sha1 hash of the inventory entry
680
759
        """
681
 
        return sha_file(self.get_inventory_xml(inventory_id))
 
760
        return self.get_revision(revision_id).inventory_sha1
682
761
 
683
762
 
684
763
    def get_revision_inventory(self, revision_id):
685
764
        """Return inventory of a past revision."""
686
 
        # bzr 0.0.6 imposes the constraint that the inventory_id
 
765
        # TODO: Unify this with get_inventory()
 
766
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
687
767
        # must be the same as its revision, so this is trivial.
688
768
        if revision_id == None:
689
 
            from bzrlib.inventory import Inventory
690
769
            return Inventory(self.get_root_id())
691
770
        else:
692
771
            return self.get_inventory(revision_id)
693
772
 
694
773
 
695
774
    def revision_history(self):
696
 
        """Return sequence of revision hashes on to this branch.
697
 
 
698
 
        >>> ScratchBranch().revision_history()
699
 
        []
700
 
        """
 
775
        """Return sequence of revision hashes on to this branch."""
701
776
        self.lock_read()
702
777
        try:
703
778
            return [l.rstrip('\r\n') for l in
760
835
        return len(self.revision_history())
761
836
 
762
837
 
763
 
    def last_patch(self):
 
838
    def last_revision(self):
764
839
        """Return last patch hash, or None if no history.
765
840
        """
766
841
        ph = self.revision_history()
771
846
 
772
847
 
773
848
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
774
 
        """
 
849
        """Return a list of new revisions that would perfectly fit.
 
850
        
775
851
        If self and other have not diverged, return a list of the revisions
776
852
        present in other, but missing from self.
777
853
 
797
873
        Traceback (most recent call last):
798
874
        DivergedBranches: These branches have diverged.
799
875
        """
 
876
        # FIXME: If the branches have diverged, but the latest
 
877
        # revision in this branch is completely merged into the other,
 
878
        # then we should still be able to pull.
800
879
        self_history = self.revision_history()
801
880
        self_len = len(self_history)
802
881
        other_history = other.revision_history()
808
887
 
809
888
        if stop_revision is None:
810
889
            stop_revision = other_len
811
 
        elif stop_revision > other_len:
812
 
            raise bzrlib.errors.NoSuchRevision(self, stop_revision)
813
 
        
 
890
        else:
 
891
            assert isinstance(stop_revision, int)
 
892
            if stop_revision > other_len:
 
893
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
814
894
        return other_history[self_len:stop_revision]
815
895
 
816
 
 
817
896
    def update_revisions(self, other, stop_revision=None):
818
 
        """Pull in all new revisions from other branch.
819
 
        """
 
897
        """Pull in new perfect-fit revisions."""
820
898
        from bzrlib.fetch import greedy_fetch
821
899
        from bzrlib.revision import get_intervening_revisions
822
 
 
823
 
        pb = bzrlib.ui.ui_factory.progress_bar()
824
 
        pb.update('comparing histories')
825
900
        if stop_revision is None:
826
 
            other_revision = other.last_patch()
827
 
        else:
828
 
            other_revision = other.get_rev_id(stop_revision)
829
 
        count = greedy_fetch(self, other, other_revision, pb)[0]
830
 
        try:
831
 
            revision_ids = self.missing_revisions(other, stop_revision)
832
 
        except DivergedBranches, e:
833
 
            try:
834
 
                revision_ids = get_intervening_revisions(self.last_patch(), 
835
 
                                                         other_revision, self)
836
 
                assert self.last_patch() not in revision_ids
837
 
            except bzrlib.errors.NotAncestor:
838
 
                if is_ancestor(self.last_patch(), other_revision, self):
839
 
                    revision_ids = []
840
 
                else:
841
 
                    raise e
842
 
 
843
 
        self.append_revision(*revision_ids)
844
 
        pb.clear()
845
 
 
846
 
    def install_revisions(self, other, revision_ids, pb):
847
 
        if hasattr(other.revision_store, "prefetch"):
848
 
            other.revision_store.prefetch(revision_ids)
849
 
        if hasattr(other.inventory_store, "prefetch"):
850
 
            inventory_ids = []
851
 
            for rev_id in revision_ids:
852
 
                try:
853
 
                    revision = other.get_revision(rev_id).inventory_id
854
 
                    inventory_ids.append(revision)
855
 
                except bzrlib.errors.NoSuchRevision:
856
 
                    pass
857
 
            other.inventory_store.prefetch(inventory_ids)
858
 
 
859
 
        if pb is None:
860
 
            pb = bzrlib.ui.ui_factory.progress_bar()
861
 
                
862
 
        revisions = []
863
 
        needed_texts = set()
864
 
        i = 0
865
 
 
866
 
        failures = set()
867
 
        for i, rev_id in enumerate(revision_ids):
868
 
            pb.update('fetching revision', i+1, len(revision_ids))
869
 
            try:
870
 
                rev = other.get_revision(rev_id)
871
 
            except bzrlib.errors.NoSuchRevision:
872
 
                failures.add(rev_id)
873
 
                continue
874
 
 
875
 
            revisions.append(rev)
876
 
            inv = other.get_inventory(str(rev.inventory_id))
877
 
            for key, entry in inv.iter_entries():
878
 
                if entry.text_id is None:
879
 
                    continue
880
 
                if entry.text_id not in self.text_store:
881
 
                    needed_texts.add(entry.text_id)
882
 
 
883
 
        pb.clear()
884
 
                    
885
 
        count, cp_fail = self.text_store.copy_multi(other.text_store, 
886
 
                                                    needed_texts)
887
 
        #print "Added %d texts." % count 
888
 
        inventory_ids = [ f.inventory_id for f in revisions ]
889
 
        count, cp_fail = self.inventory_store.copy_multi(other.inventory_store, 
890
 
                                                         inventory_ids)
891
 
        #print "Added %d inventories." % count 
892
 
        revision_ids = [ f.revision_id for f in revisions]
893
 
 
894
 
        count, cp_fail = self.revision_store.copy_multi(other.revision_store, 
895
 
                                                          revision_ids,
896
 
                                                          permit_failure=True)
897
 
        assert len(cp_fail) == 0 
898
 
        return count, failures
899
 
       
 
901
            stop_revision = other.last_revision()
 
902
        greedy_fetch(to_branch=self, from_branch=other,
 
903
                     revision=stop_revision)
 
904
        pullable_revs = self.missing_revisions(
 
905
            other, other.revision_id_to_revno(stop_revision))
 
906
        if pullable_revs:
 
907
            greedy_fetch(to_branch=self,
 
908
                         from_branch=other,
 
909
                         revision=pullable_revs[-1])
 
910
            self.append_revision(*pullable_revs)
 
911
    
900
912
 
901
913
    def commit(self, *args, **kw):
902
 
        from bzrlib.commit import commit
903
 
        commit(self, *args, **kw)
904
 
        
 
914
        from bzrlib.commit import Commit
 
915
        Commit().commit(self, *args, **kw)
 
916
    
905
917
    def revision_id_to_revno(self, revision_id):
906
918
        """Given a revision id, return its revno"""
 
919
        if revision_id is None:
 
920
            return 0
907
921
        history = self.revision_history()
908
922
        try:
909
923
            return history.index(revision_id) + 1
920
934
            raise bzrlib.errors.NoSuchRevision(self, revno)
921
935
        return history[revno - 1]
922
936
 
923
 
 
924
937
    def revision_tree(self, revision_id):
925
938
        """Return Tree for a revision on this branch.
926
939
 
932
945
            return EmptyTree()
933
946
        else:
934
947
            inv = self.get_revision_inventory(revision_id)
935
 
            return RevisionTree(self.text_store, inv)
 
948
            return RevisionTree(self.weave_store, inv, revision_id)
936
949
 
937
950
 
938
951
    def working_tree(self):
946
959
 
947
960
        If there are no revisions yet, return an `EmptyTree`.
948
961
        """
949
 
        r = self.last_patch()
950
 
        if r == None:
951
 
            return EmptyTree()
952
 
        else:
953
 
            return RevisionTree(self.text_store, self.get_revision_inventory(r))
954
 
 
 
962
        return self.revision_tree(self.last_revision())
955
963
 
956
964
 
957
965
    def rename_one(self, from_rel, to_rel):
992
1000
            from_abs = self.abspath(from_rel)
993
1001
            to_abs = self.abspath(to_rel)
994
1002
            try:
995
 
                os.rename(from_abs, to_abs)
 
1003
                rename(from_abs, to_abs)
996
1004
            except OSError, e:
997
1005
                raise BzrError("failed to rename %r to %r: %s"
998
1006
                        % (from_abs, to_abs, e[1]),
1061
1069
                result.append((f, dest_path))
1062
1070
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1063
1071
                try:
1064
 
                    os.rename(self.abspath(f), self.abspath(dest_path))
 
1072
                    rename(self.abspath(f), self.abspath(dest_path))
1065
1073
                except OSError, e:
1066
1074
                    raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1067
1075
                            ["rename rolled back"])
1133
1141
 
1134
1142
 
1135
1143
    def add_pending_merge(self, revision_id):
1136
 
        from bzrlib.revision import validate_revision_id
1137
 
 
1138
1144
        validate_revision_id(revision_id)
1139
 
 
 
1145
        # TODO: Perhaps should check at this point that the
 
1146
        # history of the revision is actually present?
1140
1147
        p = self.pending_merges()
1141
1148
        if revision_id in p:
1142
1149
            return
1246
1253
        """
1247
1254
        >>> orig = ScratchBranch(files=["file1", "file2"])
1248
1255
        >>> clone = orig.clone()
1249
 
        >>> os.path.samefile(orig.base, clone.base)
 
1256
        >>> if os.name != 'nt':
 
1257
        ...   os.path.samefile(orig.base, clone.base)
 
1258
        ... else:
 
1259
        ...   orig.base == clone.base
 
1260
        ...
1250
1261
        False
1251
1262
        >>> os.path.isfile(os.path.join(clone.base, "file1"))
1252
1263
        True
1335
1346
    return gen_file_id('TREE_ROOT')
1336
1347
 
1337
1348
 
1338
 
def copy_branch(branch_from, to_location, revno=None, basis_branch=None):
1339
 
    """Copy branch_from into the existing directory to_location.
1340
 
 
1341
 
    revision
1342
 
        If not None, only revisions up to this point will be copied.
1343
 
        The head of the new branch will be that revision.
1344
 
 
1345
 
    to_location
1346
 
        The name of a local directory that exists but is empty.
1347
 
 
1348
 
    revno
1349
 
        The revision to copy up to
1350
 
 
1351
 
    basis_branch
1352
 
        A local branch to copy revisions from, related to branch_from
1353
 
    """
1354
 
    from bzrlib.merge import merge
1355
 
 
1356
 
    assert isinstance(branch_from, Branch)
1357
 
    assert isinstance(to_location, basestring)
1358
 
    
1359
 
    br_to = Branch.initialize(to_location)
1360
 
    if basis_branch is not None:
1361
 
        copy_stores(basis_branch, br_to)
1362
 
    br_to.set_root_id(branch_from.get_root_id())
1363
 
    if revno is None:
1364
 
        revno = branch_from.revno()
1365
 
    br_to.update_revisions(branch_from, stop_revision=revno)
1366
 
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1367
 
          check_clean=False, ignore_zero=True)
1368
 
    br_to.set_parent(branch_from.base)
1369
 
    return br_to
1370
 
 
1371
 
def copy_stores(branch_from, branch_to):
1372
 
    """Copies all entries from branch stores to another branch's stores.
1373
 
    """
1374
 
    store_pairs = ((branch_from.text_store,      branch_to.text_store),
1375
 
                   (branch_from.inventory_store, branch_to.inventory_store),
1376
 
                   (branch_from.revision_store,  branch_to.revision_store))
1377
 
    try:
1378
 
        for from_store, to_store in store_pairs: 
1379
 
            copy_all(from_store, to_store)
1380
 
    except UnlistableStore:
1381
 
        raise UnlistableBranch(from_store)