~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Martin
  • Date: 2011-08-04 00:17:53 UTC
  • mto: This revision was merged to the branch mainline in revision 6055.
  • Revision ID: gzlist@googlemail.com-20110804001753-plgpwcpsxcum16yb
Make tests raising KnownFailure use the knownFailure method instead

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
19
19
from stat import S_ISREG, S_IEXEC
20
20
import time
21
21
 
22
 
from bzrlib.lazy_import import lazy_import
23
 
lazy_import(globals(), """
 
22
from bzrlib import (
 
23
    errors,
 
24
    lazy_import,
 
25
    registry,
 
26
    trace,
 
27
    tree,
 
28
    )
 
29
lazy_import.lazy_import(globals(), """
24
30
from bzrlib import (
25
31
    annotate,
26
32
    bencode,
27
33
    bzrdir,
28
34
    commit,
 
35
    conflicts,
29
36
    delta,
30
37
    errors,
31
38
    inventory,
33
40
    osutils,
34
41
    revision as _mod_revision,
35
42
    ui,
 
43
    urlutils,
36
44
    )
37
45
""")
38
46
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
40
48
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
41
49
                           UnableCreateSymlink)
42
50
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
43
 
from bzrlib.inventory import InventoryEntry
44
51
from bzrlib.osutils import (
45
52
    delete_any,
46
53
    file_kind,
47
54
    has_symlinks,
48
 
    lexists,
49
55
    pathjoin,
50
56
    sha_file,
51
57
    splitpath,
52
58
    supports_executable,
53
 
)
 
59
    )
54
60
from bzrlib.progress import ProgressPhase
55
61
from bzrlib.symbol_versioning import (
56
 
        deprecated_function,
57
 
        deprecated_in,
58
 
        )
59
 
from bzrlib.trace import mutter, warning
60
 
from bzrlib import tree
61
 
import bzrlib.ui
62
 
import bzrlib.urlutils as urlutils
 
62
    deprecated_function,
 
63
    deprecated_in,
 
64
    deprecated_method,
 
65
    )
63
66
 
64
67
 
65
68
ROOT_PARENT = "root-parent"
66
69
 
67
 
 
68
70
def unique_add(map, key, value):
69
71
    if key in map:
70
72
        raise DuplicateKey(key=key)
71
73
    map[key] = value
72
74
 
73
75
 
 
76
 
74
77
class _TransformResults(object):
75
78
    def __init__(self, modified_paths, rename_count):
76
79
        object.__init__(self)
100
103
        self._new_parent = {}
101
104
        # mapping of trans_id with new contents -> new file_kind
102
105
        self._new_contents = {}
 
106
        # mapping of trans_id => (sha1 of content, stat_value)
 
107
        self._observed_sha1s = {}
103
108
        # Set of trans_ids whose contents will be removed
104
109
        self._removed_contents = set()
105
110
        # Mapping of trans_id -> new execute-bit value
124
129
            self._new_root = self.trans_id_tree_file_id(root_id)
125
130
        else:
126
131
            self._new_root = None
127
 
        # Indictor of whether the transform has been applied
 
132
        # Indicator of whether the transform has been applied
128
133
        self._done = False
129
134
        # A progress bar
130
135
        self._pb = pb
133
138
        # A counter of how many files have been renamed
134
139
        self.rename_count = 0
135
140
 
 
141
    def __enter__(self):
 
142
        """Support Context Manager API."""
 
143
        return self
 
144
 
 
145
    def __exit__(self, exc_type, exc_val, exc_tb):
 
146
        """Support Context Manager API."""
 
147
        self.finalize()
 
148
 
136
149
    def finalize(self):
137
150
        """Release the working tree lock, if held.
138
151
 
213
226
        This means that the old root trans-id becomes obsolete, so it is
214
227
        recommended only to invoke this after the root trans-id has become
215
228
        irrelevant.
 
229
 
216
230
        """
217
231
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
218
232
                     ROOT_PARENT]
224
238
            self._new_root = new_roots[0]
225
239
            return
226
240
        old_new_root = new_roots[0]
227
 
        # TODO: What to do if a old_new_root is present, but self._new_root is
228
 
        #       not listed as being removed? This code explicitly unversions
229
 
        #       the old root and versions it with the new file_id. Though that
230
 
        #       seems like an incomplete delta
231
 
 
232
241
        # unversion the new root's directory.
233
 
        file_id = self.final_file_id(old_new_root)
 
242
        if self.final_kind(self._new_root) is None:
 
243
            file_id = self.final_file_id(old_new_root)
 
244
        else:
 
245
            file_id = self.final_file_id(self._new_root)
234
246
        if old_new_root in self._new_id:
235
247
            self.cancel_versioning(old_new_root)
236
248
        else:
240
252
        if (self.tree_file_id(self._new_root) is not None and
241
253
            self._new_root not in self._removed_id):
242
254
            self.unversion_file(self._new_root)
243
 
        self.version_file(file_id, self._new_root)
 
255
        if file_id is not None:
 
256
            self.version_file(file_id, self._new_root)
244
257
 
245
258
        # Now move children of new root into old root directory.
246
259
        # Ensure all children are registered with the transaction, but don't
315
328
 
316
329
    def delete_contents(self, trans_id):
317
330
        """Schedule the contents of a path entry for deletion"""
318
 
        # Ensure that the object exists in the WorkingTree, this will raise an
319
 
        # exception if there is a problem
320
 
        self.tree_kind(trans_id)
321
 
        self._removed_contents.add(trans_id)
 
331
        kind = self.tree_kind(trans_id)
 
332
        if kind is not None:
 
333
            self._removed_contents.add(trans_id)
322
334
 
323
335
    def cancel_deletion(self, trans_id):
324
336
        """Cancel a scheduled deletion"""
381
393
        return sorted(FinalPaths(self).get_paths(new_ids))
382
394
 
383
395
    def _inventory_altered(self):
384
 
        """Get the trans_ids and paths of files needing new inv entries."""
385
 
        new_ids = set()
386
 
        for id_set in [self._new_name, self._new_parent, self._new_id,
 
396
        """Determine which trans_ids need new Inventory entries.
 
397
 
 
398
        An new entry is needed when anything that would be reflected by an
 
399
        inventory entry changes, including file name, file_id, parent file_id,
 
400
        file kind, and the execute bit.
 
401
 
 
402
        Some care is taken to return entries with real changes, not cases
 
403
        where the value is deleted and then restored to its original value,
 
404
        but some actually unchanged values may be returned.
 
405
 
 
406
        :returns: A list of (path, trans_id) for all items requiring an
 
407
            inventory change. Ordered by path.
 
408
        """
 
409
        changed_ids = set()
 
410
        # Find entries whose file_ids are new (or changed).
 
411
        new_file_id = set(t for t in self._new_id
 
412
                          if self._new_id[t] != self.tree_file_id(t))
 
413
        for id_set in [self._new_name, self._new_parent, new_file_id,
387
414
                       self._new_executability]:
388
 
            new_ids.update(id_set)
 
415
            changed_ids.update(id_set)
 
416
        # removing implies a kind change
389
417
        changed_kind = set(self._removed_contents)
 
418
        # so does adding
390
419
        changed_kind.intersection_update(self._new_contents)
391
 
        changed_kind.difference_update(new_ids)
392
 
        changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
393
 
                        self.final_kind(t))
394
 
        new_ids.update(changed_kind)
395
 
        return sorted(FinalPaths(self).get_paths(new_ids))
 
420
        # Ignore entries that are already known to have changed.
 
421
        changed_kind.difference_update(changed_ids)
 
422
        #  to keep only the truly changed ones
 
423
        changed_kind = (t for t in changed_kind
 
424
                        if self.tree_kind(t) != self.final_kind(t))
 
425
        # all kind changes will alter the inventory
 
426
        changed_ids.update(changed_kind)
 
427
        # To find entries with changed parent_ids, find parents which existed,
 
428
        # but changed file_id.
 
429
        changed_file_id = set(t for t in new_file_id if t in self._removed_id)
 
430
        # Now add all their children to the set.
 
431
        for parent_trans_id in new_file_id:
 
432
            changed_ids.update(self.iter_tree_children(parent_trans_id))
 
433
        return sorted(FinalPaths(self).get_paths(changed_ids))
396
434
 
397
435
    def final_kind(self, trans_id):
398
436
        """Determine the final file kind, after any changes applied.
399
437
 
400
 
        Raises NoSuchFile if the file does not exist/has no contents.
401
 
        (It is conceivable that a path would be created without the
402
 
        corresponding contents insertion command)
 
438
        :return: None if the file does not exist/has no contents.  (It is
 
439
            conceivable that a path would be created without the corresponding
 
440
            contents insertion command)
403
441
        """
404
442
        if trans_id in self._new_contents:
405
443
            return self._new_contents[trans_id]
406
444
        elif trans_id in self._removed_contents:
407
 
            raise NoSuchFile(None)
 
445
            return None
408
446
        else:
409
447
            return self.tree_kind(trans_id)
410
448
 
523
561
        for trans_id in self._removed_id:
524
562
            file_id = self.tree_file_id(trans_id)
525
563
            if file_id is not None:
 
564
                # XXX: This seems like something that should go via a different
 
565
                #      indirection.
526
566
                if self._tree.inventory[file_id].kind == 'directory':
527
567
                    parents.append(trans_id)
528
568
            elif self.tree_kind(trans_id) == 'directory':
532
572
            # ensure that all children are registered with the transaction
533
573
            list(self.iter_tree_children(parent_id))
534
574
 
 
575
    @deprecated_method(deprecated_in((2, 3, 0)))
535
576
    def has_named_child(self, by_parent, parent_id, name):
536
 
        try:
537
 
            children = by_parent[parent_id]
538
 
        except KeyError:
539
 
            children = []
540
 
        for child in children:
 
577
        return self._has_named_child(
 
578
            name, parent_id, known_children=by_parent.get(parent_id, []))
 
579
 
 
580
    def _has_named_child(self, name, parent_id, known_children):
 
581
        """Does a parent already have a name child.
 
582
 
 
583
        :param name: The searched for name.
 
584
 
 
585
        :param parent_id: The parent for which the check is made.
 
586
 
 
587
        :param known_children: The already known children. This should have
 
588
            been recently obtained from `self.by_parent.get(parent_id)`
 
589
            (or will be if None is passed).
 
590
        """
 
591
        if known_children is None:
 
592
            known_children = self.by_parent().get(parent_id, [])
 
593
        for child in known_children:
541
594
            if self.final_name(child) == name:
542
595
                return True
543
 
        try:
544
 
            path = self._tree_id_paths[parent_id]
545
 
        except KeyError:
 
596
        parent_path = self._tree_id_paths.get(parent_id, None)
 
597
        if parent_path is None:
 
598
            # No parent... no children
546
599
            return False
547
 
        childpath = joinpath(path, name)
548
 
        child_id = self._tree_path_ids.get(childpath)
 
600
        child_path = joinpath(parent_path, name)
 
601
        child_id = self._tree_path_ids.get(child_path, None)
549
602
        if child_id is None:
550
 
            return lexists(self._tree.abspath(childpath))
 
603
            # Not known by the tree transform yet, check the filesystem
 
604
            return osutils.lexists(self._tree.abspath(child_path))
551
605
        else:
552
 
            if self.final_parent(child_id) != parent_id:
553
 
                return False
554
 
            if child_id in self._removed_contents:
555
 
                # XXX What about dangling file-ids?
556
 
                return False
557
 
            else:
558
 
                return True
 
606
            raise AssertionError('child_id is missing: %s, %s, %s'
 
607
                                 % (name, parent_id, child_id))
 
608
 
 
609
    def _available_backup_name(self, name, target_id):
 
610
        """Find an available backup name.
 
611
 
 
612
        :param name: The basename of the file.
 
613
 
 
614
        :param target_id: The directory trans_id where the backup should 
 
615
            be placed.
 
616
        """
 
617
        known_children = self.by_parent().get(target_id, [])
 
618
        return osutils.available_backup_name(
 
619
            name,
 
620
            lambda base: self._has_named_child(
 
621
                base, target_id, known_children))
559
622
 
560
623
    def _parent_loops(self):
561
624
        """No entry should be its own ancestor"""
596
659
        """
597
660
        conflicts = []
598
661
        for trans_id in self._new_id.iterkeys():
599
 
            try:
600
 
                kind = self.final_kind(trans_id)
601
 
            except NoSuchFile:
 
662
            kind = self.final_kind(trans_id)
 
663
            if kind is None:
602
664
                conflicts.append(('versioning no contents', trans_id))
603
665
                continue
604
 
            if not InventoryEntry.versionable_kind(kind):
 
666
            if not inventory.InventoryEntry.versionable_kind(kind):
605
667
                conflicts.append(('versioning bad kind', trans_id, kind))
606
668
        return conflicts
607
669
 
618
680
            if self.final_file_id(trans_id) is None:
619
681
                conflicts.append(('unversioned executability', trans_id))
620
682
            else:
621
 
                try:
622
 
                    non_file = self.final_kind(trans_id) != "file"
623
 
                except NoSuchFile:
624
 
                    non_file = True
625
 
                if non_file is True:
 
683
                if self.final_kind(trans_id) != "file":
626
684
                    conflicts.append(('non-file executability', trans_id))
627
685
        return conflicts
628
686
 
630
688
        """Check for overwrites (not permitted on Win32)"""
631
689
        conflicts = []
632
690
        for trans_id in self._new_contents:
633
 
            try:
634
 
                self.tree_kind(trans_id)
635
 
            except NoSuchFile:
 
691
            if self.tree_kind(trans_id) is None:
636
692
                continue
637
693
            if trans_id not in self._removed_contents:
638
694
                conflicts.append(('overwrite', trans_id,
645
701
        if (self._new_name, self._new_parent) == ({}, {}):
646
702
            return conflicts
647
703
        for children in by_parent.itervalues():
648
 
            name_ids = [(self.final_name(t), t) for t in children]
649
 
            if not self._case_sensitive_target:
650
 
                name_ids = [(n.lower(), t) for n, t in name_ids]
 
704
            name_ids = []
 
705
            for child_tid in children:
 
706
                name = self.final_name(child_tid)
 
707
                if name is not None:
 
708
                    # Keep children only if they still exist in the end
 
709
                    if not self._case_sensitive_target:
 
710
                        name = name.lower()
 
711
                    name_ids.append((name, child_tid))
651
712
            name_ids.sort()
652
713
            last_name = None
653
714
            last_trans_id = None
654
715
            for name, trans_id in name_ids:
655
 
                try:
656
 
                    kind = self.final_kind(trans_id)
657
 
                except NoSuchFile:
658
 
                    kind = None
 
716
                kind = self.final_kind(trans_id)
659
717
                file_id = self.final_file_id(trans_id)
660
718
                if kind is None and file_id is None:
661
719
                    continue
680
738
        return conflicts
681
739
 
682
740
    def _parent_type_conflicts(self, by_parent):
683
 
        """parents must have directory 'contents'."""
 
741
        """Children must have a directory parent"""
684
742
        conflicts = []
685
743
        for parent_id, children in by_parent.iteritems():
686
744
            if parent_id is ROOT_PARENT:
687
745
                continue
688
 
            if not self._any_contents(children):
 
746
            no_children = True
 
747
            for child_id in children:
 
748
                if self.final_kind(child_id) is not None:
 
749
                    no_children = False
 
750
                    break
 
751
            if no_children:
689
752
                continue
690
 
            for child in children:
691
 
                try:
692
 
                    self.final_kind(child)
693
 
                except NoSuchFile:
694
 
                    continue
695
 
            try:
696
 
                kind = self.final_kind(parent_id)
697
 
            except NoSuchFile:
698
 
                kind = None
 
753
            # There is at least a child, so we need an existing directory to
 
754
            # contain it.
 
755
            kind = self.final_kind(parent_id)
699
756
            if kind is None:
 
757
                # The directory will be deleted
700
758
                conflicts.append(('missing parent', parent_id))
701
759
            elif kind != "directory":
 
760
                # Meh, we need a *directory* to put something in it
702
761
                conflicts.append(('non-directory parent', parent_id))
703
762
        return conflicts
704
763
 
705
 
    def _any_contents(self, trans_ids):
706
 
        """Return true if any of the trans_ids, will have contents."""
707
 
        for trans_id in trans_ids:
708
 
            try:
709
 
                kind = self.final_kind(trans_id)
710
 
            except NoSuchFile:
711
 
                continue
712
 
            return True
713
 
        return False
714
 
 
715
764
    def _set_executability(self, path, trans_id):
716
765
        """Set the executability of versioned files """
717
766
        if supports_executable():
739
788
        return trans_id
740
789
 
741
790
    def new_file(self, name, parent_id, contents, file_id=None,
742
 
                 executable=None):
 
791
                 executable=None, sha1=None):
743
792
        """Convenience method to create files.
744
793
 
745
794
        name is the name of the file to create.
752
801
        trans_id = self._new_entry(name, parent_id, file_id)
753
802
        # TODO: rather than scheduling a set_executable call,
754
803
        # have create_file create the file with the right mode.
755
 
        self.create_file(contents, trans_id)
 
804
        self.create_file(contents, trans_id, sha1=sha1)
756
805
        if executable is not None:
757
806
            self.set_executability(executable, trans_id)
758
807
        return trans_id
781
830
        self.create_symlink(target, trans_id)
782
831
        return trans_id
783
832
 
 
833
    def new_orphan(self, trans_id, parent_id):
 
834
        """Schedule an item to be orphaned.
 
835
 
 
836
        When a directory is about to be removed, its children, if they are not
 
837
        versioned are moved out of the way: they don't have a parent anymore.
 
838
 
 
839
        :param trans_id: The trans_id of the existing item.
 
840
        :param parent_id: The parent trans_id of the item.
 
841
        """
 
842
        raise NotImplementedError(self.new_orphan)
 
843
 
 
844
    def _get_potential_orphans(self, dir_id):
 
845
        """Find the potential orphans in a directory.
 
846
 
 
847
        A directory can't be safely deleted if there are versioned files in it.
 
848
        If all the contained files are unversioned then they can be orphaned.
 
849
 
 
850
        The 'None' return value means that the directory contains at least one
 
851
        versioned file and should not be deleted.
 
852
 
 
853
        :param dir_id: The directory trans id.
 
854
 
 
855
        :return: A list of the orphan trans ids or None if at least one
 
856
             versioned file is present.
 
857
        """
 
858
        orphans = []
 
859
        # Find the potential orphans, stop if one item should be kept
 
860
        for child_tid in self.by_parent()[dir_id]:
 
861
            if child_tid in self._removed_contents:
 
862
                # The child is removed as part of the transform. Since it was
 
863
                # versioned before, it's not an orphan
 
864
                continue
 
865
            elif self.final_file_id(child_tid) is None:
 
866
                # The child is not versioned
 
867
                orphans.append(child_tid)
 
868
            else:
 
869
                # We have a versioned file here, searching for orphans is
 
870
                # meaningless.
 
871
                orphans = None
 
872
                break
 
873
        return orphans
 
874
 
784
875
    def _affected_ids(self):
785
876
        """Return the set of transform ids affected by the transform"""
786
877
        trans_ids = set(self._removed_id)
845
936
        Return a (name, parent, kind, executable) tuple
846
937
        """
847
938
        to_name = self.final_name(to_trans_id)
848
 
        try:
849
 
            to_kind = self.final_kind(to_trans_id)
850
 
        except NoSuchFile:
851
 
            to_kind = None
 
939
        to_kind = self.final_kind(to_trans_id)
852
940
        to_parent = self.final_file_id(self.final_parent(to_trans_id))
853
941
        if to_trans_id in self._new_executability:
854
942
            to_executable = self._new_executability[to_trans_id]
1101
1189
        self._deletiondir = None
1102
1190
        # A mapping of transform ids to their limbo filename
1103
1191
        self._limbo_files = {}
 
1192
        self._possibly_stale_limbo_files = set()
1104
1193
        # A mapping of transform ids to a set of the transform ids of children
1105
1194
        # that their limbo directory has
1106
1195
        self._limbo_children = {}
1119
1208
        if self._tree is None:
1120
1209
            return
1121
1210
        try:
1122
 
            entries = [(self._limbo_name(t), t, k) for t, k in
1123
 
                       self._new_contents.iteritems()]
1124
 
            entries.sort(reverse=True)
1125
 
            for path, trans_id, kind in entries:
1126
 
                delete_any(path)
 
1211
            limbo_paths = self._limbo_files.values() + list(
 
1212
                self._possibly_stale_limbo_files)
 
1213
            limbo_paths = sorted(limbo_paths, reverse=True)
 
1214
            for path in limbo_paths:
 
1215
                try:
 
1216
                    delete_any(path)
 
1217
                except OSError, e:
 
1218
                    if e.errno != errno.ENOENT:
 
1219
                        raise
 
1220
                    # XXX: warn? perhaps we just got interrupted at an
 
1221
                    # inconvenient moment, but perhaps files are disappearing
 
1222
                    # from under us?
1127
1223
            try:
1128
1224
                delete_any(self._limbodir)
1129
1225
            except OSError:
1178
1274
        entries from _limbo_files, because they are now stale.
1179
1275
        """
1180
1276
        for trans_id in trans_ids:
1181
 
            old_path = self._limbo_files.pop(trans_id)
 
1277
            old_path = self._limbo_files[trans_id]
 
1278
            self._possibly_stale_limbo_files.add(old_path)
 
1279
            del self._limbo_files[trans_id]
1182
1280
            if trans_id not in self._new_contents:
1183
1281
                continue
1184
1282
            new_path = self._limbo_name(trans_id)
1185
 
            osutils.rename(old_path, new_path)
 
1283
            os.rename(old_path, new_path)
 
1284
            self._possibly_stale_limbo_files.remove(old_path)
1186
1285
            for descendant in self._limbo_descendants(trans_id):
1187
1286
                desc_path = self._limbo_files[descendant]
1188
1287
                desc_path = new_path + desc_path[len(old_path):]
1195
1294
            descendants.update(self._limbo_descendants(descendant))
1196
1295
        return descendants
1197
1296
 
1198
 
    def create_file(self, contents, trans_id, mode_id=None):
 
1297
    def create_file(self, contents, trans_id, mode_id=None, sha1=None):
1199
1298
        """Schedule creation of a new file.
1200
1299
 
1201
 
        See also new_file.
1202
 
 
1203
 
        Contents is an iterator of strings, all of which will be written
1204
 
        to the target destination.
1205
 
 
1206
 
        New file takes the permissions of any existing file with that id,
1207
 
        unless mode_id is specified.
 
1300
        :seealso: new_file.
 
1301
 
 
1302
        :param contents: an iterator of strings, all of which will be written
 
1303
            to the target destination.
 
1304
        :param trans_id: TreeTransform handle
 
1305
        :param mode_id: If not None, force the mode of the target file to match
 
1306
            the mode of the object referenced by mode_id.
 
1307
            Otherwise, we will try to preserve mode bits of an existing file.
 
1308
        :param sha1: If the sha1 of this content is already known, pass it in.
 
1309
            We can use it to prevent future sha1 computations.
1208
1310
        """
1209
1311
        name = self._limbo_name(trans_id)
1210
1312
        f = open(name, 'wb')
1211
1313
        try:
1212
 
            try:
1213
 
                unique_add(self._new_contents, trans_id, 'file')
1214
 
            except:
1215
 
                # Clean up the file, it never got registered so
1216
 
                # TreeTransform.finalize() won't clean it up.
1217
 
                f.close()
1218
 
                os.unlink(name)
1219
 
                raise
1220
 
 
 
1314
            unique_add(self._new_contents, trans_id, 'file')
1221
1315
            f.writelines(contents)
1222
1316
        finally:
1223
1317
            f.close()
1224
1318
        self._set_mtime(name)
1225
1319
        self._set_mode(trans_id, mode_id, S_ISREG)
 
1320
        # It is unfortunate we have to use lstat instead of fstat, but we just
 
1321
        # used utime and chmod on the file, so we need the accurate final
 
1322
        # details.
 
1323
        if sha1 is not None:
 
1324
            self._observed_sha1s[trans_id] = (sha1, osutils.lstat(name))
1226
1325
 
1227
1326
    def _read_file_chunks(self, trans_id):
1228
1327
        cur_file = open(self._limbo_name(trans_id), 'rb')
1287
1386
    def cancel_creation(self, trans_id):
1288
1387
        """Cancel the creation of new file contents."""
1289
1388
        del self._new_contents[trans_id]
 
1389
        if trans_id in self._observed_sha1s:
 
1390
            del self._observed_sha1s[trans_id]
1290
1391
        children = self._limbo_children.get(trans_id)
1291
1392
        # if this is a limbo directory with children, move them before removing
1292
1393
        # the directory
1296
1397
            del self._limbo_children_names[trans_id]
1297
1398
        delete_any(self._limbo_name(trans_id))
1298
1399
 
 
1400
    def new_orphan(self, trans_id, parent_id):
 
1401
        # FIXME: There is no tree config, so we use the branch one (it's weird
 
1402
        # to define it this way as orphaning can only occur in a working tree,
 
1403
        # but that's all we have (for now). It will find the option in
 
1404
        # locations.conf or bazaar.conf though) -- vila 20100916
 
1405
        conf = self._tree.branch.get_config()
 
1406
        conf_var_name = 'bzr.transform.orphan_policy'
 
1407
        orphan_policy = conf.get_user_option(conf_var_name)
 
1408
        default_policy = orphaning_registry.default_key
 
1409
        if orphan_policy is None:
 
1410
            orphan_policy = default_policy
 
1411
        if orphan_policy not in orphaning_registry:
 
1412
            trace.warning('%s (from %s) is not a known policy, defaulting '
 
1413
                'to %s' % (orphan_policy, conf_var_name, default_policy))
 
1414
            orphan_policy = default_policy
 
1415
        handle_orphan = orphaning_registry.get(orphan_policy)
 
1416
        handle_orphan(self, trans_id, parent_id)
 
1417
 
 
1418
 
 
1419
class OrphaningError(errors.BzrError):
 
1420
 
 
1421
    # Only bugs could lead to such exception being seen by the user
 
1422
    internal_error = True
 
1423
    _fmt = "Error while orphaning %s in %s directory"
 
1424
 
 
1425
    def __init__(self, orphan, parent):
 
1426
        errors.BzrError.__init__(self)
 
1427
        self.orphan = orphan
 
1428
        self.parent = parent
 
1429
 
 
1430
 
 
1431
class OrphaningForbidden(OrphaningError):
 
1432
 
 
1433
    _fmt = "Policy: %s doesn't allow creating orphans."
 
1434
 
 
1435
    def __init__(self, policy):
 
1436
        errors.BzrError.__init__(self)
 
1437
        self.policy = policy
 
1438
 
 
1439
 
 
1440
def move_orphan(tt, orphan_id, parent_id):
 
1441
    """See TreeTransformBase.new_orphan.
 
1442
 
 
1443
    This creates a new orphan in the `bzr-orphans` dir at the root of the
 
1444
    `TreeTransform`.
 
1445
 
 
1446
    :param tt: The TreeTransform orphaning `trans_id`.
 
1447
 
 
1448
    :param orphan_id: The trans id that should be orphaned.
 
1449
 
 
1450
    :param parent_id: The orphan parent trans id.
 
1451
    """
 
1452
    # Add the orphan dir if it doesn't exist
 
1453
    orphan_dir_basename = 'bzr-orphans'
 
1454
    od_id = tt.trans_id_tree_path(orphan_dir_basename)
 
1455
    if tt.final_kind(od_id) is None:
 
1456
        tt.create_directory(od_id)
 
1457
    parent_path = tt._tree_id_paths[parent_id]
 
1458
    # Find a name that doesn't exist yet in the orphan dir
 
1459
    actual_name = tt.final_name(orphan_id)
 
1460
    new_name = tt._available_backup_name(actual_name, od_id)
 
1461
    tt.adjust_path(new_name, od_id, orphan_id)
 
1462
    trace.warning('%s has been orphaned in %s'
 
1463
                  % (joinpath(parent_path, actual_name), orphan_dir_basename))
 
1464
 
 
1465
 
 
1466
def refuse_orphan(tt, orphan_id, parent_id):
 
1467
    """See TreeTransformBase.new_orphan.
 
1468
 
 
1469
    This refuses to create orphan, letting the caller handle the conflict.
 
1470
    """
 
1471
    raise OrphaningForbidden('never')
 
1472
 
 
1473
 
 
1474
orphaning_registry = registry.Registry()
 
1475
orphaning_registry.register(
 
1476
    'conflict', refuse_orphan,
 
1477
    'Leave orphans in place and create a conflict on the directory.')
 
1478
orphaning_registry.register(
 
1479
    'move', move_orphan,
 
1480
    'Move orphans into the bzr-orphans directory.')
 
1481
orphaning_registry._set_default_key('conflict')
 
1482
 
1299
1483
 
1300
1484
class TreeTransform(DiskTreeTransform):
1301
1485
    """Represent a tree transformation.
1419
1603
    def tree_kind(self, trans_id):
1420
1604
        """Determine the file kind in the working tree.
1421
1605
 
1422
 
        Raises NoSuchFile if the file does not exist
 
1606
        :returns: The file kind or None if the file does not exist
1423
1607
        """
1424
1608
        path = self._tree_id_paths.get(trans_id)
1425
1609
        if path is None:
1426
 
            raise NoSuchFile(None)
 
1610
            return None
1427
1611
        try:
1428
1612
            return file_kind(self._tree.abspath(path))
1429
 
        except OSError, e:
1430
 
            if e.errno != errno.ENOENT:
1431
 
                raise
1432
 
            else:
1433
 
                raise NoSuchFile(path)
 
1613
        except errors.NoSuchFile:
 
1614
            return None
1434
1615
 
1435
1616
    def _set_mode(self, trans_id, mode_id, typefunc):
1436
1617
        """Set the mode of new file contents.
1542
1723
        """
1543
1724
        if not no_conflicts:
1544
1725
            self._check_malformed()
1545
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1726
        child_pb = ui.ui_factory.nested_progress_bar()
1546
1727
        try:
1547
1728
            if precomputed_delta is None:
1548
1729
                child_pb.update('Apply phase', 0, 2)
1567
1748
                mover.apply_deletions()
1568
1749
        finally:
1569
1750
            child_pb.finished()
 
1751
        if self.final_file_id(self.root) is None:
 
1752
            inventory_delta = [e for e in inventory_delta if e[0] != '']
1570
1753
        self._tree.apply_inventory_delta(inventory_delta)
 
1754
        self._apply_observed_sha1s()
1571
1755
        self._done = True
1572
1756
        self.finalize()
1573
1757
        return _TransformResults(modified_paths, self.rename_count)
1575
1759
    def _generate_inventory_delta(self):
1576
1760
        """Generate an inventory delta for the current transform."""
1577
1761
        inventory_delta = []
1578
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1762
        child_pb = ui.ui_factory.nested_progress_bar()
1579
1763
        new_paths = self._inventory_altered()
1580
1764
        total_entries = len(new_paths) + len(self._removed_id)
1581
1765
        try:
1605
1789
                if file_id is None:
1606
1790
                    continue
1607
1791
                needs_entry = False
1608
 
                try:
1609
 
                    kind = self.final_kind(trans_id)
1610
 
                except NoSuchFile:
 
1792
                kind = self.final_kind(trans_id)
 
1793
                if kind is None:
1611
1794
                    kind = self._tree.stored_kind(file_id)
1612
1795
                parent_trans_id = self.final_parent(trans_id)
1613
1796
                parent_file_id = new_path_file_ids.get(parent_trans_id)
1644
1827
        """
1645
1828
        tree_paths = list(self._tree_path_ids.iteritems())
1646
1829
        tree_paths.sort(reverse=True)
1647
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1830
        child_pb = ui.ui_factory.nested_progress_bar()
1648
1831
        try:
1649
 
            for num, data in enumerate(tree_paths):
1650
 
                path, trans_id = data
 
1832
            for num, (path, trans_id) in enumerate(tree_paths):
 
1833
                # do not attempt to move root into a subdirectory of itself.
 
1834
                if path == '':
 
1835
                    continue
1651
1836
                child_pb.update('removing file', num, len(tree_paths))
1652
1837
                full_path = self._tree.abspath(path)
1653
1838
                if trans_id in self._removed_contents:
1657
1842
                      or trans_id in self._new_parent):
1658
1843
                    try:
1659
1844
                        mover.rename(full_path, self._limbo_name(trans_id))
1660
 
                    except OSError, e:
 
1845
                    except errors.TransformRenameFailed, e:
1661
1846
                        if e.errno != errno.ENOENT:
1662
1847
                            raise
1663
1848
                    else:
1679
1864
        modified_paths = []
1680
1865
        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1681
1866
                                 new_paths)
1682
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1867
        child_pb = ui.ui_factory.nested_progress_bar()
1683
1868
        try:
1684
1869
            for num, (path, trans_id) in enumerate(new_paths):
1685
1870
                if (num % 10) == 0:
1688
1873
                if trans_id in self._needs_rename:
1689
1874
                    try:
1690
1875
                        mover.rename(self._limbo_name(trans_id), full_path)
1691
 
                    except OSError, e:
 
1876
                    except errors.TransformRenameFailed, e:
1692
1877
                        # We may be renaming a dangling inventory id
1693
1878
                        if e.errno != errno.ENOENT:
1694
1879
                            raise
1695
1880
                    else:
1696
1881
                        self.rename_count += 1
 
1882
                    # TODO: if trans_id in self._observed_sha1s, we should
 
1883
                    #       re-stat the final target, since ctime will be
 
1884
                    #       updated by the change.
1697
1885
                if (trans_id in self._new_contents or
1698
1886
                    self.path_changed(trans_id)):
1699
1887
                    if trans_id in self._new_contents:
1700
1888
                        modified_paths.append(full_path)
1701
1889
                if trans_id in self._new_executability:
1702
1890
                    self._set_executability(path, trans_id)
 
1891
                if trans_id in self._observed_sha1s:
 
1892
                    o_sha1, o_st_val = self._observed_sha1s[trans_id]
 
1893
                    st = osutils.lstat(full_path)
 
1894
                    self._observed_sha1s[trans_id] = (o_sha1, st)
1703
1895
        finally:
1704
1896
            child_pb.finished()
 
1897
        for path, trans_id in new_paths:
 
1898
            # new_paths includes stuff like workingtree conflicts. Only the
 
1899
            # stuff in new_contents actually comes from limbo.
 
1900
            if trans_id in self._limbo_files:
 
1901
                del self._limbo_files[trans_id]
1705
1902
        self._new_contents.clear()
1706
1903
        return modified_paths
1707
1904
 
 
1905
    def _apply_observed_sha1s(self):
 
1906
        """After we have finished renaming everything, update observed sha1s
 
1907
 
 
1908
        This has to be done after self._tree.apply_inventory_delta, otherwise
 
1909
        it doesn't know anything about the files we are updating. Also, we want
 
1910
        to do this as late as possible, so that most entries end up cached.
 
1911
        """
 
1912
        # TODO: this doesn't update the stat information for directories. So
 
1913
        #       the first 'bzr status' will still need to rewrite
 
1914
        #       .bzr/checkout/dirstate. However, we at least don't need to
 
1915
        #       re-read all of the files.
 
1916
        # TODO: If the operation took a while, we could do a time.sleep(3) here
 
1917
        #       to allow the clock to tick over and ensure we won't have any
 
1918
        #       problems. (we could observe start time, and finish time, and if
 
1919
        #       it is less than eg 10% overhead, add a sleep call.)
 
1920
        paths = FinalPaths(self)
 
1921
        for trans_id, observed in self._observed_sha1s.iteritems():
 
1922
            path = paths.get_path(trans_id)
 
1923
            # We could get the file_id, but dirstate prefers to use the path
 
1924
            # anyway, and it is 'cheaper' to determine.
 
1925
            # file_id = self._new_id[trans_id]
 
1926
            self._tree._observed_sha1(None, path, observed)
 
1927
 
1708
1928
 
1709
1929
class TransformPreview(DiskTreeTransform):
1710
1930
    """A TreeTransform for generating preview trees.
1725
1945
    def tree_kind(self, trans_id):
1726
1946
        path = self._tree_id_paths.get(trans_id)
1727
1947
        if path is None:
1728
 
            raise NoSuchFile(None)
1729
 
        file_id = self._tree.path2id(path)
1730
 
        return self._tree.kind(file_id)
 
1948
            return None
 
1949
        kind = self._tree.path_content_summary(path)[0]
 
1950
        if kind == 'missing':
 
1951
            kind = None
 
1952
        return kind
1731
1953
 
1732
1954
    def _set_mode(self, trans_id, mode_id, typefunc):
1733
1955
        """Set the mode of new file contents.
1753
1975
            childpath = joinpath(path, child)
1754
1976
            yield self.trans_id_tree_path(childpath)
1755
1977
 
1756
 
 
1757
 
class _PreviewTree(tree.Tree):
 
1978
    def new_orphan(self, trans_id, parent_id):
 
1979
        raise NotImplementedError(self.new_orphan)
 
1980
 
 
1981
 
 
1982
class _PreviewTree(tree.InventoryTree):
1758
1983
    """Partial implementation of Tree to support show_diff_trees"""
1759
1984
 
1760
1985
    def __init__(self, transform):
1789
2014
                yield self._get_repository().revision_tree(revision_id)
1790
2015
 
1791
2016
    def _get_file_revision(self, file_id, vf, tree_revision):
1792
 
        parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
 
2017
        parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
1793
2018
                       self._iter_parent_trees()]
1794
2019
        vf.add_lines((file_id, tree_revision), parent_keys,
1795
 
                     self.get_file(file_id).readlines())
 
2020
                     self.get_file_lines(file_id))
1796
2021
        repo = self._get_repository()
1797
2022
        base_vf = repo.texts
1798
2023
        if base_vf not in vf.fallback_versionedfiles:
1799
2024
            vf.fallback_versionedfiles.append(base_vf)
1800
2025
        return tree_revision
1801
2026
 
1802
 
    def _stat_limbo_file(self, file_id):
1803
 
        trans_id = self._transform.trans_id_file_id(file_id)
 
2027
    def _stat_limbo_file(self, file_id=None, trans_id=None):
 
2028
        if trans_id is None:
 
2029
            trans_id = self._transform.trans_id_file_id(file_id)
1804
2030
        name = self._transform._limbo_name(trans_id)
1805
2031
        return os.lstat(name)
1806
2032
 
1929
2155
            if (specific_file_ids is not None
1930
2156
                and file_id not in specific_file_ids):
1931
2157
                continue
1932
 
            try:
1933
 
                kind = self._transform.final_kind(trans_id)
1934
 
            except NoSuchFile:
 
2158
            kind = self._transform.final_kind(trans_id)
 
2159
            if kind is None:
1935
2160
                kind = self._transform._tree.stored_kind(file_id)
1936
2161
            new_entry = inventory.make_entry(
1937
2162
                kind,
2022
2247
 
2023
2248
    def get_file_size(self, file_id):
2024
2249
        """See Tree.get_file_size"""
 
2250
        trans_id = self._transform.trans_id_file_id(file_id)
 
2251
        kind = self._transform.final_kind(trans_id)
 
2252
        if kind != 'file':
 
2253
            return None
 
2254
        if trans_id in self._transform._new_contents:
 
2255
            return self._stat_limbo_file(trans_id=trans_id).st_size
2025
2256
        if self.kind(file_id) == 'file':
2026
2257
            return self._transform._tree.get_file_size(file_id)
2027
2258
        else:
2055
2286
            except errors.NoSuchId:
2056
2287
                return False
2057
2288
 
 
2289
    def has_filename(self, path):
 
2290
        trans_id = self._path2trans_id(path)
 
2291
        if trans_id in self._transform._new_contents:
 
2292
            return True
 
2293
        elif trans_id in self._transform._removed_contents:
 
2294
            return False
 
2295
        else:
 
2296
            return self._transform._tree.has_filename(path)
 
2297
 
2058
2298
    def path_content_summary(self, path):
2059
2299
        trans_id = self._path2trans_id(path)
2060
2300
        tt = self._transform
2148
2388
                                   self.get_file(file_id).readlines(),
2149
2389
                                   default_revision)
2150
2390
 
2151
 
    def get_symlink_target(self, file_id):
 
2391
    def get_symlink_target(self, file_id, path=None):
2152
2392
        """See Tree.get_symlink_target"""
2153
2393
        if not self._content_change(file_id):
2154
2394
            return self._transform._tree.get_symlink_target(file_id)
2169
2409
                path_from_root = self._final_paths.get_path(child_id)
2170
2410
                basename = self._transform.final_name(child_id)
2171
2411
                file_id = self._transform.final_file_id(child_id)
2172
 
                try:
2173
 
                    kind = self._transform.final_kind(child_id)
 
2412
                kind  = self._transform.final_kind(child_id)
 
2413
                if kind is not None:
2174
2414
                    versioned_kind = kind
2175
 
                except NoSuchFile:
 
2415
                else:
2176
2416
                    kind = 'unknown'
2177
2417
                    versioned_kind = self._transform._tree.stored_kind(file_id)
2178
2418
                if versioned_kind == 'directory':
2291
2531
    for num, _unused in enumerate(wt.all_file_ids()):
2292
2532
        if num > 0:  # more than just a root
2293
2533
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2294
 
    existing_files = set()
2295
 
    for dir, files in wt.walkdirs():
2296
 
        existing_files.update(f[0] for f in files)
2297
2534
    file_trans_id = {}
2298
 
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2535
    top_pb = ui.ui_factory.nested_progress_bar()
2299
2536
    pp = ProgressPhase("Build phase", 2, top_pb)
2300
2537
    if tree.inventory.root is not None:
2301
2538
        # This is kind of a hack: we should be altering the root
2314
2551
        pp.next_phase()
2315
2552
        file_trans_id[wt.get_root_id()] = \
2316
2553
            tt.trans_id_tree_file_id(wt.get_root_id())
2317
 
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2554
        pb = ui.ui_factory.nested_progress_bar()
2318
2555
        try:
2319
2556
            deferred_contents = []
2320
2557
            num = 0
2323
2560
                precomputed_delta = []
2324
2561
            else:
2325
2562
                precomputed_delta = None
 
2563
            # Check if tree inventory has content. If so, we populate
 
2564
            # existing_files with the directory content. If there are no
 
2565
            # entries we skip populating existing_files as its not used.
 
2566
            # This improves performance and unncessary work on large
 
2567
            # directory trees. (#501307)
 
2568
            if total > 0:
 
2569
                existing_files = set()
 
2570
                for dir, files in wt.walkdirs():
 
2571
                    existing_files.update(f[0] for f in files)
2326
2572
            for num, (tree_path, entry) in \
2327
2573
                enumerate(tree.inventory.iter_entries_by_dir()):
2328
2574
                pb.update("Building tree", num - len(deferred_contents), total)
2358
2604
                    executable = tree.is_executable(file_id, tree_path)
2359
2605
                    if executable:
2360
2606
                        tt.set_executability(executable, trans_id)
2361
 
                    trans_data = (trans_id, tree_path)
 
2607
                    trans_data = (trans_id, tree_path, entry.text_sha1)
2362
2608
                    deferred_contents.append((file_id, trans_data))
2363
2609
                else:
2364
2610
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
2380
2626
            precomputed_delta = None
2381
2627
        conflicts = cook_conflicts(raw_conflicts, tt)
2382
2628
        for conflict in conflicts:
2383
 
            warning(conflict)
 
2629
            trace.warning(unicode(conflict))
2384
2630
        try:
2385
2631
            wt.add_conflicts(conflicts)
2386
2632
        except errors.UnsupportedOperation:
2409
2655
        unchanged = dict(unchanged)
2410
2656
        new_desired_files = []
2411
2657
        count = 0
2412
 
        for file_id, (trans_id, tree_path) in desired_files:
 
2658
        for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2413
2659
            accelerator_path = unchanged.get(file_id)
2414
2660
            if accelerator_path is None:
2415
 
                new_desired_files.append((file_id, (trans_id, tree_path)))
 
2661
                new_desired_files.append((file_id,
 
2662
                    (trans_id, tree_path, text_sha1)))
2416
2663
                continue
2417
2664
            pb.update('Adding file contents', count + offset, total)
2418
2665
            if hardlink:
2425
2672
                    contents = filtered_output_bytes(contents, filters,
2426
2673
                        ContentFilterContext(tree_path, tree))
2427
2674
                try:
2428
 
                    tt.create_file(contents, trans_id)
 
2675
                    tt.create_file(contents, trans_id, sha1=text_sha1)
2429
2676
                finally:
2430
2677
                    try:
2431
2678
                        contents.close()
2434
2681
                        pass
2435
2682
            count += 1
2436
2683
        offset += count
2437
 
    for count, ((trans_id, tree_path), contents) in enumerate(
 
2684
    for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
2438
2685
            tree.iter_files_bytes(new_desired_files)):
2439
2686
        if wt.supports_content_filtering():
2440
2687
            filters = wt._content_filter_stack(tree_path)
2441
2688
            contents = filtered_output_bytes(contents, filters,
2442
2689
                ContentFilterContext(tree_path, tree))
2443
 
        tt.create_file(contents, trans_id)
 
2690
        tt.create_file(contents, trans_id, sha1=text_sha1)
2444
2691
        pb.update('Adding file contents', count + offset, total)
2445
2692
 
2446
2693
 
2448
2695
    for child in tt.iter_tree_children(old_parent):
2449
2696
        tt.adjust_path(tt.final_name(child), new_parent, child)
2450
2697
 
 
2698
 
2451
2699
def _reparent_transform_children(tt, old_parent, new_parent):
2452
2700
    by_parent = tt.by_parent()
2453
2701
    for child in by_parent[old_parent]:
2454
2702
        tt.adjust_path(tt.final_name(child), new_parent, child)
2455
2703
    return by_parent[old_parent]
2456
2704
 
 
2705
 
2457
2706
def _content_match(tree, entry, file_id, kind, target_path):
2458
2707
    if entry.kind != kind:
2459
2708
        return False
2460
2709
    if entry.kind == "directory":
2461
2710
        return True
2462
2711
    if entry.kind == "file":
2463
 
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2464
 
            return True
 
2712
        f = file(target_path, 'rb')
 
2713
        try:
 
2714
            if tree.get_file_text(file_id) == f.read():
 
2715
                return True
 
2716
        finally:
 
2717
            f.close()
2465
2718
    elif entry.kind == "symlink":
2466
2719
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
2467
2720
            return True
2519
2772
        raise errors.BadFileKindError(name, kind)
2520
2773
 
2521
2774
 
2522
 
@deprecated_function(deprecated_in((1, 9, 0)))
2523
 
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2524
 
    """Create new file contents according to an inventory entry.
2525
 
 
2526
 
    DEPRECATED.  Use create_from_tree instead.
2527
 
    """
2528
 
    if entry.kind == "file":
2529
 
        if lines is None:
2530
 
            lines = tree.get_file(entry.file_id).readlines()
2531
 
        tt.create_file(lines, trans_id, mode_id=mode_id)
2532
 
    elif entry.kind == "symlink":
2533
 
        tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2534
 
    elif entry.kind == "directory":
2535
 
        tt.create_directory(trans_id)
2536
 
 
2537
 
 
2538
2775
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2539
2776
    filter_tree_path=None):
2540
2777
    """Create new file contents according to tree contents.
2571
2808
        tt.set_executability(entry.executable, trans_id)
2572
2809
 
2573
2810
 
 
2811
@deprecated_function(deprecated_in((2, 3, 0)))
2574
2812
def get_backup_name(entry, by_parent, parent_trans_id, tt):
2575
2813
    return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
2576
2814
 
2577
2815
 
 
2816
@deprecated_function(deprecated_in((2, 3, 0)))
2578
2817
def _get_backup_name(name, by_parent, parent_trans_id, tt):
2579
2818
    """Produce a backup-style name that appears to be available"""
2580
2819
    def name_gen():
2629
2868
                unversioned_filter=working_tree.is_ignored)
2630
2869
            delta.report_changes(tt.iter_changes(), change_reporter)
2631
2870
        for conflict in conflicts:
2632
 
            warning(conflict)
 
2871
            trace.warning(unicode(conflict))
2633
2872
        pp.next_phase()
2634
2873
        tt.apply()
2635
2874
        working_tree.set_merge_modified(merge_modified)
2643
2882
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2644
2883
                              backups, pp, basis_tree=None,
2645
2884
                              merge_modified=None):
