/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 bzrlib/transform.py

Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
import errno
19
19
from stat import S_ISREG
20
20
 
21
 
from bzrlib import bzrdir, errors
 
21
from bzrlib.lazy_import import lazy_import
 
22
lazy_import(globals(), """
 
23
from bzrlib import (
 
24
    bzrdir,
 
25
    delta,
 
26
    errors,
 
27
    inventory
 
28
    )
 
29
""")
22
30
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
23
31
                           ReusingTransform, NotVersionedError, CantMoveRoot,
24
32
                           ExistingLimbo, ImmortalLimbo, NoFinalPath)
26
34
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
27
35
                            delete_any)
28
36
from bzrlib.progress import DummyProgress, ProgressPhase
 
37
from bzrlib.symbol_versioning import deprecated_function, zero_fifteen
29
38
from bzrlib.trace import mutter, warning
30
39
from bzrlib import tree
31
 
import bzrlib.ui 
 
40
import bzrlib.ui
32
41
import bzrlib.urlutils as urlutils
33
42
 
34
43
 
99
108
        self._new_contents = {}
100
109
        self._removed_contents = set()
101
110
        self._new_executability = {}
 
111
        self._new_reference_revision = {}
102
112
        self._new_id = {}
103
113
        self._non_present_ids = {}
104
114
        self._r_new_id = {}
273
283
                os.unlink(name)
274
284
                raise
275
285
 
276
 
            for segment in contents:
277
 
                f.write(segment)
 
286
            f.writelines(contents)
278
287
        finally:
279
288
            f.close()
280
289
        self._set_mode(trans_id, mode_id, S_ISREG)
350
359
        else:
351
360
            unique_add(self._new_executability, trans_id, executability)
352
361
 
 
362
    def set_tree_reference(self, revision_id, trans_id):
 
363
        """Set the reference associated with a directory"""
 
364
        unique_add(self._new_reference_revision, trans_id, revision_id)
 
365
 
353
366
    def version_file(self, file_id, trans_id):
354
367
        """Schedule a file to become versioned."""
355
368
        assert file_id is not None
725
738
        conflicts = self.find_conflicts()
726
739
        if len(conflicts) != 0:
727
740
            raise MalformedTransform(conflicts=conflicts)
728
 
        limbo_inv = {}
729
741
        inv = self._tree.inventory
 
742
        inventory_delta = []
730
743
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
731
744
        try:
732
745
            child_pb.update('Apply phase', 0, 2)
733
 
            self._apply_removals(inv, limbo_inv)
 
746
            self._apply_removals(inv, inventory_delta)
734
747
            child_pb.update('Apply phase', 1, 2)
735
 
            modified_paths = self._apply_insertions(inv, limbo_inv)
 
748
            modified_paths = self._apply_insertions(inv, inventory_delta)
736
749
        finally:
737
750
            child_pb.finished()
738
 
        self._tree._write_inventory(inv)
 
751
        self._tree.apply_inventory_delta(inventory_delta)
739
752
        self.__done = True
740
753
        self.finalize()
741
754
        return _TransformResults(modified_paths)
744
757
        """Generate the limbo name of a file"""
745
758
        return pathjoin(self._limbodir, trans_id)
746
759
 
747
 
    def _apply_removals(self, inv, limbo_inv):
 
760
    def _apply_removals(self, inv, inventory_delta):
748
761
        """Perform tree operations that remove directory/inventory names.
749
762
        
750
763
        That is, delete files that are to be deleted, and put any files that
773
786
                        file_id = self._tree.inventory.root.file_id
774
787
                    else:
775
788
                        file_id = self.tree_file_id(trans_id)
776
 
                    del inv[file_id]
777
 
                elif trans_id in self._new_name or trans_id in self._new_parent:
778
 
                    file_id = self.tree_file_id(trans_id)
779
 
                    if file_id is not None:
780
 
                        limbo_inv[trans_id] = inv[file_id]
781
 
                        del inv[file_id]
 
789
                    assert file_id is not None
 
790
                    inventory_delta.append((path, None, file_id, None))
782
791
        finally:
783
792
            child_pb.finished()
784
793
 
785
 
    def _apply_insertions(self, inv, limbo_inv):
 
794
    def _apply_insertions(self, inv, inventory_delta):
786
795
        """Perform tree operations that insert directory/inventory names.
