/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to workingtree.py

Fix WorkingTree.remove to behave more like the tests expect.

Merged from https://code.launchpad.net/~jelmer/brz-git/remove-fixes/+merge/341511

Show diffs side-by-side

added added

removed removed

Lines of Context:
43
43
    )
44
44
from dulwich.objects import (
45
45
    Blob,
 
46
    Tree,
46
47
    S_IFGITLINK,
47
48
    )
48
49
from dulwich.repo import Repo
395
396
        if to_file is None:
396
397
            to_file = sys.stdout
397
398
 
398
 
        files = list(files)
399
 
 
400
 
        if len(files) == 0:
401
 
            return # nothing to do
402
 
 
403
 
        # Sort needed to first handle directory content before the directory
404
 
        files.sort(reverse=True)
405
 
 
406
399
        def backup(file_to_backup):
407
400
            abs_path = self.abspath(file_to_backup)
408
401
            backup_name = self.controldir._available_backup_name(file_to_backup)
410
403
            return "removed %s (but kept a copy: %s)" % (
411
404
                file_to_backup, backup_name)
412
405
 
 
406
        # Sort needed to first handle directory content before the directory
 
407
        files_to_backup = []
 
408
 
 
409
        all_files = set()
 
410
 
 
411
        def recurse_directory_to_add_files(directory):
 
412
            # Recurse directory and add all files
 
413
            # so we can check if they have changed.
 
414
            for parent_info, file_infos in self.walkdirs(directory):
 
415
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
416
                    # Is it versioned or ignored?
 
417
                    if self.is_versioned(relpath):
 
418
                        # Add nested content for deletion.
 
419
                        all_files.add(relpath)
 
420
                    else:
 
421
                        # Files which are not versioned
 
422
                        # should be treated as unknown.
 
423
                        files_to_backup.append(relpath)
 
424
 
413
425
        with self.lock_tree_write():
 
426
            for filepath in files:
 
427
                # Get file name into canonical form.
 
428
                abspath = self.abspath(filepath)
 
429
                filepath = self.relpath(abspath)
 
430
 
 
431
                if filepath:
 
432
                    all_files.add(filepath)
 
433
                    recurse_directory_to_add_files(filepath)
 
434
 
 
435
            files = list(all_files)
 
436
 
 
437
            if len(files) == 0:
 
438
                return # nothing to do
 
439
 
 
440
            # Sort needed to first handle directory content before the directory
 
441
            files.sort(reverse=True)
 
442
 
 
443
            # Bail out if we are going to delete files we shouldn't
 
444
            if not keep_files and not force:
 
445
                for (file_id, path, content_change, versioned, parent_id, name,
 
446
                     kind, executable) in self.iter_changes(self.basis_tree(),
 
447
                         include_unchanged=True, require_versioned=False,
 
448
                         want_unversioned=True, specific_files=files):
 
449
                    if versioned[0] == False:
 
450
                        # The record is unknown or newly added
 
451
                        files_to_backup.append(path[1])
 
452
                        files_to_backup.extend(osutils.parent_directories(path[1]))
 
453
                    elif (content_change and (kind[1] is not None) and
 
454
                            osutils.is_inside_any(files, path[1])):
 
455
                        # Versioned and changed, but not deleted, and still
 
456
                        # in one of the dirs to be deleted.
 
457
                        files_to_backup.append(path[1])
 
458
                        files_to_backup.extend(osutils.parent_directories(path[1]))
 
459
 
414
460
            for f in files:
415
461
                if f == '':
416
462
                    continue
 
463
 
 
464
                try:
 
465
                    kind = self.kind(f)
 
466
                except errors.NoSuchFile:
 
467
                    kind = None
 
468
 
 
469
                abs_path = self.abspath(f)
 
470
                if verbose:
 
471
                    # having removed it, it must be either ignored or unknown
 
472
                    if self.is_ignored(f):
 
473
                        new_status = 'I'
 
474
                    else:
 
475
                        new_status = '?'
 
476
                    kind_ch = osutils.kind_marker(kind)
 
477
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
 
478
                if kind is None:
 
479
                    message = "%s does not exist" % (f, )
417
480
                else:
418
 
                    abs_path = self.abspath(f)
419
 
                    if verbose:
420
 
                        # having removed it, it must be either ignored or unknown
421
 
                        if self.is_ignored(f):
422
 
                            new_status = 'I'
 
481
                    if not keep_files:
 
482
                        if f in files_to_backup and not force:
 
483
                            message = backup(f)
423
484
                        else:
424
 
                            new_status = '?'