2646
 
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2885
    child_pb = ui.ui_factory.nested_progress_bar()
2647
2886
    try:
2648
2887
        if merge_modified is None:
2649
2888
            merge_modified = working_tree.merge_modified()
2652
2891
                                      merge_modified, basis_tree)
2653
2892
    finally:
2654
2893
        child_pb.finished()
2655
 
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2894
    child_pb = ui.ui_factory.nested_progress_bar()
2656
2895
    try:
2657
2896
        raw_conflicts = resolve_conflicts(tt, child_pb,
2658
2897
            lambda t, c: conflict_pass(t, c, target_tree))
2666
2905
                 backups, merge_modified, basis_tree=None):
2667
2906
    if basis_tree is not None:
2668
2907
        basis_tree.lock_read()
2669
 
    change_list = target_tree.iter_changes(working_tree,
 
2908
    # We ask the working_tree for its changes relative to the target, rather
 
2909
    # than the target changes relative to the working tree. Because WT4 has an
 
2910
    # optimizer to compare itself to a target, but no optimizer for the
 
2911
    # reverse.
 
2912
    change_list = working_tree.iter_changes(target_tree,
2670
2913
        specific_files=specific_files, pb=pb)
2671
2914
    if target_tree.get_root_id() is None:
2672
2915
        skip_root = True
2676
2919
        deferred_files = []
2677
2920
        for id_num, (file_id, path, changed_content, versioned, parent, name,
2678
2921
                kind, executable) in enumerate(change_list):
2679
 
            if skip_root and file_id[0] is not None and parent[0] is None:
 
2922
            target_path, wt_path = path
 
2923
            target_versioned, wt_versioned = versioned
 
2924
            target_parent, wt_parent = parent
 
2925
            target_name, wt_name = name
 
2926
            target_kind, wt_kind = kind
 
2927
            target_executable, wt_executable = executable
 
2928
            if skip_root and wt_parent is None:
2680
2929
                continue
2681
2930
            trans_id = tt.trans_id_file_id(file_id)
2682
2931
            mode_id = None
2683
2932
            if changed_content:
2684
2933
                keep_content = False
2685
 
                if kind[0] == 'file' and (backups or kind[1] is None):
 
2934
                if wt_kind == 'file' and (backups or target_kind is None):
2686
2935
                    wt_sha1 = working_tree.get_file_sha1(file_id)
2687
2936
                    if merge_modified.get(file_id) != wt_sha1:
2688
2937
                        # acquire the basis tree lazily to prevent the
2691
2940
                        if basis_tree is None:
2692
2941
                            basis_tree = working_tree.basis_tree()
2693
2942
                            basis_tree.lock_read()
2694
 
                        if file_id in basis_tree:
 
2943
                        if basis_tree.has_id(file_id):
2695
2944
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
2696
2945
                                keep_content = True
2697
 
                        elif kind[1] is None and not versioned[1]:
 
2946
                        elif target_kind is None and not target_versioned:
2698
2947
                            keep_content = True
2699
 
                if kind[0] is not None:
 
2948
                if wt_kind is not None:
2700
2949
                    if not keep_content:
2701
2950
                        tt.delete_contents(trans_id)
2702
 
                    elif kind[1] is not None:
2703
 
                        parent_trans_id = tt.trans_id_file_id(parent[0])
2704
 
                        by_parent = tt.by_parent()
2705
 
                        backup_name = _get_backup_name(name[0], by_parent,
2706
 
                                                       parent_trans_id, tt)
 
2951
                    elif target_kind is not None:
 
2952
                        parent_trans_id = tt.trans_id_file_id(wt_parent)
 
2953
                        backup_name = tt._available_backup_name(
 
2954
                            wt_name, parent_trans_id)
2707
2955
                        tt.adjust_path(backup_name, parent_trans_id, trans_id)
2708
 
                        new_trans_id = tt.create_path(name[0], parent_trans_id)
2709
 
                        if versioned == (True, True):
 
2956
                        new_trans_id = tt.create_path(wt_name, parent_trans_id)
 
2957
                        if wt_versioned and target_versioned:
2710
2958
                            tt.unversion_file(trans_id)
2711
2959
                            tt.version_file(file_id, new_trans_id)
2712
2960
                        # New contents should have the same unix perms as old
2713
2961
                        # contents
2714
2962
                        mode_id = trans_id
2715
2963
                        trans_id = new_trans_id
2716
 
                if kind[1] in ('directory', 'tree-reference'):
 
2964
                if target_kind in ('directory', 'tree-reference'):
2717
2965
                    tt.create_directory(trans_id)
2718
 
                    if kind[1] == 'tree-reference':
 
2966
                    if target_kind == 'tree-reference':
2719
2967
                        revision = target_tree.get_reference_revision(file_id,
2720
 
                                                                      path[1])
 
2968
                                                                      target_path)