787
796
        
788
797
        That is, create any files that need to be created, and restore from
794
803
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
795
804
        try:
796
805
            for num, (path, trans_id) in enumerate(new_paths):
 
806
                new_entry = None
797
807
                child_pb.update('adding file', num, len(new_paths))
798
808
                try:
799
809
                    kind = self._new_contents[trans_id]
815
825
                if trans_id in self._new_id:
816
826
                    if kind is None:
817
827
                        kind = file_kind(self._tree.abspath(path))
818
 
                    inv.add_path(path, kind, self._new_id[trans_id])
819
 
                elif trans_id in self._new_name or trans_id in\
820
 
                    self._new_parent:
821
 
                    entry = limbo_inv.get(trans_id)
822
 
                    if entry is not None:
823
 
                        entry.name = self.final_name(trans_id)
824
 
                        parent_path = os.path.dirname(path)
825
 
                        entry.parent_id = \
826
 
                            self._tree.inventory.path2id(parent_path)
827
 
                        inv.add(entry)
828
 
 
829
 
                # requires files and inventory entries to be in place
 
828
                    if trans_id in self._new_reference_revision:
 
829
                        new_entry = inventory.TreeReference(
 
830
                            self._new_id[trans_id],
 
831
                            self._new_name[trans_id], 
 
832
                            self.final_file_id(self._new_parent[trans_id]),
 
833
                            None, self._new_reference_revision[trans_id])
 
834
                    else:
 
835
                        new_entry = inventory.make_entry(kind,
 
836
                            self.final_name(trans_id),
 
837
                            self.final_file_id(self.final_parent(trans_id)),
 
838
                            self._new_id[trans_id])
 
839
                else:
 
840
                    if trans_id in self._new_name or trans_id in\
 
841
                        self._new_parent or\
 
842
                        trans_id in self._new_executability:
 
843
                        file_id = self.final_file_id(trans_id)
 
844
                        if file_id is not None:
 
845
                            entry = inv[file_id]
 
846
                            new_entry = entry.copy()
 
847
 
 
848
                    if trans_id in self._new_name or trans_id in\
 
849
                        self._new_parent:
 
850
                            if new_entry is not None:
 
851
                                new_entry.name = self.final_name(trans_id)
 
852
                                parent = self.final_parent(trans_id)
 
853
                                parent_id = self.final_file_id(parent)
 
854
                                new_entry.parent_id = parent_id
 
855
 
830
856
                if trans_id in self._new_executability:
831
 
                    self._set_executability(path, inv, trans_id)
 
857
                    self._set_executability(path, new_entry, trans_id)
 
858
                if new_entry is not None:
 
859
                    if new_entry.file_id in inv:
 
860
                        old_path = inv.id2path(new_entry.file_id)
 
861
                    else:
 
862
                        old_path = None
 
863
                    inventory_delta.append((old_path, path,
 
864
                                            new_entry.file_id,
 
865
                                            new_entry))
832
866
        finally:
833
867
            child_pb.finished()
834
868
        return modified_paths
835
869
 
836
 
    def _set_executability(self, path, inv, trans_id):
 
870
    def _set_executability(self, path, entry, trans_id):
837
871
        """Set the executability of versioned files """
838
 
        file_id = inv.path2id(path)
839
872
        new_executability = self._new_executability[trans_id]
840
 
        inv[file_id].executable = new_executability
 
873
        entry.executable = new_executability
841
874
        if supports_executable():
842
875
            abspath = self._tree.abspath(path)
843
876
            current_mode = os.stat(abspath).st_mode
904
937
        self.create_symlink(target, trans_id)
905
938
        return trans_id
906
939
 
 
940
    def _affected_ids(self):
 