425
 
                        # XXX: Really should be a more abstract reporter interface
426
 
                        kind_ch = osutils.kind_marker(self.kind(f))
427
 
                        to_file.write(new_status + '       ' + f + kind_ch + '\n')
428
 
                    # Unversion file
429
 
                    # TODO(jelmer): _unversion_path() is O(size-of-index) for directories
430
 
                    if self._unversion_path(f) == 0:
431
 
                        if (osutils.isdir(abs_path) and
432
 
                            len(os.listdir(abs_path)) == 0):
433
 
                            if not keep_files:
 
485
                            if kind == 'directory':
 
486
                                osutils.rmtree(abs_path)
 
487
                            else:
434
488
                                osutils.delete_any(abs_path)
435
 
                            message = "removed %s" % (f,)
436
 
                        else:
437
 
                            message = "%s is not versioned." % (f,)
 
489
                            message = "deleted %s" % (f,)
438
490
                    else:
439
491
                        message = "removed %s" % (f,)
440
 
                        if osutils.lexists(abs_path):
441
 
                            if (osutils.isdir(abs_path) and
442
 
                                len(os.listdir(abs_path)) > 0):
443
 
                                if force:
444
 
                                    osutils.rmtree(abs_path)
445
 
                                    message = "deleted %s" % (f,)
446
 
                                else:
447
 
                                    message = backup(f)
448
 
                            else:
449
 
                                if not keep_files:
450
 
                                    osutils.delete_any(abs_path)
451
 
                                    message = "deleted %s" % (f,)
 
492
                self._unversion_path(f)
452
493
 
453
494
                # print only one message (if any) per file.
454
495
                if message is not None:
455
496
                    trace.note(message)
 
497
            self._versioned_dirs = None
456
498
            self.flush()
457
499
 
458
500
    def _add(self, files, ids, kinds):
1371
1413
 
1372
1414
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1373
1415
            require_versioned=False, include_root=False):
1374
 
        # TODO(jelmer): Handle include_root
1375
 
        # TODO(jelmer): Handle require_versioned
 
1416
        if require_versioned and specific_files:
 
1417
            for path in specific_files:
 
1418
                if (not self.source.is_versioned(path) and
 
1419
                    not self.target.is_versioned(path)):
 
1420
                    raise errors.PathsNotVersionedError(path)
1376
1421
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1377
1422
        with self.lock_read():
1378
1423
            return changes_between_git_tree_and_working_copy(
1379
1424
                self.source.store, self.source.tree,
1380
 
                self.target, want_unchanged=want_unchanged)
 
1425
                self.target, want_unchanged=want_unchanged,
 
1426
                include_root=include_root)
1381
1427
 
1382
1428
    def compare(self, want_unchanged=False, specific_files=None,
1383
1429
                extra_trees=None, require_versioned=False, include_root=False,
1415
1461
                        untracked_changes(self.target))
1416
1462
            return changes_from_git_changes(
1417
1463
                    changes, self.target.mapping,
1418
 
                    specific_files=specific_files)
 
1464
                    specific_files=specific_files,
 
1465
                    include_unchanged=include_unchanged)
1419
1466
 
1420
1467
 
1421
1468
tree.InterTree.register_optimiser(InterIndexGitTree)
1424
1471
def untracked_changes(tree):
1425
1472
    for e in tree.extras():
1426
1473
        ap = tree.abspath(e)
1427
 
        st = os.stat(ap)
 
1474
        st = os.lstat(ap)
1428
1475
        try:
1429
1476
            np, accessible  = osutils.normalized_filename(e)
1430
1477
        except UnicodeDecodeError:
1431
1478
            raise errors.BadFilenameEncoding(
1432
1479
                e, osutils._fs_enc)
1433
 
        yield ((None, np), (None, st.st_mode),
1434
 
               (None, blob_from_path_and_stat(ap.encode('utf-8'), st).id))
 
1480
        if stat.S_ISDIR(st.st_mode):
 
1481
            obj_id = Tree().id
 
1482
        else:
 
1483
            obj_id = blob_from_path_and_stat(ap.encode('utf-8'), st).id
 
1484
        yield ((None, np), (None, st.st_mode), (None, obj_id))
1435
1485
 
1436
1486
 
1437
1487
def changes_between_git_tree_and_index(store, from_tree_sha, target,
1445
1495
 
1446
1496
 
1447
1497
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1448
 
        want_unchanged=False, update_index=False):
 
1498
        want_unchanged=False, update_index=False, include_root=False):
1449
1499
    """Determine the changes between a git tree and a working tree with index.
1450
1500
 
1451
1501
    """