2721
2969
                        tt.set_tree_reference(revision, trans_id)
2722
 
                elif kind[1] == 'symlink':
 
2970
                elif target_kind == 'symlink':
2723
2971
                    tt.create_symlink(target_tree.get_symlink_target(file_id),
2724
2972
                                      trans_id)
2725
 
                elif kind[1] == 'file':
 
2973
                elif target_kind == 'file':
2726
2974
                    deferred_files.append((file_id, (trans_id, mode_id)))
2727
2975
                    if basis_tree is None:
2728
2976
                        basis_tree = working_tree.basis_tree()
2729
2977
                        basis_tree.lock_read()
2730
2978
                    new_sha1 = target_tree.get_file_sha1(file_id)
2731
 
                    if (file_id in basis_tree and new_sha1 ==
2732
 
                        basis_tree.get_file_sha1(file_id)):
 
2979
                    if (basis_tree.has_id(file_id) and
 
2980
                        new_sha1 == basis_tree.get_file_sha1(file_id)):
2733
2981
                        if file_id in merge_modified:
2734
2982
                            del merge_modified[file_id]
2735
2983
                    else:
2736
2984
                        merge_modified[file_id] = new_sha1
2737
2985
 
2738
2986
                    # preserve the execute bit when backing up
2739
 
                    if keep_content and executable[0] == executable[1]:
2740
 
                        tt.set_executability(executable[1], trans_id)
2741
 
                elif kind[1] is not None:
2742
 
                    raise AssertionError(kind[1])
2743
 
            if versioned == (False, True):
 
2987
                    if keep_content and wt_executable == target_executable:
 
2988
                        tt.set_executability(target_executable, trans_id)
 
2989
                elif target_kind is not None:
 
2990
                    raise AssertionError(target_kind)
 
2991
            if not wt_versioned and target_versioned:
2744
2992
                tt.version_file(file_id, trans_id)
2745
 
            if versioned == (True, False):
 
2993
            if wt_versioned and not target_versioned:
2746
2994
                tt.unversion_file(trans_id)
2747
 
            if (name[1] is not None and
2748
 
                (name[0] != name[1] or parent[0] != parent[1])):
2749
 
                if name[1] == '' and parent[1] is None:
 
2995
            if (target_name is not None and
 
2996
                (wt_name != target_name or wt_parent != target_parent)):
 
2997
                if target_name == '' and target_parent is None:
2750
2998
                    parent_trans = ROOT_PARENT
2751
2999
                else:
2752
 
                    parent_trans = tt.trans_id_file_id(parent[1])
2753
 
                if parent[0] is None and versioned[0]:
2754
 
                    tt.adjust_root_path(name[1], parent_trans)
 
3000
                    parent_trans = tt.trans_id_file_id(target_parent)
 
3001
                if wt_parent is None and wt_versioned:
 
3002
                    tt.adjust_root_path(target_name, parent_trans)
2755
3003
                else:
2756
 
                    tt.adjust_path(name[1], parent_trans, trans_id)
2757
 
            if executable[0] != executable[1] and kind[1] == "file":
2758
 
                tt.set_executability(executable[1], trans_id)
 
3004
                    tt.adjust_path(target_name, parent_trans, trans_id)
 
3005
            if wt_executable != target_executable and target_kind == "file":
 
3006
                tt.set_executability(target_executable, trans_id)
2759
3007
        if working_tree.supports_content_filtering():
2760
3008
            for index, ((trans_id, mode_id), bytes) in enumerate(
2761
3009
                target_tree.iter_files_bytes(deferred_files)):
2832
3080
 
2833
3081
        elif c_type == 'missing parent':
2834
3082
            trans_id = conflict[1]
2835
 
            try:
2836
 
                tt.cancel_deletion(trans_id)
2837
 
                new_conflicts.add(('deleting parent', 'Not deleting',
2838
 
                                   trans_id))
2839
 
            except KeyError:
 
3083
            if trans_id in tt._removed_contents:
 
3084
                cancel_deletion = True
 
3085
                orphans = tt._get_potential_orphans(trans_id)
 
3086
                if orphans:
 
3087
                    cancel_deletion = False
 
3088
                    # All children are orphans
 
3089
                    for o in orphans:
 
3090
                        try:
 
3091
                            tt.new_orphan(o, trans_id)
 
3092
                        except OrphaningError:
 
3093
                            # Something bad happened so we cancel the directory
 
3094
                            # deletion which will leave it in place with a
 
3095
                            # conflict. The user can deal with it from there.
 
3096
                            # Note that this also catch the case where we don't
 
3097
                            # want to create orphans and leave the directory in
 
3098
                            # place.
 
3099
                            cancel_deletion = True
 
3100
                            break
 
3101
                if cancel_deletion:
 
3102
                    # Cancel the directory deletion
 
3103
                    tt.cancel_deletion(trans_id)
 
3104
                    new_conflicts.add(('deleting parent', 'Not deleting',
 
3105
                                       trans_id))
 
3106
            else:
2840
3107
                create = True
2841
3108
                try:
2842
3109
                    tt.final_name(trans_id)
2845
3112
                        file_id = tt.final_file_id(trans_id)
2846
3113
                        if file_id is None:
2847
3114
                            file_id = tt.inactive_file_id(trans_id)
2848
 
                        entry = path_tree.inventory[file_id]
 
3115
                        _, entry = path_tree.iter_entries_by_dir(
 
3116
                            [file_id]).next()
2849
3117
                        # special-case the other tree root (move its
2850
3118
                        # children to current root)
2851
3119
                        if entry.parent_id is None:
2866
3134
        elif c_type == 'unversioned parent':
2867
3135
            file_id = tt.inactive_file_id(conflict[1])
2868
3136
            # special-case the other tree root (move its children instead)
2869
 
            if path_tree and file_id in path_tree:
2870
 
                if path_tree.inventory[file_id].parent_id is None:
 
3137
            if path_tree and path_tree.has_id(file_id):
 
3138
                if path_tree.path2id('') == file_id:
 
3139
                    # This is the root entry, skip it
2871
3140
                    continue
2872
3141
            tt.version_file(file_id, conflict[1])
2873
3142
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
2889
3158
 
2890
3159
def cook_conflicts(raw_conflicts, tt):
2891
3160
    """Generate a list of cooked conflicts, sorted by file path"""
2892
 
    from bzrlib.conflicts import Conflict
2893
3161
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
2894
 
    return sorted(conflict_iter, key=Conflict.sort_key)
 
3162
    return sorted(conflict_iter, key=conflicts.Conflict.sort_key)
2895
3163
 
2896
3164
 
2897
3165
def iter_cook_conflicts(raw_conflicts, tt):
2898
 
    from bzrlib.conflicts import Conflict
2899
3166
    fp = FinalPaths(tt)
2900
3167
    for conflict in raw_conflicts:
2901
3168
        c_type = conflict[0]
2903
3170
        modified_path = fp.get_path(conflict[2])
2904
3171
        modified_id = tt.final_file_id(conflict[2])
2905
3172
        if len(conflict) == 3:
2906
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
2907
 
                                     file_id=modified_id)
 
3173
            yield conflicts.Conflict.factory(
 
3174
                c_type, action=action, path=modified_path, file_id=modified_id)
2908
3175
 
2909
3176
        else:
2910
3177
            conflicting_path = fp.get_path(conflict[3])
2911
3178
            conflicting_id = tt.final_file_id(conflict[3])
2912
 
            yield Conflict.factory(c_type, action=action, path=modified_path,
2913
 
                                   file_id=modified_id,
2914
 
                                   conflict_path=conflicting_path,
2915
 
                                   conflict_file_id=conflicting_id)
 
3179
            yield conflicts.Conflict.factory(
 
3180
                c_type, action=action, path=modified_path,
 
3181
                file_id=modified_id,
 
3182
                conflict_path=conflicting_path,
 
3183
                conflict_file_id=conflicting_id)
2916
3184
 
2917
3185
 
2918
3186
class _FileMover(object):
2925
3193
    def rename(self, from_, to):
2926
3194
        """Rename a file from one path to another."""
2927
3195
        try:
2928
 
            osutils.rename(from_, to)
 
3196
            os.rename(from_, to)
2929
3197
        except OSError, e:
2930
3198
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2931
3199
                raise errors.FileExists(to, str(e))
2932
 
            raise
 
3200
            # normal OSError doesn't include filenames so it's hard to see where
 
3201
            # the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
 
3202
            raise errors.TransformRenameFailed(from_, to, str(e), e.errno)
2933
3203
        self.past_renames.append((from_, to))
2934
3204
 
2935
3205
    def pre_delete(self, from_, to):
2945
3215
    def rollback(self):
2946
3216
        """Reverse all renames that have been performed"""
2947
3217
        for from_, to in reversed(self.past_renames):
2948
 
            osutils.rename(to, from_)
 
3218
            try:
 
3219
                os.rename(to, from_)
 
3220
            except OSError, e:
 
3221
                raise errors.TransformRenameFailed(to, from_, str(e), e.errno)
2949
3222
        # after rollback, don't reuse _FileMover
2950
3223
        past_renames = None
2951
3224
        pending_deletions = None