~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree.py

  • Committer: Andrew Bennetts
  • Date: 2010-07-29 04:07:27 UTC
  • mto: (5050.3.16 2.2)
  • mto: This revision was merged to the branch mainline in revision 5365.
  • Revision ID: andrew.bennetts@canonical.com-20100729040727-amlyw1jt3xhw17ku
Delete test_merge_into, and put those tests directly in test_merge.

Show diffs side-by-side

added added

removed removed

Lines of Context:
49
49
    branch,
50
50
    bzrdir,
51
51
    conflicts as _mod_conflicts,
52
 
    controldir,
53
52
    errors,
54
53
    generate_ids,
55
54
    globbing,
169
168
 
170
169
 
171
170
class WorkingTree(bzrlib.mutabletree.MutableTree,
172
 
    controldir.ControlComponent):
 
171
    bzrdir.ControlComponent):
173
172
    """Working copy tree.
174
173
 
175
174
    The inventory is held in the `Branch` working-inventory, and the
350
349
        if path is None:
351
350
            path = osutils.getcwd()
352
351
        control, relpath = bzrdir.BzrDir.open_containing(path)
 
352
 
353
353
        return control.open_workingtree(), relpath
354
354
 
355
355
    @staticmethod
356
 
    def open_containing_paths(file_list, default_directory=None,
357
 
                              canonicalize=True, apply_view=True):
358
 
        """Open the WorkingTree that contains a set of paths.
359
 
 
360
 
        Fail if the paths given are not all in a single tree.
361
 
 
362
 
        This is used for the many command-line interfaces that take a list of
363
 
        any number of files and that require they all be in the same tree.
364
 
        """
365
 
        if default_directory is None:
366
 
            default_directory = u'.'
367
 
        # recommended replacement for builtins.internal_tree_files
368
 
        if file_list is None or len(file_list) == 0:
369
 
            tree = WorkingTree.open_containing(default_directory)[0]
370
 
            # XXX: doesn't really belong here, and seems to have the strange
371
 
            # side effect of making it return a bunch of files, not the whole
372
 
            # tree -- mbp 20100716
373
 
            if tree.supports_views() and apply_view:
374
 
                view_files = tree.views.lookup_view()
375
 
                if view_files:
376
 
                    file_list = view_files
377
 
                    view_str = views.view_display_str(view_files)
378
 
                    note("Ignoring files outside view. View is %s" % view_str)
379
 
            return tree, file_list
380
 
        if default_directory == u'.':
381
 
            seed = file_list[0]
382
 
        else:
383
 
            seed = default_directory
384
 
            file_list = [osutils.pathjoin(default_directory, f)
385
 
                         for f in file_list]
386
 
        tree = WorkingTree.open_containing(seed)[0]
387
 
        return tree, tree.safe_relpath_files(file_list, canonicalize,
388
 
                                             apply_view=apply_view)
389
 
 
390
 
    def safe_relpath_files(self, file_list, canonicalize=True, apply_view=True):
391
 
        """Convert file_list into a list of relpaths in tree.
392
 
 
393
 
        :param self: A tree to operate on.
394
 
        :param file_list: A list of user provided paths or None.
395
 
        :param apply_view: if True and a view is set, apply it or check that
396
 
            specified files are within it
397
 
        :return: A list of relative paths.
398
 
        :raises errors.PathNotChild: When a provided path is in a different self
399
 
            than self.
400
 
        """
401
 
        if file_list is None:
402
 
            return None
403
 
        if self.supports_views() and apply_view:
404
 
            view_files = self.views.lookup_view()
405
 
        else:
406
 
            view_files = []
407
 
        new_list = []
408
 
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
409
 
        # doesn't - fix that up here before we enter the loop.
410
 
        if canonicalize:
411
 
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
412
 
        else:
413
 
            fixer = self.relpath
414
 
        for filename in file_list:
415
 
            relpath = fixer(osutils.dereference_path(filename))
416
 
            if view_files and not osutils.is_inside_any(view_files, relpath):
417
 
                raise errors.FileOutsideView(filename, view_files)
418
 
            new_list.append(relpath)
419
 
        return new_list
420
 
 
421
 
    @staticmethod
422
356
    def open_downlevel(path=None):
423
357
        """Open an unsupported working tree.
424
358
 
1275
1209
                # absolute path
1276
1210
                fap = from_dir_abspath + '/' + f
1277
1211
 
1278
 
                dir_ie = inv[from_dir_id]
1279
 
                if dir_ie.kind == 'directory':
1280
 
                    f_ie = dir_ie.children.get(f)
1281
 
                else:
1282
 
                    f_ie = None
 
1212
                f_ie = inv.get_child(from_dir_id, f)
1283
1213
                if f_ie:
1284
1214
                    c = 'V'
1285
1215
                elif self.is_ignored(fp[1:]):
