~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Vincent Ladeuil
  • Date: 2011-06-15 11:36:05 UTC
  • mto: This revision was merged to the branch mainline in revision 5975.
  • Revision ID: v.ladeuil+lp@free.fr-20110615113605-p7zyyfry9wy1hquc
Make ContentConflict resolution more robust

Show diffs side-by-side

added added

removed removed

Lines of Context:
54
54
    generate_ids,
55
55
    globbing,
56
56
    graph as _mod_graph,
 
57
    hashcache,
57
58
    ignores,
58
59
    inventory,
59
60
    merge,
71
72
 
72
73
from bzrlib import symbol_versioning
73
74
from bzrlib.decorators import needs_read_lock, needs_write_lock
74
 
from bzrlib.i18n import gettext
75
75
from bzrlib.lock import LogicalLockResult
76
76
import bzrlib.mutabletree
77
77
from bzrlib.mutabletree import needs_tree_write_lock
194
194
        self.basedir = realpath(basedir)
195
195
        self._control_files = _control_files
196
196
        self._transport = self._control_files._transport
 
197
        # update the whole cache up front and write to disk if anything changed;
 
198
        # in the future we might want to do this more selectively
 
199
        # two possible ways offer themselves : in self._unlock, write the cache
 
200
        # if needed, or, when the cache sees a change, append it to the hash
 
201
        # cache file, and have the parser take the most recent entry for a
 
202
        # given path only.
 
203
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
204
        cache_filename = wt_trans.local_abspath('stat-cache')
 
205
        self._hashcache = hashcache.HashCache(basedir, cache_filename,
 
206
            self.bzrdir._get_file_mode(),
 
207
            self._content_filter_stack_provider())
 
208
        hc = self._hashcache
 
209
        hc.read()
 
210
        # is this scan needed ? it makes things kinda slow.
 
211
        #hc.scan()
 
212
 
 
213
        if hc.needs_write:
 
214
            mutter("write hc")
 
215
            hc.write()
 
216
 
 
217
        self._detect_case_handling()
197
218
        self._rules_searcher = None
198
219
        self.views = self._make_views()
199
220
 
217
238
        """
218
239
        return self.bzrdir.is_control_filename(filename)
219
240
 
 
241
    def _detect_case_handling(self):
 
242
        wt_trans = self.bzrdir.get_workingtree_transport(None)
 
243
        try:
 
244
            wt_trans.stat(self._format.case_sensitive_filename)
 
245
        except errors.NoSuchFile:
 
246
            self.case_sensitive = True
 
247
        else:
 
248
            self.case_sensitive = False
 
249
 
 
250
        self._setup_directory_is_tree_reference()
 
251
 
220
252
    branch = property(
221
253
        fget=lambda self: self._branch,
222
254
        doc="""The branch this WorkingTree is connected to.
225
257
            the working tree has been constructed from.
226
258
            """)
227
259
 
228
 
    def has_versioned_directories(self):
229
 
        """See `Tree.has_versioned_directories`."""
230
 
        return self._format.supports_versioned_directories
231
 
 
232
260
    def break_lock(self):
233
261
        """Break a lock if one is present from another instance.
234
262
 
259
287
        """
260
288
        if path is None:
261
289
            path = osutils.getcwd()
262
 
        control = controldir.ControlDir.open(path, _unsupported)
 
290
        control = bzrdir.BzrDir.open(path, _unsupported)
263
291
        return control.open_workingtree(_unsupported)
264
292
 
265
293
    @staticmethod
277
305
        """
278
306
        if path is None:
279
307
            path = osutils.getcwd()
280
 
        control, relpath = controldir.ControlDir.open_containing(path)
 
308
        control, relpath = bzrdir.BzrDir.open_containing(path)
281
309
        return control.open_workingtree(), relpath
282
310
 
283
311
    @staticmethod
303
331
                if view_files:
304
332
                    file_list = view_files
305
333
                    view_str = views.view_display_str(view_files)
306
 
                    note(gettext("Ignoring files outside view. View is %s") % view_str)
 
334
                    note("Ignoring files outside view. View is %s" % view_str)
307
335
            return tree, file_list
308
336
        if default_directory == u'.':
309
337
            seed = file_list[0]
366
394
            else:
367
395
                return True, tree
368
396
        t = transport.get_transport(location)
369
 
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
 
397
        iterator = bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate,
370
398
                                              list_current=list_current)
371
399
        return [tr for tr in iterator if tr is not None]
372
400
 
468
496
        finally:
469
497
            file.close()
470
498
 
 
499
    def _get_ancestors(self, default_revision):
 
500
        ancestors = set([default_revision])
 
501
        for parent_id in self.get_parent_ids():
 
502
            ancestors.update(self.branch.repository.get_ancestry(
 
503
                             parent_id, topo_sorted=False))
 
504
        return ancestors
 
505
 
471
506
    def get_parent_ids(self):
472
507
        """See Tree.get_parent_ids.
473
508
 
494
529
        raise NotImplementedError(self.get_root_id)
495
530
 
496
531
    @needs_read_lock
497
 
    def clone(self, to_controldir, revision_id=None):
 
532
    def clone(self, to_bzrdir, revision_id=None):
498
533
        """Duplicate this working tree into to_bzr, including all state.
499
534
 
500
535
        Specifically modified files are kept as modified, but
501
536
        ignored and unknown files are discarded.
502
537
 
503
 
        If you want to make a new line of development, see ControlDir.sprout()
 
538
        If you want to make a new line of development, see bzrdir.sprout()
504
539
 
505
540
        revision
506
541
            If not None, the cloned tree will have its last revision set to
508
543
            and this one merged in.
509
544
        """
510
545
        # assumes the target bzr dir format is compatible.
511
 
        result = to_controldir.create_workingtree()
 
546
        result = to_bzrdir.create_workingtree()
512
547
        self.copy_content_into(result, revision_id)
513
548
        return result
514
549
 
567
602
            else:
568
603
                return None
569
604
 
 
605
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
606
        # FIXME: Shouldn't this be in Tree?
 
607
        raise NotImplementedError(self.get_file_sha1)
 
608
 
570
609
    @needs_tree_write_lock
571
610
    def _gather_kinds(self, files, kinds):
572
611
        """See MutableTree._gather_kinds."""
934
973
        file and change the file_id. That is the normal mode. Second, it can
935
974
        only change the file_id without touching any physical file.
936
975
 
937
 
        rename_one uses the second mode if 'after == True' and 'to_rel' is
938
 
        either not versioned or newly added, and present in the working tree.
 
976
        rename_one uses the second mode if 'after == True' and 'to_rel' is not
 
977
        versioned but present in the working tree.
939
978
 
940
979
        rename_one uses the second mode if 'after == False' and 'from_rel' is
941
980
        versioned but no longer in the working tree, and 'to_rel' is not
1034
1073
            stream.write(bytes)
1035
1074
        finally:
1036
1075
            stream.close()
 
1076
        # TODO: update the hashcache here ?
1037
1077
 
1038
1078
    def extras(self):
1039
1079
        """Yield all unversioned files in this WorkingTree.
1499
1539
                                             show_base=show_base)
1500
1540
            if nb_conflicts:
1501
1541
                self.add_parent_tree((old_tip, other_tree))
1502
 
                note(gettext('Rerun update after fixing the conflicts.'))
 
1542
                note('Rerun update after fixing the conflicts.')
1503
1543
                return nb_conflicts
1504
1544
 
1505
1545
        if last_rev != _mod_revision.ensure_null(revision):
1547
1587
            last_rev = parent_trees[0][0]
1548
1588
        return nb_conflicts
1549
1589
 
 
1590
    def _write_hashcache_if_dirty(self):
 
1591
        """Write out the hashcache if it is dirty."""
 
1592
        if self._hashcache.needs_write:
 
1593
            try:
 
1594
                self._hashcache.write()
 
1595
            except OSError, e:
 
1596
                if e.errno not in (errno.EPERM, errno.EACCES):
 
1597
                    raise
 
1598
                # TODO: jam 20061219 Should this be a warning? A single line
 
1599
                #       warning might be sufficient to let the user know what
 
1600
                #       is going on.
 
1601
                mutter('Could not write hashcache for %s\nError: %s',
 
1602
                              self._hashcache.cache_file_name(), e)
 
1603
 
1550
1604
    def set_conflicts(self, arg):
1551
1605
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1552
1606
 
1784
1838
            branch=branch, _control_files=_control_files, _internal=_internal,
1785
1839
            _format=_format, _bzrdir=_bzrdir)
1786
1840
 
1787
 
        self._detect_case_handling()
1788
 
 
1789
1841
        if _inventory is None:
1790
1842
            # This will be acquired on lock_read() or lock_write()
1791
1843
            self._inventory_is_modified = False
1810
1862
        self._inventory = inv
1811
1863
        self._inventory_is_modified = dirty
1812
1864
 
1813
 
    def _detect_case_handling(self):
1814
 
        wt_trans = self.bzrdir.get_workingtree_transport(None)
1815
 
        try:
1816
 
            wt_trans.stat(self._format.case_sensitive_filename)
1817
 
        except errors.NoSuchFile:
1818
 
            self.case_sensitive = True
1819
 
        else:
1820
 
            self.case_sensitive = False
1821
 
 
1822
 
        self._setup_directory_is_tree_reference()
1823
 
 
1824
1865
    def _serialize(self, inventory, out_file):
1825
1866
        xml5.serializer_v5.write_inventory(self._inventory, out_file,
1826
1867
            working=True)
2043
2084
            return True
2044
2085
        return self.inventory.has_id(file_id)
2045
2086
 
 
2087
    __contains__ = has_id
 
2088
 
2046
2089
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2047
2090
    def __iter__(self):
2048
2091
        """Iterate through file_ids for this tree.
2130
2173
            mode=self.bzrdir._get_file_mode())
2131
2174
        self._inventory_is_modified = False
2132
2175
 
 
2176
    @needs_read_lock
 
2177
    def get_file_sha1(self, file_id, path=None, stat_value=None):
 
2178
        if not path:
 
2179
            path = self._inventory.id2path(file_id)
 
2180
        return self._hashcache.get_sha1(path, stat_value)
 
2181
 
2133
2182
    def get_file_mtime(self, file_id, path=None):
2134
2183
        """See Tree.get_file_mtime."""
2135
2184
        if not path:
2136
2185
            path = self.inventory.id2path(file_id)
2137
 
        try:
2138
 
            return os.lstat(self.abspath(path)).st_mtime
2139
 
        except OSError, e:
2140
 
            if e.errno == errno.ENOENT:
2141
 
                raise errors.FileTimestampUnavailable(path)
2142
 
            raise
 
2186
        return os.lstat(self.abspath(path)).st_mtime
2143
2187
 
2144
2188
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
2145
2189
        file_id = self.path2id(path)
2223
2267
                parent_tree = self.branch.repository.revision_tree(parent_id)
2224
2268
            parent_tree.lock_read()
2225
2269
            try:
2226
 
                if not parent_tree.has_id(file_id):
 
2270
                if file_id not in parent_tree:
2227
2271
                    continue
2228
2272
                ie = parent_tree.inventory[file_id]
2229
2273
                if ie.kind != 'file':
2277
2321
            for s in _mod_rio.RioReader(hashfile):
2278
2322
                # RioReader reads in Unicode, so convert file_ids back to utf8
2279
2323
                file_id = osutils.safe_file_id(s.get("file_id"), warn=False)
2280
 
                if not self.inventory.has_id(file_id):
 
2324
                if file_id not in self.inventory:
2281
2325
                    continue
2282
2326
                text_hash = s.get("hash")
2283
2327
                if text_hash == self.get_file_sha1(file_id):
2524
2568
        inventory. The second mode only updates the inventory without
2525
2569
        touching the file on the filesystem.
2526
2570
 
2527
 
        move uses the second mode if 'after == True' and the target is
2528
 
        either not versioned or newly added, and present in the working tree.
 
2571
        move uses the second mode if 'after == True' and the target is not
 
2572
        versioned but present in the working tree.
2529
2573
 
2530
2574
        move uses the second mode if 'after == False' and the source is
2531
2575
        versioned but no longer in the working tree, and the target is not
2678
2722
 
2679
2723
    class _RenameEntry(object):
2680
2724
        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
2681
 
                     to_rel, to_tail, to_parent_id, only_change_inv=False,
2682
 
                     change_id=False):
 
2725
                     to_rel, to_tail, to_parent_id, only_change_inv=False):
2683
2726
            self.from_rel = from_rel
2684
2727
            self.from_id = from_id
2685
2728
            self.from_tail = from_tail
2687
2730
            self.to_rel = to_rel
2688
2731
            self.to_tail = to_tail
2689
2732
            self.to_parent_id = to_parent_id
2690
 
            self.change_id = change_id
2691
2733
            self.only_change_inv = only_change_inv
2692
2734
 
2693
2735
    def _determine_mv_mode(self, rename_entries, after=False):
2705
2747
            to_rel = rename_entry.to_rel
2706
2748
            to_id = inv.path2id(to_rel)
2707
2749
            only_change_inv = False
2708
 
            change_id = False
2709
2750
 
2710
2751
            # check the inventory for source and destination
2711
2752
            if from_id is None:
2712
2753
                raise errors.BzrMoveFailedError(from_rel,to_rel,
2713
2754
                    errors.NotVersionedError(path=from_rel))
2714
2755
            if to_id is not None:
2715
 
                allowed = False
2716
 
                # allow it with --after but only if dest is newly added
2717
 
                if after:
2718
 
                    basis = self.basis_tree()
2719
 
                    basis.lock_read()
2720
 
                    try:
2721
 
                        if not basis.has_id(to_id):
2722
 
                            rename_entry.change_id = True
2723
 
                            allowed = True
2724
 
                    finally:
2725
 
                        basis.unlock()
2726
 
                if not allowed:
2727
 
                    raise errors.BzrMoveFailedError(from_rel,to_rel,
2728
 
                        errors.AlreadyVersionedError(path=to_rel))
 
2756
                raise errors.BzrMoveFailedError(from_rel,to_rel,
 
2757
                    errors.AlreadyVersionedError(path=to_rel))
2729
2758
 
2730
2759
            # try to determine the mode for rename (only change inv or change
2731
2760
            # inv and file system)
2802
2831
            except OSError, e:
2803
2832
                raise errors.BzrMoveFailedError(entry.from_rel,
2804
2833
                    entry.to_rel, e[1])
2805
 
        if entry.change_id:
2806
 
            to_id = inv.path2id(entry.to_rel)
2807
 
            inv.remove_recursive_id(to_id)
2808
2834
        inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
2809
2835
 
2810
2836
    @needs_tree_write_lock
2818
2844
        :raises: NoSuchId if any fileid is not currently versioned.
2819
2845
        """
2820
2846
        for file_id in file_ids:
2821
 
            if not self._inventory.has_id(file_id):
 
2847
            if file_id not in self._inventory:
2822
2848
                raise errors.NoSuchId(self, file_id)
2823
2849
        for file_id in file_ids:
2824
2850
            if self._inventory.has_id(file_id):
2979
3005
    missing_parent_conflicts = False
2980
3006
    """If this format supports missing parent conflicts."""
2981
3007
 
2982
 
    supports_versioned_directories = None
2983
 
 
2984
3008
    @classmethod
2985
 
    def find_format_string(klass, controldir):
2986
 
        """Return format name for the working tree object in controldir."""
 
3009
    def find_format_string(klass, a_bzrdir):
 
3010
        """Return format name for the working tree object in a_bzrdir."""
2987
3011
        try:
2988
 
            transport = controldir.get_workingtree_transport(None)
 
3012
            transport = a_bzrdir.get_workingtree_transport(None)
2989
3013
            return transport.get_bytes("format")
2990
3014
        except errors.NoSuchFile:
2991
3015
            raise errors.NoWorkingTree(base=transport.base)
2992
3016
 
2993
3017
    @classmethod
2994
 
    def find_format(klass, controldir):
2995
 
        """Return the format for the working tree object in controldir."""
 
3018
    def find_format(klass, a_bzrdir):
 
3019
        """Return the format for the working tree object in a_bzrdir."""
2996
3020
        try:
2997
 
            format_string = klass.find_format_string(controldir)
 
3021
            format_string = klass.find_format_string(a_bzrdir)
2998
3022
            return format_registry.get(format_string)
2999
3023
        except KeyError:
3000
3024
            raise errors.UnknownFormatError(format=format_string,
3001
3025
                                            kind="working tree")
3002
3026
 
3003
 
    def initialize(self, controldir, revision_id=None, from_branch=None,
 
3027
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
3004
3028
                   accelerator_tree=None, hardlink=False):
3005
 
        """Initialize a new working tree in controldir.
 
3029
        """Initialize a new working tree in a_bzrdir.
3006
3030
 
3007
 
        :param controldir: ControlDir to initialize the working tree in.
 
3031
        :param a_bzrdir: BzrDir to initialize the working tree in.
3008
3032
        :param revision_id: allows creating a working tree at a different
3009
3033
            revision than the branch is at.
3010
3034
        :param from_branch: Branch to checkout
3091
3115
    def unregister_format(klass, format):
3092
3116
        format_registry.remove(format)
3093
3117
 
3094
 
    def get_controldir_for_branch(self):
3095
 
        """Get the control directory format for creating branches.
3096
 
 
3097
 
        This is to support testing of working tree formats that can not exist
3098
 
        in the same control directory as a branch.
3099
 
        """
3100
 
        return self._matchingbzrdir
3101
 
 
3102
3118
 
3103
3119
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
3104
3120
    "bzrlib.workingtree_4", "WorkingTreeFormat4")