~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-11 21:41:24 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080711214124-qi09irlj7pd5cuzg
Shortcut the case when one revision is in the ancestry of the other.

At the cost of a heads() check, when one parent supersedes, we don't have to extract
the text for the other. Changes merge time from 3m37s => 3m21s. Using a
CachingParentsProvider would drop the time down to 3m11s.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
79
79
import bzrlib.branch
80
80
from bzrlib.transport import get_transport
81
81
import bzrlib.ui
82
 
from bzrlib.workingtree_4 import WorkingTreeFormat4, WorkingTreeFormat5
 
82
from bzrlib.workingtree_4 import WorkingTreeFormat4
83
83
""")
84
84
 
85
85
from bzrlib import symbol_versioning
86
86
from bzrlib.decorators import needs_read_lock, needs_write_lock
87
87
from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID, TreeReference
88
 
from bzrlib.lockable_files import LockableFiles
 
88
from bzrlib.lockable_files import LockableFiles, TransportLock
89
89
from bzrlib.lockdir import LockDir
90
90
import bzrlib.mutabletree
91
91
from bzrlib.mutabletree import needs_tree_write_lock
246
246
            # permitted to do this.
247
247
            self._set_inventory(_inventory, dirty=False)
248
248
        self._detect_case_handling()
249
 
        self._rules_searcher = None
250
249
 
251
250
    def _detect_case_handling(self):
252
251
        wt_trans = self.bzrdir.get_workingtree_transport(None)
284
283
    def supports_tree_reference(self):
285
284
        return False
286
285
 
287
 
    def supports_content_filtering(self):
288
 
        return self._format.supports_content_filtering()
289
 
 
290
 
    def supports_views(self):
291
 
        return self._format.supports_views()
292
 
 
293
286
    def _set_inventory(self, inv, dirty):
294
287
        """Set the internal cached inventory.
295
288
 
309
302
 
310
303
        """
311
304
        if path is None:
312
 
            path = osutils.getcwd()
 
305
            path = os.path.getcwdu()
313
306
        control = bzrdir.BzrDir.open(path, _unsupported)
314
307
        return control.open_workingtree(_unsupported)
315
 
 
 
308
        
316
309
    @staticmethod
317
310
    def open_containing(path=None):
318
311
        """Open an existing working tree which has its root about path.
319
 
 
 
312
        
320
313
        This probes for a working tree at path and searches upwards from there.
321
314
 
322
315
        Basically we keep looking up until we find the control directory or
385
378
        """Return RevisionTree for the current last revision.
386
379
        
387
380
        If the left most parent is a ghost then the returned tree will be an
388
 
        empty tree - one obtained by calling 
389
 
        repository.revision_tree(NULL_REVISION).
 
381
        empty tree - one obtained by calling repository.revision_tree(None).
390
382
        """
391
383
        try:
392
384
            revision_id = self.get_parent_ids()[0]
394
386
            # no parents, return an empty revision tree.
395
387
            # in the future this should return the tree for
396
388
            # 'empty:' - the implicit root empty tree.
397
 
            return self.branch.repository.revision_tree(
398
 
                       _mod_revision.NULL_REVISION)
 
389
            return self.branch.repository.revision_tree(None)
399
390
        try:
400
391
            return self.revision_tree(revision_id)
401
392
        except errors.NoSuchRevision:
412
403
            if self.branch.repository.has_revision(revision_id):
413
404
                raise
414
405
            # the basis tree is a ghost so return an empty tree.
415
 
            return self.branch.repository.revision_tree(
416
 
                       _mod_revision.NULL_REVISION)
 
406
            return self.branch.repository.revision_tree(None)
417
407
 
418
408
    def _cleanup(self):
419
409
        self._flush_ignore_list_cache()
430
420
        return osutils.lexists(self.abspath(filename))
431
421
 
432
422
    def get_file(self, file_id, path=None):
433
 
        return self.get_file_with_stat(file_id, path)[0]
434
 
 
435
 
    def get_file_with_stat(self, file_id, path=None, _fstat=os.fstat):
436
 
        """See MutableTree.get_file_with_stat."""
437
423
        if path is None:
438
424
            path = self.id2path(file_id)
439
 
        file_obj = self.get_file_byname(path)
440
 
        return (file_obj, _fstat(file_obj.fileno()))
 
425
        return self.get_file_byname(path)
 
426
 
 
427
    def get_file_text(self, file_id):
 
428
        return self.get_file(file_id).read()
441
429
 
442
430
    def get_file_byname(self, filename):
443
431
        return file(self.abspath(filename), 'rb')
444
432
 
445
 
    def get_file_lines(self, file_id, path=None):
446
 
        """See Tree.get_file_lines()"""
447
 
        file = self.get_file(file_id, path)
448
 
        try:
449
 
            return file.readlines()
450
 
        finally:
451
 
            file.close()
452
 
 
453
433
    @needs_read_lock
454
434
    def annotate_iter(self, file_id, default_revision=CURRENT_REVISION):
455
435
        """See Tree.annotate_iter
539
519
            and this one merged in.
540
520
        """
541
521
        # assumes the target bzr dir format is compatible.
542
 
        result = to_bzrdir.create_workingtree()
 
522
        result = self._format.initialize(to_bzrdir)
543
523
        self.copy_content_into(result, revision_id)
544
524
        return result
545
525
 
723
703
                kind = 'tree-reference'
724
704
            return kind, None, None, None
725
705
        elif kind == 'symlink':
726
 
            return ('symlink', None, None, os.readlink(abspath.encode(osutils._fs_enc)))
 
706
            return ('symlink', None, None, os.readlink(abspath))
727
707
        else:
728
708
            return (kind, None, None, None)
729
709
 
916
896
            hashfile = self._transport.get('merge-hashes')
917
897
        except errors.NoSuchFile:
918
898
            return {}
 
899
        merge_hashes = {}
919
900
        try:
920
 
            merge_hashes = {}
921
 
            try:
922
 
                if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
923
 
                    raise errors.MergeModifiedFormatError()
924
 
            except StopIteration:
 
901
            if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
925
902
                raise errors.MergeModifiedFormatError()
926
 
            for s in RioReader(hashfile):
927
 
                # RioReader reads in Unicode, so convert file_ids back to utf8
928
 
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
929
 
                if file_id not in self.inventory:
930
 
                    continue
931
 
                text_hash = s.get("hash")
932
 
                if text_hash == self.get_file_sha1(file_id):
933
 
                    merge_hashes[file_id] = text_hash
934
 
            return merge_hashes
935
 
        finally:
936
 
            hashfile.close()
 
903
        except StopIteration:
 
904
            raise errors.MergeModifiedFormatError()
 
905
        for s in RioReader(hashfile):
 
906
            # RioReader reads in Unicode, so convert file_ids back to utf8
 
907
            file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
 
908
            if file_id not in self.inventory:
 
909
                continue
 
910
            text_hash = s.get("hash")
 
911
            if text_hash == self.get_file_sha1(file_id):
 
912
                merge_hashes[file_id] = text_hash
 
913
        return merge_hashes
937
914
 
938
915
    @needs_write_lock
939
916
    def mkdir(self, path, file_id=None):
945
922
        return file_id
946
923
 
947
924
    def get_symlink_target(self, file_id):
948
 
        return os.readlink(self.id2abspath(file_id).encode(osutils._fs_enc))
 
925
        return os.readlink(self.id2abspath(file_id))
949
926
 
950
927
    @needs_write_lock
951
928
    def subsume(self, other_tree):
1338
1315
                only_change_inv = True
1339
1316
            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1340
1317
                only_change_inv = False
1341
 
            elif (not self.case_sensitive
1342
 
                  and from_rel.lower() == to_rel.lower()
1343
 
                  and self.has_filename(from_rel)):
 
1318
            elif (sys.platform == 'win32'
 
1319
                and from_rel.lower() == to_rel.lower()
 
1320
                and self.has_filename(from_rel)):
1344
1321
                only_change_inv = False
1345
1322
            else:
1346
1323
                # something is wrong, so lets determine what exactly
1614
1591
                if subf == '.bzr':
1615
1592
                    continue
1616
1593
                if subf not in dir_entry.children:
1617
 
                    try:
1618
 
                        (subf_norm,
1619
 
                         can_access) = osutils.normalized_filename(subf)
1620
 
                    except UnicodeDecodeError:
1621
 
                        path_os_enc = path.encode(osutils._fs_enc)
1622
 
                        relpath = path_os_enc + '/' + subf
1623
 
                        raise errors.BadFilenameEncoding(relpath,
1624
 
                                                         osutils._fs_enc)
 
1594
                    subf_norm, can_access = osutils.normalized_filename(subf)
1625
1595
                    if subf_norm != subf and can_access:
1626
1596
                        if subf_norm not in dir_entry.children:
1627
1597
                            fl.append(subf_norm)
1881
1851
            # Recurse directory and add all files
1882
1852
            # so we can check if they have changed.
1883
1853
            for parent_info, file_infos in\
1884
 
                self.walkdirs(directory):
1885
 
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1854
                osutils.walkdirs(self.abspath(directory),
 
1855
                    directory):
 
1856
                for relpath, basename, kind, lstat, abspath in file_infos:
1886
1857
                    # Is it versioned or ignored?
1887
1858
                    if self.path2id(relpath) or self.is_ignored(relpath):
1888
1859
                        # Add nested content for deletion.
1898
1869
            filename = self.relpath(abspath)
1899
1870
            if len(filename) > 0:
1900
1871
                new_files.add(filename)
1901
 
                recurse_directory_to_add_files(filename)
 
1872
                if osutils.isdir(abspath):
 
1873
                    recurse_directory_to_add_files(filename)
1902
1874
 
1903
1875
        files = list(new_files)
1904
1876
 
2342
2314
                    # value.
2343
2315
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
2344
2316
                        ('.bzr', '.bzr'))
2345
 
                    if (bzrdir_loc < len(cur_disk_dir_content)
2346
 
                        and cur_disk_dir_content[bzrdir_loc][0] == '.bzr'):
 
2317
                    if cur_disk_dir_content[bzrdir_loc][0] == '.bzr':
2347
2318
                        # we dont yield the contents of, or, .bzr itself.
2348
2319
                        del cur_disk_dir_content[bzrdir_loc]
2349
2320
            if inv_finished:
2439
2410
                relroot = ""
2440
2411
            # FIXME: stash the node in pending
2441
2412
            entry = inv[top_id]
2442
 
            if entry.kind == 'directory':
2443
 
                for name, child in entry.sorted_children():
2444
 
                    dirblock.append((relroot + name, name, child.kind, None,
2445
 
                        child.file_id, child.kind
2446
 
                        ))
 
2413
            for name, child in entry.sorted_children():
 
2414
                dirblock.append((relroot + name, name, child.kind, None,
 
2415
                    child.file_id, child.kind
 
2416
                    ))
2447
2417
            yield (currentdir[0], entry.file_id), dirblock
2448
2418
            # push the user specified dirs from dirblock
2449
2419
            for dir in reversed(dirblock):
2482
2452
        self.set_conflicts(un_resolved)
2483
2453
        return un_resolved, resolved
2484
2454
 
2485
 
    @needs_read_lock
2486
 
    def _check(self):
2487
 
        tree_basis = self.basis_tree()
2488
 
        tree_basis.lock_read()
2489
 
        try:
2490
 
            repo_basis = self.branch.repository.revision_tree(
2491
 
                self.last_revision())
2492
 
            if len(list(repo_basis.iter_changes(tree_basis))) > 0:
2493
 
                raise errors.BzrCheckError(
2494
 
                    "Mismatched basis inventory content.")
2495
 
            self._validate()
2496
 
        finally:
2497
 
            tree_basis.unlock()
2498
 
 
2499
2455
    def _validate(self):
2500
2456
        """Validate internal structures.
2501
2457
 
2507
2463
        """
2508
2464
        return
2509
2465
 
2510
 
    @needs_read_lock
2511
 
    def _get_rules_searcher(self, default_searcher):
2512
 
        """See Tree._get_rules_searcher."""
2513
 
        if self._rules_searcher is None:
2514
 
            self._rules_searcher = super(WorkingTree,
2515
 
                self)._get_rules_searcher(default_searcher)
2516
 
        return self._rules_searcher
2517
 
 
2518
 
    def get_shelf_manager(self):
2519
 
        """Return the ShelfManager for this WorkingTree."""
2520
 
        from bzrlib.shelf import ShelfManager
2521
 
        return ShelfManager(self, self._transport)
2522
 
 
2523
2466
 
2524
2467
class WorkingTree2(WorkingTree):
2525
2468
    """This is the Format 2 working tree.
2621
2564
        except errors.NoSuchFile:
2622
2565
            return _mod_conflicts.ConflictList()
2623
2566
        try:
2624
 
            try:
2625
 
                if confile.next() != CONFLICT_HEADER_1 + '\n':
2626
 
                    raise errors.ConflictFormatError()
2627
 
            except StopIteration:
 
2567
            if confile.next() != CONFLICT_HEADER_1 + '\n':
2628
2568
                raise errors.ConflictFormatError()
2629
 
            return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2630
 
        finally:
2631
 
            confile.close()
 
2569
        except StopIteration:
 
2570
            raise errors.ConflictFormatError()
 
2571
        return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
2632
2572
 
2633
2573
    def unlock(self):
2634
2574
        # do non-implementation specific cleanup
2720
2660
        """
2721
2661
        return True
2722
2662
 
2723
 
    def supports_content_filtering(self):
2724
 
        """True if this format supports content filtering."""
2725
 
        return False
2726
 
 
2727
 
    def supports_views(self):
2728
 
        """True if this format supports stored views."""
2729
 
        return False
2730
 
 
2731
2663
    @classmethod
2732
2664
    def register_format(klass, format):
2733
2665
        klass._formats[format.get_format_string()] = format
2753
2685
        """See WorkingTreeFormat.get_format_description()."""
2754
2686
        return "Working tree format 2"
2755
2687
 
2756
 
    def _stub_initialize_on_transport(self, transport, file_mode):
2757
 
        """Workaround: create control files for a remote working tree.
2758
 
 
 
2688
    def _stub_initialize_remote(self, branch):
 
2689
        """As a special workaround create critical control files for a remote working tree.
 
2690
        
2759
2691
        This ensures that it can later be updated and dealt with locally,
2760
 
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with
 
2692
        since BzrDirFormat6 and BzrDirFormat5 cannot represent dirs with 
2761
2693
        no working tree.  (See bug #43064).
2762
2694
        """
2763
2695
        sio = StringIO()
2764
2696
        inv = Inventory()
2765
2697
        xml5.serializer_v5.write_inventory(inv, sio, working=True)
2766
2698
        sio.seek(0)
2767
 
        transport.put_file('inventory', sio, file_mode)
2768
 
        transport.put_bytes('pending-merges', '', file_mode)
 
2699
        branch._transport.put_file('inventory', sio,
 
2700
            mode=branch.control_files._file_mode)
 
2701
        branch._transport.put_bytes('pending-merges', '',
 
2702
            mode=branch.control_files._file_mode)
 
2703
        
2769
2704
 
2770
2705
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
2771
2706
                   accelerator_tree=None, hardlink=False):
2956
2891
 
2957
2892
__default_format = WorkingTreeFormat4()
2958
2893
WorkingTreeFormat.register_format(__default_format)
2959
 
WorkingTreeFormat.register_format(WorkingTreeFormat5())
2960
2894
WorkingTreeFormat.register_format(WorkingTreeFormat3())
2961
2895
WorkingTreeFormat.set_default_format(__default_format)
2962
2896
# formats which have no format string are not discoverable