1286
1216
                    c = 'I'
1287
1217
                else:
1288
 
                    # we may not have found this file, because of a unicode
1289
 
                    # issue, or because the directory was actually a symlink.
 
1218
                    # we may not have found this file, because of a unicode issue
1290
1219
                    f_norm, can_access = osutils.normalized_filename(f)
1291
1220
                    if f == f_norm or not can_access:
1292
1221
                        # No change, so treat this file normally
1335
1264
                stack.pop()
1336
1265
 
1337
1266
    @needs_tree_write_lock
1338
 
    def move(self, from_paths, to_dir=None, after=False):
 
1267
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
1339
1268
        """Rename files.
1340
1269
 
1341
1270
        to_dir must exist in the inventory.
1375
1304
 
1376
1305
        # check for deprecated use of signature
1377
1306
        if to_dir is None:
1378
 
            raise TypeError('You must supply a target directory')
 
1307
            to_dir = kwargs.get('to_name', None)
 
1308
            if to_dir is None:
 
1309
                raise TypeError('You must supply a target directory')
 
1310
            else:
 
1311
                symbol_versioning.warn('The parameter to_name was deprecated'
 
1312
                                       ' in version 0.13. Use to_dir instead',
 
1313
                                       DeprecationWarning)
 
1314
 
1379
1315
        # check destination directory
1380
1316
        if isinstance(from_paths, basestring):
1381
1317
            raise ValueError()
1671
1607
 
1672
1608
    @needs_write_lock
1673
1609
    def pull(self, source, overwrite=False, stop_revision=None,
1674
 
             change_reporter=None, possible_transports=None, local=False,
1675
 
             show_base=False):
 
1610
             change_reporter=None, possible_transports=None, local=False):
1676
1611
        source.lock_read()
1677
1612
        try:
1678
1613
            old_revision_info = self.branch.last_revision_info()
1692
1627
                                basis_tree,
1693
1628
                                this_tree=self,
1694
1629
                                pb=None,
1695
 
                                change_reporter=change_reporter,
1696
 
                                show_base=show_base)
 
1630
                                change_reporter=change_reporter)
1697
1631
                    basis_root_id = basis_tree.get_root_id()
1698
1632
                    new_root_id = new_basis_tree.get_root_id()
1699
1633
                    if basis_root_id != new_root_id:
2035
1969
 
2036
1970
        inv_delta = []
2037
1971
 
2038
 
        all_files = set() # specified and nested files 
 
1972
        new_files=set()
2039
1973
        unknown_nested_files=set()
2040
1974
        if to_file is None:
2041
1975
            to_file = sys.stdout
2042
1976
 
2043
 
        files_to_backup = []
2044
 
 
2045
1977
        def recurse_directory_to_add_files(directory):
2046
1978
            # Recurse directory and add all files
2047
1979
            # so we can check if they have changed.
2048
1980
            for parent_info, file_infos in self.walkdirs(directory):
2049
1981
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
2050
1982
                    # Is it versioned or ignored?
2051
 
                    if self.path2id(relpath):
 
1983
                    if self.path2id(relpath) or self.is_ignored(relpath):
2052
1984
                        # Add nested content for deletion.
2053
 
                        all_files.add(relpath)
 
1985
                        new_files.add(relpath)
2054
1986
                    else:
2055
 
                        # Files which are not versioned
 
1987
                        # Files which are not versioned and not ignored
2056
1988
                        # should be treated as unknown.
2057
 
                        files_to_backup.append(relpath)
 
1989
                        unknown_nested_files.add((relpath, None, kind))
2058
1990
 
2059
1991
        for filename in files:
2060
1992
            # Get file name into canonical form.
2061
1993
            abspath = self.abspath(filename)
2062
1994
            filename = self.relpath(abspath)
2063
1995
            if len(filename) > 0:
2064
 
                all_files.add(filename)
 
1996
                new_files.add(filename)
2065
1997
                recurse_directory_to_add_files(filename)
2066
1998
 
2067
 
        files = list(all_files)
 
1999
        files = list(new_files)
2068
2000
 
2069
2001
        if len(files) == 0:
2070
2002
            return # nothing to do
2074
2006
 
2075
2007
        # Bail out if we are going to delete files we shouldn't
2076
2008
        if not keep_files and not force:
2077
 
            for (file_id, path, content_change, versioned, parent_id, name,
2078
 
                 kind, executable) in self.iter_changes(self.basis_tree(),
2079
 
                     include_unchanged=True, require_versioned=False,
2080
 
                     want_unversioned=True, specific_files=files):
2081
 
                if versioned[0] == False:
2082
 
                    # The record is unknown or newly added
2083
 
                    files_to_backup.append(path[1])
2084
 
                elif (content_change and (kind[1] is not None) and
2085
 
                        osutils.is_inside_any(files, path[1])):
2086
 
                    # Versioned and changed, but not deleted, and still
2087
 
                    # in one of the dirs to be deleted.
2088
 
                    files_to_backup.append(path[1])
 
2009
            has_changed_files = len(unknown_nested_files) > 0
 
2010
            if not has_changed_files:
 
2011
                for (file_id, path, content_change, versioned, parent_id, name,
 
2012
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
2013
                         include_unchanged=True, require_versioned=False,
 
2014
                         want_unversioned=True, specific_files=files):
 
2015
                    if versioned == (False, False):
 
2016
                        # The record is unknown ...
 
2017
                        if not self.is_ignored(path[1]):
 
2018
                            # ... but not ignored
 
2019
                            has_changed_files = True
 
2020
                            break
 
2021
                    elif (content_change and (kind[1] is not None) and
 
2022
                            osutils.is_inside_any(files, path[1])):
 
2023
                        # Versioned and changed, but not deleted, and still
 
2024
                        # in one of the dirs to be deleted.
 
2025
                        has_changed_files = True
 
2026
                        break
2089
2027
 
2090
 
        def backup(file_to_backup):
2091
 
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
2092
 
            osutils.rename(abs_path, self.abspath(backup_name))
2093
 
            return "removed %s (but kept a copy: %s)" % (file_to_backup,
2094
 
                                                         backup_name)
 
2028
            if has_changed_files:
 
2029
                # Make delta show ALL applicable changes in error message.
 
2030
                tree_delta = self.changes_from(self.basis_tree(),
 
2031
                    require_versioned=False, want_unversioned=True,
 
2032
                    specific_files=files)
 
2033
                for unknown_file in unknown_nested_files:
 
2034
                    if unknown_file not in tree_delta.unversioned:
 
2035
                        tree_delta.unversioned.extend((unknown_file,))
 
2036
                raise errors.BzrRemoveChangedFilesError(tree_delta)
2095
2037
 
2096
2038
        # Build inv_delta and delete files where applicable,
2097
2039
        # do this before any modifications to inventory.
2121
2063
                        len(os.listdir(abs_path)) > 0):
2122
2064
                        if force:
2123
2065
                            osutils.rmtree(abs_path)
2124
 
                            message = "deleted %s" % (f,)
2125
2066
                        else:
2126
 
                            message = backup(f)
 
2067
                            message = "%s is not an empty directory "\
 
2068
                                "and won't be deleted." % (f,)
2127
2069
                    else:
2128
 
                        if f in files_to_backup:
2129
 
                            message = backup(f)
2130
 
                        else:
2131
 
                            osutils.delete_any(abs_path)
2132
 
                            message = "deleted %s" % (f,)
 
2070
                        osutils.delete_any(abs_path)
 
2071
                        message = "deleted %s" % (f,)
2133
2072
                elif message is not None:
2134
2073
                    # Only care if we haven't done anything yet.
2135
2074
                    message = "%s does not exist." % (f,)
2272
2211
    _marker = object()
2273
2212
 
2274
2213
    def update(self, change_reporter=None, possible_transports=None,
2275
 
               revision=None, old_tip=_marker, show_base=False):
 
2214
               revision=None, old_tip=_marker):
2276
2215
        """Update a working tree along its branch.