941
        """Return the set of transform ids affected by the transform"""
 
942
        trans_ids = set(self._removed_id)
 
943
        trans_ids.update(self._new_id.keys())
 
944
        trans_ids.update(self._removed_contents)
 
945
        trans_ids.update(self._new_contents.keys())
 
946
        trans_ids.update(self._new_executability.keys())
 
947
        trans_ids.update(self._new_name.keys())
 
948
        trans_ids.update(self._new_parent.keys())
 
949
        return trans_ids
 
950
 
 
951
    def _get_file_id_maps(self):
 
952
        """Return mapping of file_ids to trans_ids in the to and from states"""
 
953
        trans_ids = self._affected_ids()
 
954
        from_trans_ids = {}
 
955
        to_trans_ids = {}
 
956
        # Build up two dicts: trans_ids associated with file ids in the
 
957
        # FROM state, vs the TO state.
 
958
        for trans_id in trans_ids:
 
959
            from_file_id = self.tree_file_id(trans_id)
 
960
            if from_file_id is not None:
 
961
                from_trans_ids[from_file_id] = trans_id
 
962
            to_file_id = self.final_file_id(trans_id)
 
963
            if to_file_id is not None:
 
964
                to_trans_ids[to_file_id] = trans_id
 
965
        return from_trans_ids, to_trans_ids
 
966
 
 
967
    def _from_file_data(self, from_trans_id, from_versioned, file_id):
 
968
        """Get data about a file in the from (tree) state
 
969
 
 
970
        Return a (name, parent, kind, executable) tuple
 
971
        """
 
972
        from_path = self._tree_id_paths.get(from_trans_id)
 
973
        if from_versioned:
 
974
            # get data from working tree if versioned
 
975
            from_entry = self._tree.inventory[file_id]
 
976
            from_name = from_entry.name
 
977
            from_parent = from_entry.parent_id
 
978
        else:
 
979
            from_entry = None
 
980
            if from_path is None:
 
981
                # File does not exist in FROM state
 
982
                from_name = None
 
983
                from_parent = None
 
984
            else:
 
985
                # File exists, but is not versioned.  Have to use path-
 
986
                # splitting stuff
 
987
                from_name = os.path.basename(from_path)
 
988
                tree_parent = self.get_tree_parent(from_trans_id)
 
989
                from_parent = self.tree_file_id(tree_parent)
 
990
        if from_path is not None:
 
991
            from_kind, from_executable, from_stats = \
 
992
                self._tree._comparison_data(from_entry, from_path)
 
993
        else:
 
994
            from_kind = None
 
995
            from_executable = False
 
996
        return from_name, from_parent, from_kind, from_executable
 
997
 
 
998
    def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
 
999
        """Get data about a file in the to (target) state
 
1000
 
 
1001
        Return a (name, parent, kind, executable) tuple
 
1002
        """
 
1003
        to_name = self.final_name(to_trans_id)
 
1004
        try:
 
1005
            to_kind = self.final_kind(to_trans_id)
 
1006
        except NoSuchFile:
 
1007
            to_kind = None
 
1008
        to_parent = self.final_file_id(self.final_parent(to_trans_id))
 
1009
        if to_trans_id in self._new_executability:
 
1010
            to_executable = self._new_executability[to_trans_id]
 
1011
        elif to_trans_id == from_trans_id:
 
1012
            to_executable = from_executable
 
1013
        else:
 
1014
            to_executable = False
 
1015
        return to_name, to_parent, to_kind, to_executable
 
1016
 
 
1017
    def _iter_changes(self):
 
1018
        """Produce output in the same format as Tree._iter_changes.
 
1019
 
 
1020
        Will produce nonsensical results if invoked while inventory/filesystem
 
1021
        conflicts (as reported by TreeTransform.find_conflicts()) are present.
 
1022
 
 
1023
        This reads the Transform, but only reproduces changes involving a
 
1024
        file_id.  Files that are not versioned in either of the FROM or TO
 
1025
        states are not reflected.
 
1026
        """
 
1027
        final_paths = FinalPaths(self)
 
1028
        from_trans_ids, to_trans_ids = self._get_file_id_maps()
 
1029
        results = []
 
1030
        # Now iterate through all active file_ids
 
1031
        for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
 
1032
            modified = False
 
1033
            from_trans_id = from_trans_ids.get(file_id)
 
1034
            # find file ids, and determine versioning state
 
1035
            if from_trans_id is None:
 
1036
                from_versioned = False
 
1037
                from_trans_id = to_trans_ids[file_id]
 
1038
            else:
 
1039
                from_versioned = True
 
1040
            to_trans_id = to_trans_ids.get(file_id)
 
1041
            if to_trans_id is None:
 
1042
                to_versioned = False
 
1043
                to_trans_id = from_trans_id
 
1044
            else:
 
1045
                to_versioned = True
 
1046
 
 
1047
            from_name, from_parent, from_kind, from_executable = \
 
1048
                self._from_file_data(from_trans_id, from_versioned, file_id)
 
1049
 
 
1050
            to_name, to_parent, to_kind, to_executable = \
 
1051
                self._to_file_data(to_trans_id, from_trans_id, from_executable)
 
1052
 
 
1053
            if not from_versioned:
 
1054
                from_path = None
 
1055
            else:
 
1056
                from_path = self._tree_id_paths.get(from_trans_id)
 
1057
            if not to_versioned:
 
1058
                to_path = None
 
1059
            else:
 
1060
                to_path = final_paths.get_path(to_trans_id)
 
1061
            if from_kind != to_kind:
 
1062
                modified = True
 
1063
            elif to_kind in ('file', 'symlink') and (
 
1064
                to_trans_id != from_trans_id or
 
1065
                to_trans_id in self._new_contents):
 
1066
                modified = True
 
1067
            if (not modified and from_versioned == to_versioned and
 
1068
                from_parent==to_parent and from_name == to_name and
 
1069
                from_executable == to_executable):
 
1070
                continue
 
1071
            results.append((file_id, (from_path, to_path), modified,
 
1072
                   (from_versioned, to_versioned),
 
1073
                   (from_parent, to_parent),
 
1074
                   (from_name, to_name),
 
1075
                   (from_kind, to_kind),
 
1076
                   (from_executable, to_executable)))
 
1077
        return iter(sorted(results, key=lambda x:x[1]))
 
1078
 
 
1079
 
907
1080
def joinpath(parent, child):
908
1081
    """Join tree-relative paths, handling the tree root specially"""
909
1082
    if parent is None or parent == "":
960
1133
      it is silently replaced.
961
1134
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
962
1135
    """
 
1136
    wt.lock_tree_write()
 
1137
    try:
 
1138
        tree.lock_read()
 
1139
        try:
 
1140
            return _build_tree(tree, wt)
 
1141
        finally:
 
1142
            tree.unlock()
 
1143
    finally:
 
1144
        wt.unlock()
 
1145
 
 
1146
def _build_tree(tree, wt):
 
1147
    """See build_tree."""
963
1148
    if len(wt.inventory) > 1:  # more than just a root
964
1149
        raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
965
1150
    file_trans_id = {}
966
1151
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
967
1152
    pp = ProgressPhase("Build phase", 2, top_pb)
968
1153
    if tree.inventory.root is not None:
969
 
        wt.set_root_id(tree.inventory.root.file_id)
 
1154
        # this is kindof a hack: we should be altering the root 
 
1155
        # as partof the regular tree shape diff logic.
 
1156
        # the conditional test hereis to avoid doing an
 
1157
        # expensive operation (flush) every time the root id
 
1158
        # is set within the tree, nor setting the root and thus
 
1159
        # marking the tree as dirty, because we use two different
 
1160
        # idioms here: tree interfaces and inventory interfaces.
 
1161
        if wt.path2id('') != tree.inventory.root.file_id:
 
1162
            wt.set_root_id(tree.inventory.root.file_id)
 
1163
            wt.flush()
970
1164
    tt = TreeTransform(wt)
971
1165
    divert = set()
972
1166
    try:
1002
1196
                        if kind == 'directory':
1003
1197
                            reparent = True
1004
1198
                if entry.parent_id not in file_trans_id:
1005
 
                    raise repr(entry.parent_id)
 
1199
                    raise AssertionError(
 
1200
                        'entry %s parent id %r is not in file_trans_id %r'
 
1201
                        % (entry, entry.parent_id, file_trans_id))
1006
1202
                parent_id = file_trans_id[entry.parent_id]
1007
1203
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1008
1204
                                                      tree)
1086
1282
        executable = tree.is_executable(entry.file_id)
1087
1283
        return tt.new_file(name, parent_id, contents, entry.file_id, 
1088
1284
                           executable)
1089
 
    elif kind == 'directory':
1090
 
        return tt.new_directory(name, parent_id, entry.file_id)
 
1285
    elif kind in ('directory', 'tree-reference'):
 
1286
        trans_id = tt.new_directory(name, parent_id, entry.file_id)
 
1287
        if kind == 'tree-reference':
 
1288
            tt.set_tree_reference(entry.reference_revision, trans_id)
 
1289
        return trans_id 
1091
1290
    elif kind == 'symlink':
1092
1291
        target = tree.get_symlink_target(entry.file_id)
1093
1292
        return tt.new_symlink(name, parent_id, target, entry.file_id)
 
1293
    else:
 
1294
        raise errors.BadFileKindError(name, kind)
1094
1295
 
1095
1296
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1096
1297
    """Create new file contents according to an inventory entry."""
1109
1310
        tt.set_executability(entry.executable, trans_id)
1110
1311
 
1111
1312
 
 
1313
@deprecated_function(zero_fifteen)
1112
1314
def find_interesting(working_tree, target_tree, filenames):
1113
 
    """Find the ids corresponding to specified filenames."""
1114
 
    trees = (working_tree, target_tree)
1115
 
    return tree.find_ids_across_trees(filenames, trees)
 
1315
    """Find the ids corresponding to specified filenames.
 
1316
    
 
1317
    Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
 
1318
    """
 
1319
    working_tree.lock_read()
 
1320
    try:
 
1321
        target_tree.lock_read()
 
1322
        try:
 
1323
            return working_tree.paths2ids(filenames, [target_tree])
 
1324
        finally:
 
1325
            target_tree.unlock()
 
1326
    finally:
 
1327
        working_tree.unlock()
1116
1328
 
1117
1329
 
1118
1330
def change_entry(tt, file_id, working_tree, target_tree, 
1198
1410
    return has_contents, contents_mod, meta_mod
1199
1411
 
1200
1412
 
1201
 
def revert(working_tree, target_tree, filenames, backups=False, 
1202
 
           pb=DummyProgress()):
 
1413
def revert(working_tree, target_tree, filenames, backups=False,
 
1414
           pb=DummyProgress(), change_reporter=None):
1203
1415
    """Revert a working tree's contents to those of a target tree."""
1204
 
    interesting_ids = find_interesting(working_tree, target_tree, filenames)
 
1416
    target_tree.lock_read()
1205
1417
    tt = TreeTransform(working_tree, pb)
1206
1418
    try:
1207
1419
        pp = ProgressPhase("Revert phase", 3, pb)
1208
1420
        pp.next_phase()
1209
1421
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1210
1422
        try:
1211
 
            _alter_files(working_tree, target_tree, tt, child_pb, 
1212
 
                         interesting_ids, backups)
 
1423
            _alter_files(working_tree, target_tree, tt, child_pb,
 
1424
                         filenames, backups)
1213
1425
        finally:
1214
1426
            child_pb.finished()
1215
1427
        pp.next_phase()
1219
1431
        finally:
1220
1432
            child_pb.finished()
1221
1433
        conflicts = cook_conflicts(raw_conflicts, tt)
 
1434
        if change_reporter:
 
1435
            change_reporter = delta._ChangeReporter(
 
1436
                unversioned_filter=working_tree.is_ignored)
 
1437
            delta.report_changes(tt._iter_changes(), change_reporter)
1222
1438
        for conflict in conflicts:
1223
1439
            warning(conflict)
1224
1440
        pp.next_phase()
1225
1441
        tt.apply()
1226
1442
        working_tree.set_merge_modified({})
1227
1443
    finally:
 
1444
        target_tree.unlock()
1228
1445
        tt.finalize()
1229
1446
        pb.clear()
1230
1447
    return conflicts
1231
1448
 
1232
1449
 
1233
 
def _alter_files(working_tree, target_tree, tt, pb, interesting_ids, backups):
 
1450
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
 
1451
                 backups):
1234
1452
    merge_modified = working_tree.merge_modified()
1235
 
    iterator = target_tree._iter_changes(working_tree, 
1236
 
                                         specific_file_ids=interesting_ids,
1237
 
                                         pb=pb)
 
1453
    change_list = target_tree._iter_changes(working_tree,
 
1454
        specific_files=specific_files, pb=pb)
1238
1455
    if target_tree.inventory.root is None:
1239
1456
        skip_root = True
1240
1457
    else:
1241
1458
        skip_root = False
1242
1459
    basis_tree = None
1243
 
    for id_num, (file_id, path, changed_content, versioned, parent, name, kind,
1244
 
                 executable) in enumerate(iterator):
1245
 
        if skip_root and file_id[0] is not None and parent[0] is None:
1246
 
            continue
1247
 
        trans_id = tt.trans_id_file_id(file_id)
1248
 
        mode_id = None
1249
 
        if changed_content:
1250
 
            keep_content = False
1251
 
            if kind[0] == 'file' and (backups or kind[1] is None):
1252
 
                wt_sha1 = working_tree.get_file_sha1(file_id)
1253
 
                if merge_modified.get(file_id) != wt_sha1:
1254
 
                    if basis_tree is None:
1255
 
                        basis_tree = working_tree.basis_tree()
1256
 
                    if file_id in basis_tree:
1257
 
                        if wt_sha1 != basis_tree.get_file_sha1(file_id):
 
1460
    try:
 
1461
        for id_num, (file_id, path, changed_content, versioned, parent, name,
 
1462
                kind, executable) in enumerate(change_list):
 
1463
            if skip_root and file_id[0] is not None and parent[0] is None:
 
1464
                continue
 
1465
            trans_id = tt.trans_id_file_id(file_id)
 
1466
            mode_id = None
 
1467
            if changed_content:
 
1468
                keep_content = False
 
1469
                if kind[0] == 'file' and (backups or kind[1] is None):
 
1470
                    wt_sha1 = working_tree.get_file_sha1(file_id)
 
1471
                    if merge_modified.get(file_id) != wt_sha1:
 
1472
                        # acquire the basis tree lazyily to prevent the expense
 
1473
                        # of accessing it when its not needed ? (Guessing, RBC,
 
1474
                        # 200702)
 
1475
                        if basis_tree is None:
 
1476
                            basis_tree = working_tree.basis_tree()
 
1477
                            basis_tree.lock_read()
 
1478
                        if file_id in basis_tree:
 
1479
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
 
1480
                                keep_content = True
 
1481
                        elif kind[1] is None and not versioned[1]:
1258
1482
                            keep_content = True
1259
 
                    elif kind[1] is None and not versioned[1]:
1260
 
                        keep_content = True
1261
 
            if kind[0] is not None:
1262
 
                if not keep_content:
1263
 
                    tt.delete_contents(trans_id)
1264
 
                elif kind[1] is not None:
1265
 
                    parent_trans_id = tt.trans_id_file_id(parent[0])
1266
 
                    by_parent = tt.by_parent()
1267
 
                    backup_name = _get_backup_name(name[0], by_parent,
1268
 
                                                   parent_trans_id, tt)
1269
 
                    tt.adjust_path(backup_name, parent_trans_id, trans_id)
1270
 
                    new_trans_id = tt.create_path(name[0], parent_trans_id)
1271
 
                    if versioned == (True, True):
1272
 
                        tt.unversion_file(trans_id)
1273
 
                        tt.version_file(file_id, new_trans_id)
1274
 
                    # New contents should have the same unix perms as old
1275
 
                    # contents
1276
 
                    mode_id = trans_id
1277
 
                    trans_id = new_trans_id
1278
 
            if kind[1] == 'directory':
1279
 
                tt.create_directory(trans_id)
1280
 
            elif kind[1] == 'symlink':
1281
 
                tt.create_symlink(target_tree.get_symlink_target(file_id),
1282
 
                                  trans_id)
1283
 
            elif kind[1] == 'file':
1284
 
                tt.create_file(target_tree.get_file_lines(file_id),
1285
 
                               trans_id, mode_id)
1286
 
                # preserve the execute bit when backing up
1287
 
                if keep_content and executable[0] == executable[1]:
1288
 
                    tt.set_executability(executable[1], trans_id)
1289
 
            else:
1290
 
                assert kind[1] is None
1291
 
        if versioned == (False, True):
1292
 
            tt.version_file(file_id, trans_id)
1293
 
        if versioned == (True, False):
1294
 
            tt.unversion_file(trans_id)
1295
 
        if (name[1] is not None and 
1296
 
            (name[0] != name[1] or parent[0] != parent[1])):
1297
 
            tt.adjust_path(name[1], tt.trans_id_file_id(parent[1]), trans_id)
1298
 
        if executable[0] != executable[1] and kind[1] == "file":
1299
 
            tt.set_executability(executable[1], trans_id)
 
1483
                if kind[0] is not None:
 
1484
                    if not keep_content:
 
1485
                        tt.delete_contents(trans_id)
 
1486
                    elif kind[1] is not None:
 
1487
                        parent_trans_id = tt.trans_id_file_id(parent[0])
 
1488
                        by_parent = tt.by_parent()
 
1489
                        backup_name = _get_backup_name(name[0], by_parent,
 
1490
                                                       parent_trans_id, tt)
 
1491
                        tt.adjust_path(backup_name, parent_trans_id, trans_id)
 
1492
                        new_trans_id = tt.create_path(name[0], parent_trans_id)
 
1493
                        if versioned == (True, True):
 
1494
                            tt.unversion_file(trans_id)
 
1495
                            tt.version_file(file_id, new_trans_id)
 
1496
                        # New contents should have the same unix perms as old
 
1497
                        # contents
 
1498
                        mode_id = trans_id
 
1499
                        trans_id = new_trans_id
 
1500
                if kind[1] == 'directory':
 
1501
                    tt.create_directory(trans_id)
 
1502
                elif kind[1] == 'symlink':
 
1503
                    tt.create_symlink(target_tree.get_symlink_target(file_id),
 
1504
                                      trans_id)
 
1505
                elif kind[1] == 'file':
 
1506
                    tt.create_file(target_tree.get_file_lines(file_id),
 
1507
                                   trans_id, mode_id)
 
1508
                    # preserve the execute bit when backing up
 
1509
                    if keep_content and executable[0] == executable[1]:
 
1510
                        tt.set_executability(executable[1], trans_id)
 
1511
                else:
 
1512
                    assert kind[1] is None
 
1513
            if versioned == (False, True):
 
1514
                tt.version_file(file_id, trans_id)
 
1515
            if versioned == (True, False):
 
1516
                tt.unversion_file(trans_id)
 
1517
            if (name[1] is not None and 
 
1518
                (name[0] != name[1] or parent[0] != parent[1])):
 
1519
                tt.adjust_path(
 
1520
                    name[1], tt.trans_id_file_id(parent[1]), trans_id)
 
1521
            if executable[0] != executable[1] and kind[1] == "file":
 
1522
                tt.set_executability(executable[1], trans_id)
 
1523
    finally:
 
1524
        if basis_tree is not None:
 
1525
            basis_tree.unlock()
1300
1526
 
1301
1527
 
1302
1528
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):