~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-07 10:45:44 UTC
  • mfrom: (2321.1.2 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070307104544-59e3e6358e4bdb29
(robertc) Merge dirstate and subtrees. (Robert Collins, Martin Pool, Aaaron Bentley, John A Meinel, James Westby)

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
22
21
from bzrlib.lazy_import import lazy_import
23
22
lazy_import(globals(), """
24
 
from bzrlib import delta
 
23
from bzrlib import (
 
24
    bzrdir,
 
25
    delta,
 
26
    errors,
 
27
    inventory
 
28
    )
25
29
""")
26
30
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
27
31
                           ReusingTransform, NotVersionedError, CantMoveRoot,
30
34
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
31
35
                            delete_any)
32
36
from bzrlib.progress import DummyProgress, ProgressPhase
 
37
from bzrlib.symbol_versioning import deprecated_function, zero_fifteen
33
38
from bzrlib.trace import mutter, warning
34
39
from bzrlib import tree
35
 
import bzrlib.ui 
 
40
import bzrlib.ui
36
41
import bzrlib.urlutils as urlutils
37
42
 
38
43
 
103
108
        self._new_contents = {}
104
109
        self._removed_contents = set()
105
110
        self._new_executability = {}
 
111
        self._new_reference_revision = {}
106
112
        self._new_id = {}
107
113
        self._non_present_ids = {}
108
114
        self._r_new_id = {}
353
359
        else:
354
360
            unique_add(self._new_executability, trans_id, executability)
355
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
 
356
366
    def version_file(self, file_id, trans_id):
357
367
        """Schedule a file to become versioned."""
358
368
        assert file_id is not None
781
791
                    file_id = self.tree_file_id(trans_id)
782
792
                    if file_id is not None:
783
793
                        limbo_inv[trans_id] = inv[file_id]
784
 
                        del inv[file_id]
 
794
                        inv.remove_recursive_id(file_id)
785
795
        finally:
786
796
            child_pb.finished()
787
797
 
818
828
                if trans_id in self._new_id:
819
829
                    if kind is None:
820
830
                        kind = file_kind(self._tree.abspath(path))
821
 
                    inv.add_path(path, kind, self._new_id[trans_id])
 
831
                    if trans_id in self._new_reference_revision:
 
832
                        entry = inventory.TreeReference(self._new_id[trans_id], 
 
833
                            self._new_name[trans_id], 
 
834
                            self.final_file_id(self._new_parent[trans_id]),
 
835
                            None, self._new_reference_revision[trans_id])
 
836
                        inv.add(entry)
 
837
                    else:
 
838
                        inv.add_path(path, kind, self._new_id[trans_id])
822
839
                elif trans_id in self._new_name or trans_id in\
823
840
                    self._new_parent:
824
841
                    entry = limbo_inv.get(trans_id)
1020
1037
            to_name, to_parent, to_kind, to_executable = \
1021
1038
                self._to_file_data(to_trans_id, from_trans_id, from_executable)
1022
1039
 
1023
 
            to_path = final_paths.get_path(to_trans_id)
 
1040
            if not from_versioned:
 
1041
                from_path = None
 
1042
            else:
 
1043
                from_path = self._tree_id_paths.get(from_trans_id)
 
1044
            if not to_versioned:
 
1045
                to_path = None
 
1046
            else:
 
1047
                to_path = final_paths.get_path(to_trans_id)
1024
1048
            if from_kind != to_kind:
1025
1049
                modified = True
1026
1050
            elif to_kind in ('file' or 'symlink') and (
1031
1055
                from_parent==to_parent and from_name == to_name and
1032
1056
                from_executable == to_executable):
1033
1057
                continue
1034
 
            results.append((file_id, to_path, modified,
 
1058
            results.append((file_id, (from_path, to_path), modified,
1035
1059
                   (from_versioned, to_versioned),
1036
1060
                   (from_parent, to_parent),
1037
1061
                   (from_name, to_name),
1096
1120
      it is silently replaced.
1097
1121
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
1098
1122
    """
 
1123
    wt.lock_tree_write()
 
1124
    try:
 
1125
        tree.lock_read()
 
1126
        try:
 
1127
            return _build_tree(tree, wt)
 
1128
        finally:
 
1129
            tree.unlock()
 
1130
    finally:
 
1131
        wt.unlock()
 
1132
 
 
1133
def _build_tree(tree, wt):
 
1134
    """See build_tree."""
1099
1135
    if len(wt.inventory) > 1:  # more than just a root
1100
1136
        raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
1101
1137
    file_trans_id = {}
1102
1138
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1103
1139
    pp = ProgressPhase("Build phase", 2, top_pb)
1104
1140
    if tree.inventory.root is not None:
1105
 
        wt.set_root_id(tree.inventory.root.file_id)
 
1141
        # this is kindof a hack: we should be altering the root 
 
1142
        # as partof the regular tree shape diff logic.
 
1143
        # the conditional test hereis to avoid doing an
 
1144
        # expensive operation (flush) every time the root id
 
1145
        # is set within the tree, nor setting the root and thus
 
1146
        # marking the tree as dirty, because we use two different
 
1147
        # idioms here: tree interfaces and inventory interfaces.
 
1148
        if wt.path2id('') != tree.inventory.root.file_id:
 
1149
            wt.set_root_id(tree.inventory.root.file_id)
 
1150
            wt.flush()
1106
1151
    tt = TreeTransform(wt)
1107
1152
    divert = set()
1108
1153
    try:
1138
1183
                        if kind == 'directory':
1139
1184
                            reparent = True
1140
1185
                if entry.parent_id not in file_trans_id:
1141
 
                    raise repr(entry.parent_id)
 
1186
                    raise AssertionError(
 
1187
                        'entry %s parent id %r is not in file_trans_id %r'
 
1188
                        % (entry, entry.parent_id, file_trans_id))
1142
1189
                parent_id = file_trans_id[entry.parent_id]
1143
1190
                file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1144
1191
                                                      tree)
1222
1269
        executable = tree.is_executable(entry.file_id)
1223
1270
        return tt.new_file(name, parent_id, contents, entry.file_id, 
1224
1271
                           executable)
1225
 
    elif kind == 'directory':
1226
 
        return tt.new_directory(name, parent_id, entry.file_id)
 
1272
    elif kind in ('directory', 'tree-reference'):
 
1273
        trans_id = tt.new_directory(name, parent_id, entry.file_id)
 
1274
        if kind == 'tree-reference':
 
1275
            tt.set_tree_reference(entry.reference_revision, trans_id)
 
1276
        return trans_id 
1227
1277
    elif kind == 'symlink':
1228
1278
        target = tree.get_symlink_target(entry.file_id)
1229
1279
        return tt.new_symlink(name, parent_id, target, entry.file_id)
 
1280
    else:
 
1281
        raise errors.BadFileKindError(name, kind)
1230
1282
 
1231
1283
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1232
1284
    """Create new file contents according to an inventory entry."""
1245
1297
        tt.set_executability(entry.executable, trans_id)
1246
1298
 
1247
1299
 
 
1300
@deprecated_function(zero_fifteen)
1248
1301
def find_interesting(working_tree, target_tree, filenames):
1249
 
    """Find the ids corresponding to specified filenames."""
1250
 
    trees = (working_tree, target_tree)
1251
 
    return tree.find_ids_across_trees(filenames, trees)
 
1302
    """Find the ids corresponding to specified filenames.
 
1303
    
 
1304
    Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
 
1305
    """
 
1306
    working_tree.lock_read()
 
1307
    try:
 
1308
        target_tree.lock_read()
 
1309
        try:
 
1310
            return working_tree.paths2ids(filenames, [target_tree])
 
1311
        finally:
 
1312
            target_tree.unlock()
 
1313
    finally:
 
1314
        working_tree.unlock()
1252
1315
 
1253
1316
 
1254
1317
def change_entry(tt, file_id, working_tree, target_tree, 
1334
1397
    return has_contents, contents_mod, meta_mod
1335
1398
 
1336
1399
 
1337
 
def revert(working_tree, target_tree, filenames, backups=False, 
 
1400
def revert(working_tree, target_tree, filenames, backups=False,
1338
1401
           pb=DummyProgress(), change_reporter=None):
1339
1402
    """Revert a working tree's contents to those of a target tree."""
1340
 
    interesting_ids = find_interesting(working_tree, target_tree, filenames)
 
1403
    target_tree.lock_read()
1341
1404
    tt = TreeTransform(working_tree, pb)
1342
1405
    try:
1343
1406
        pp = ProgressPhase("Revert phase", 3, pb)
1344
1407
        pp.next_phase()
1345
1408
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1346
1409
        try:
1347
 
            _alter_files(working_tree, target_tree, tt, child_pb, 
1348
 
                         interesting_ids, backups)
 
1410
            _alter_files(working_tree, target_tree, tt, child_pb,
 
1411
                         filenames, backups)
1349
1412
        finally:
1350
1413
            child_pb.finished()
1351
1414
        pp.next_phase()
1356
1419
            child_pb.finished()
1357
1420
        conflicts = cook_conflicts(raw_conflicts, tt)
1358
1421
        if change_reporter:
1359
 
            change_reporter = delta.ChangeReporter(working_tree.inventory)
 
1422
            change_reporter = delta.ChangeReporter(
 
1423
                unversioned_filter=working_tree.is_ignored)
1360
1424
            delta.report_changes(tt._iter_changes(), change_reporter)
1361
1425
        for conflict in conflicts:
1362
1426
            warning(conflict)
1364
1428
        tt.apply()
1365
1429
        working_tree.set_merge_modified({})
1366
1430
    finally:
 
1431
        target_tree.unlock()
1367
1432
        tt.finalize()
1368
1433
        pb.clear()
1369
1434
    return conflicts
1370
1435
 
1371
1436
 
1372
 
def _alter_files(working_tree, target_tree, tt, pb, interesting_ids,
 
1437
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1373
1438
                 backups):
1374
1439
    merge_modified = working_tree.merge_modified()
1375
1440
    change_list = target_tree._iter_changes(working_tree,
1376
 
        specific_file_ids=interesting_ids, pb=pb)
 
1441
        specific_files=specific_files, pb=pb)
1377
1442
    if target_tree.inventory.root is None:
1378
1443
        skip_root = True
1379
1444
    else:
1380
1445
        skip_root = False
1381
1446
    basis_tree = None
1382
 
    for id_num, (file_id, path, changed_content, versioned, parent, name,
1383
 
        kind, executable) in enumerate(change_list):
1384
 
        if skip_root and file_id[0] is not None and parent[0] is None:
1385
 
            continue
1386
 
        trans_id = tt.trans_id_file_id(file_id)
1387
 
        mode_id = None
1388
 
        if changed_content:
1389
 
            keep_content = False
1390
 
            if kind[0] == 'file' and (backups or kind[1] is None):
1391
 
                wt_sha1 = working_tree.get_file_sha1(file_id)
1392
 
                if merge_modified.get(file_id) != wt_sha1:
1393
 
                    if basis_tree is None:
1394
 
                        basis_tree = working_tree.basis_tree()
1395
 
                    if file_id in basis_tree:
1396
 
                        if wt_sha1 != basis_tree.get_file_sha1(file_id):
 
1447
    try:
 
1448
        for id_num, (file_id, path, changed_content, versioned, parent, name,
 
1449
                kind, executable) in enumerate(change_list):
 
1450
            if skip_root and file_id[0] is not None and parent[0] is None:
 
1451
                continue
 
1452
            trans_id = tt.trans_id_file_id(file_id)
 
1453
            mode_id = None
 
1454
            if changed_content:
 
1455
                keep_content = False
 
1456
                if kind[0] == 'file' and (backups or kind[1] is None):
 
1457
                    wt_sha1 = working_tree.get_file_sha1(file_id)
 
1458
                    if merge_modified.get(file_id) != wt_sha1:
 
1459
                        # acquire the basis tree lazyily to prevent the expense
 
1460
                        # of accessing it when its not needed ? (Guessing, RBC,
 
1461
                        # 200702)
 
1462
                        if basis_tree is None:
 
1463
                            basis_tree = working_tree.basis_tree()
 
1464
                            basis_tree.lock_read()
 
1465
                        if file_id in basis_tree:
 
1466
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
 
1467
                                keep_content = True
 
1468
                        elif kind[1] is None and not versioned[1]:
1397
1469
                            keep_content = True
1398
 
                    elif kind[1] is None and not versioned[1]:
1399
 
                        keep_content = True
1400
 
            if kind[0] is not None:
1401
 
                if not keep_content:
1402
 
                    tt.delete_contents(trans_id)
1403
 
                elif kind[1] is not None:
1404
 
                    parent_trans_id = tt.trans_id_file_id(parent[0])
1405
 
                    by_parent = tt.by_parent()
1406
 
                    backup_name = _get_backup_name(name[0], by_parent,
1407
 
                                                   parent_trans_id, tt)
1408
 
                    tt.adjust_path(backup_name, parent_trans_id, trans_id)
1409
 
                    new_trans_id = tt.create_path(name[0], parent_trans_id)
1410
 
                    if versioned == (True, True):
1411
 
                        tt.unversion_file(trans_id)
1412
 
                        tt.version_file(file_id, new_trans_id)
1413
 
                    # New contents should have the same unix perms as old
1414
 
                    # contents
1415
 
                    mode_id = trans_id
1416
 
                    trans_id = new_trans_id
1417
 
            if kind[1] == 'directory':
1418
 
                tt.create_directory(trans_id)
1419
 
            elif kind[1] == 'symlink':
1420
 
                tt.create_symlink(target_tree.get_symlink_target(file_id),
1421
 
                                  trans_id)
1422
 
            elif kind[1] == 'file':
1423
 
                tt.create_file(target_tree.get_file_lines(file_id),
1424
 
                               trans_id, mode_id)
1425
 
                # preserve the execute bit when backing up
1426
 
                if keep_content and executable[0] == executable[1]:
1427
 
                    tt.set_executability(executable[1], trans_id)
1428
 
            else:
1429
 
                assert kind[1] is None
1430
 
        if versioned == (False, True):
1431
 
            tt.version_file(file_id, trans_id)
1432
 
        if versioned == (True, False):
1433
 
            tt.unversion_file(trans_id)
1434
 
        if (name[1] is not None and 
1435
 
            (name[0] != name[1] or parent[0] != parent[1])):
1436
 
            tt.adjust_path(name[1], tt.trans_id_file_id(parent[1]), trans_id)
1437
 
        if executable[0] != executable[1] and kind[1] == "file":
1438
 
            tt.set_executability(executable[1], trans_id)
 
1470
                if kind[0] is not None:
 
1471
                    if not keep_content:
 
1472
                        tt.delete_contents(trans_id)
 
1473
                    elif kind[1] is not None:
 
1474
                        parent_trans_id = tt.trans_id_file_id(parent[0])
 
1475
                        by_parent = tt.by_parent()
 
1476
                        backup_name = _get_backup_name(name[0], by_parent,
 
1477
                                                       parent_trans_id, tt)
 
1478
                        tt.adjust_path(backup_name, parent_trans_id, trans_id)
 
1479
                        new_trans_id = tt.create_path(name[0], parent_trans_id)
 
1480
                        if versioned == (True, True):
 
1481
                            tt.unversion_file(trans_id)
 
1482
                            tt.version_file(file_id, new_trans_id)
 
1483
                        # New contents should have the same unix perms as old
 
1484
                        # contents
 
1485
                        mode_id = trans_id
 
1486
                        trans_id = new_trans_id
 
1487
                if kind[1] == 'directory':
 
1488
                    tt.create_directory(trans_id)
 
1489
                elif kind[1] == 'symlink':
 
1490
                    tt.create_symlink(target_tree.get_symlink_target(file_id),
 
1491
                                      trans_id)
 
1492
                elif kind[1] == 'file':
 
1493
                    tt.create_file(target_tree.get_file_lines(file_id),
 
1494
                                   trans_id, mode_id)
 
1495
                    # preserve the execute bit when backing up
 
1496
                    if keep_content and executable[0] == executable[1]:
 
1497
                        tt.set_executability(executable[1], trans_id)
 
1498
                else:
 
1499
                    assert kind[1] is None
 
1500
            if versioned == (False, True):
 
1501
                tt.version_file(file_id, trans_id)
 
1502
            if versioned == (True, False):
 
1503
                tt.unversion_file(trans_id)
 
1504
            if (name[1] is not None and 
 
1505
                (name[0] != name[1] or parent[0] != parent[1])):
 
1506
                tt.adjust_path(
 
1507
                    name[1], tt.trans_id_file_id(parent[1]), trans_id)
 
1508
            if executable[0] != executable[1] and kind[1] == "file":
 
1509
                tt.set_executability(executable[1], trans_id)
 
1510
    finally:
 
1511
        if basis_tree is not None:
 
1512
            basis_tree.unlock()
1439
1513
 
1440
1514
 
1441
1515
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):