2277
2216
 
2278
2217
        This will update the branch if its bound too, which means we have
2315
2254
            else:
2316
2255
                if old_tip is self._marker:
2317
2256
                    old_tip = None
2318
 
            return self._update_tree(old_tip, change_reporter, revision, show_base)
 
2257
            return self._update_tree(old_tip, change_reporter, revision)
2319
2258
        finally:
2320
2259
            self.unlock()
2321
2260
 
2322
2261
    @needs_tree_write_lock
2323
 
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
2324
 
                     show_base=False):
 
2262
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
2325
2263
        """Update a tree to the master branch.
2326
2264
 
2327
2265
        :param old_tip: if supplied, the previous tip revision the branch,
2354
2292
            other_tree = self.branch.repository.revision_tree(old_tip)
2355
2293
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
2356
2294
                                             base_tree, this_tree=self,
2357
 
                                             change_reporter=change_reporter,
2358
 
                                             show_base=show_base)
 
2295
                                             change_reporter=change_reporter)
2359
2296
            if nb_conflicts:
2360
2297
                self.add_parent_tree((old_tip, other_tree))
2361
2298
                trace.note('Rerun update after fixing the conflicts.')
2385
2322
 
2386
2323
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
2387
2324
                                             this_tree=self,
2388
 
                                             change_reporter=change_reporter,
2389
 
                                             show_base=show_base)
 
2325
                                             change_reporter=change_reporter)
2390
2326
            self.set_last_revision(revision)
2391
2327
            # TODO - dedup parents list with things merged by pull ?
2392
2328
            # reuse the tree we've updated to to set the basis: