/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

  • Committer: John Arbash Meinel
  • Date: 2008-09-05 03:11:40 UTC
  • mfrom: (3691 +trunk)
  • mto: (3697.7.4 1.7)
  • mto: This revision was merged to the branch mainline in revision 3748.
  • Revision ID: john@arbash-meinel.com-20080905031140-hj0adlcf30l7i99v
Merge in bzr.dev 3691

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import os
18
18
import errno
19
19
from stat import S_ISREG, S_IEXEC
20
 
import tempfile
21
20
 
22
21
from bzrlib.lazy_import import lazy_import
23
22
lazy_import(globals(), """
27
26
    delta,
28
27
    errors,
29
28
    inventory,
 
29
    osutils,
30
30
    revision as _mod_revision,
31
31
    )
32
32
""")
128
128
        # Cache of relpath results, to speed up canonical_path
129
129
        self._relpaths = {}
130
130
        # The trans_id that will be used as the tree root
131
 
        self._new_root = self.trans_id_tree_file_id(tree.get_root_id())
 
131
        root_id = tree.get_root_id()
 
132
        if root_id is not None:
 
133
            self._new_root = self.trans_id_tree_file_id(root_id)
 
134
        else:
 
135
            self._new_root = None
132
136
        # Indictor of whether the transform has been applied
133
137
        self._done = False
134
138
        # A progress bar
195
199
        previous_name = self._new_name.get(trans_id)
196
200
        self._new_name[trans_id] = name
197
201
        self._new_parent[trans_id] = parent
 
202
        if parent == ROOT_PARENT:
 
203
            if self._new_root is not None:
 
204
                raise ValueError("Cannot have multiple roots.")
 
205
            self._new_root = trans_id
198
206
        if (trans_id in self._limbo_files and
199
207
            trans_id not in self._needs_rename):
200
208
            self._rename_in_limbo([trans_id])
257
265
        This reflects only files that already exist, not ones that will be
258
266
        added by transactions.
259
267
        """
 
268
        if inventory_id is None:
 
269
            raise ValueError('None is not a valid file id')
260
270
        path = self._tree.id2path(inventory_id)
261
271
        return self.trans_id_tree_path(path)
262
272
 
266
276
        a transaction has been unversioned, it is deliberately still returned.
267
277
        (this will likely lead to an unversioned parent conflict.)
268
278
        """
 
279
        if file_id is None:
 
280
            raise ValueError('None is not a valid file id')
269
281
        if file_id in self._r_new_id and self._r_new_id[file_id] is not None:
270
282
            return self._r_new_id[file_id]
271
283
        elif file_id in self._tree.inventory:
470
482
        """
471
483
        new_ids = set()
472
484
        if filesystem_only:
473
 
            id_sets = (self._needs_rename, self._new_executability)
 
485
            stale_ids = self._needs_rename.difference(self._new_name)
 
486
            stale_ids.difference_update(self._new_parent)
 
487
            stale_ids.difference_update(self._new_contents)
 
488
            stale_ids.difference_update(self._new_id)
 
489
            needs_rename = self._needs_rename.difference(stale_ids)
 
490
            id_sets = (needs_rename, self._new_executability)
474
491
        else:
475
492
            id_sets = (self._new_name, self._new_parent, self._new_contents,
476
493
                       self._new_id, self._new_executability)
478
495
            new_ids.update(id_set)
479
496
        return sorted(FinalPaths(self).get_paths(new_ids))
480
497
 
 
498
    def _inventory_altered(self):
 
499
        """Get the trans_ids and paths of files needing new inv entries."""
 
500
        new_ids = set()
 
501
        for id_set in [self._new_name, self._new_parent, self._new_id,
 
502
                       self._new_executability]:
 
503
            new_ids.update(id_set)
 
504
        changed_kind = set(self._removed_contents)
 
505
        changed_kind.intersection_update(self._new_contents)
 
506
        changed_kind.difference_update(new_ids)
 
507
        changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
 
508
                        self.final_kind(t))
 
509
        new_ids.update(changed_kind)
 
510
        return sorted(FinalPaths(self).get_paths(new_ids))
 
511
 
481
512
    def tree_kind(self, trans_id):
482
513
        """Determine the file kind in the working tree.
483
514
 
636
667
        try:
637
668
            children = os.listdir(self._tree.abspath(path))
638
669
        except OSError, e:
639
 
            if e.errno not in (errno.ENOENT, errno.ESRCH, errno.ENOTDIR):
 
670
            if not (osutils._is_error_enotdir(e)
 
671
                    or e.errno in (errno.ENOENT, errno.ESRCH)):
640
672
                raise
641
673
            return
642
 
            
 
674
 
643
675
        for child in children:
644
676
            childpath = joinpath(path, child)
645
677
            if self._tree.is_control_filename(childpath):
870
902
        self._limbo_files[trans_id] = limbo_name
871
903
        return limbo_name
872
904
 
873
 
    def _set_executability(self, path, entry, trans_id):
 
905
    def _set_executability(self, path, trans_id):
874
906
        """Set the executability of versioned files """
875
 
        new_executability = self._new_executability[trans_id]
876
 
        if entry is not None:
877
 
            entry.executable = new_executability
878
907
        if supports_executable():
 
908
            new_executability = self._new_executability[trans_id]
879
909
            abspath = self._tree.abspath(path)
880
910
            current_mode = os.stat(abspath).st_mode
881
911
            if new_executability:
1203
1233
            conflicts = self.find_conflicts()
1204
1234
            if len(conflicts) != 0:
1205
1235
                raise MalformedTransform(conflicts=conflicts)
1206
 
        if precomputed_delta is None:
1207
 
            new_inventory_delta = []
1208
 
            inventory_delta = new_inventory_delta
1209
 
        else:
1210
 
            new_inventory_delta = None
1211
 
            inventory_delta = precomputed_delta
1212
1236
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1213
1237
        try:
 
1238
            if precomputed_delta is None:
 
1239
                child_pb.update('Apply phase', 0, 2)
 
1240
                inventory_delta = self._generate_inventory_delta()
 
1241
                offset = 1
 
1242
            else:
 
1243
                inventory_delta = precomputed_delta
 
1244
                offset = 0
1214
1245
            if _mover is None:
1215
1246
                mover = _FileMover()
1216
1247
            else:
1217
1248
                mover = _mover
1218
1249
            try:
1219
 
                child_pb.update('Apply phase', 0, 2)
1220
 
                self._apply_removals(new_inventory_delta, mover)
1221
 
                child_pb.update('Apply phase', 1, 2)
1222
 
                modified_paths = self._apply_insertions(new_inventory_delta,
1223
 
                                                        mover)
 
1250
                child_pb.update('Apply phase', 0 + offset, 2 + offset)
 
1251
                self._apply_removals(mover)
 
1252
                child_pb.update('Apply phase', 1 + offset, 2 + offset)
 
1253
                modified_paths = self._apply_insertions(mover)
1224
1254
            except:
1225
1255
                mover.rollback()
1226
1256
                raise
1233
1263
        self.finalize()
1234
1264
        return _TransformResults(modified_paths, self.rename_count)
1235
1265
 
1236
 
    def _apply_removals(self, inventory_delta, mover):
 
1266
    def _generate_inventory_delta(self):
 
1267
        """Generate an inventory delta for the current transform."""
 
1268
        inventory_delta = []
 
1269
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1270
        new_paths = self._inventory_altered()
 
1271
        total_entries = len(new_paths) + len(self._removed_id)
 
1272
        try:
 
1273
            for num, trans_id in enumerate(self._removed_id):
 
1274
                if (num % 10) == 0:
 
1275
                    child_pb.update('removing file', num, total_entries)
 
1276
                if trans_id == self._new_root:
 
1277
                    file_id = self._tree.get_root_id()
 
1278
                else:
 
1279
                    file_id = self.tree_file_id(trans_id)
 
1280
                # File-id isn't really being deleted, just moved
 
1281
                if file_id in self._r_new_id:
 
1282
                    continue
 
1283
                path = self._tree_id_paths[trans_id]
 
1284
                inventory_delta.append((path, None, file_id, None))
 
1285
            new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
 
1286
                                     new_paths)
 
1287
            entries = self._tree.iter_entries_by_dir(
 
1288
                new_path_file_ids.values())
 
1289
            old_paths = dict((e.file_id, p) for p, e in entries)
 
1290
            final_kinds = {}
 
1291
            for num, (path, trans_id) in enumerate(new_paths):
 
1292
                if (num % 10) == 0:
 
1293
                    child_pb.update('adding file',
 
1294
                                    num + len(self._removed_id), total_entries)
 
1295
                file_id = new_path_file_ids[trans_id]
 
1296
                if file_id is None:
 
1297
                    continue
 
1298
                needs_entry = False
 
1299
                try:
 
1300
                    kind = self.final_kind(trans_id)
 
1301
                except NoSuchFile:
 
1302
                    kind = self._tree.stored_kind(file_id)
 
1303
                parent_trans_id = self.final_parent(trans_id)
 
1304
                parent_file_id = new_path_file_ids.get(parent_trans_id)
 
1305
                if parent_file_id is None:
 
1306
                    parent_file_id = self.final_file_id(parent_trans_id)
 
1307
                if trans_id in self._new_reference_revision:
 
1308
                    new_entry = inventory.TreeReference(
 
1309
                        file_id,
 
1310
                        self._new_name[trans_id],
 
1311
                        self.final_file_id(self._new_parent[trans_id]),
 
1312
                        None, self._new_reference_revision[trans_id])
 
1313
                else:
 
1314
                    new_entry = inventory.make_entry(kind,
 
1315
                        self.final_name(trans_id),
 
1316
                        parent_file_id, file_id)
 
1317
                old_path = old_paths.get(new_entry.file_id)
 
1318
                new_executability = self._new_executability.get(trans_id)
 
1319
                if new_executability is not None:
 
1320
                    new_entry.executable = new_executability
 
1321
                inventory_delta.append(
 
1322
                    (old_path, path, new_entry.file_id, new_entry))
 
1323
        finally:
 
1324
            child_pb.finished()
 
1325
        return inventory_delta
 
1326
 
 
1327
    def _apply_removals(self, mover):
1237
1328
        """Perform tree operations that remove directory/inventory names.
1238
1329
 
1239
1330
        That is, delete files that are to be deleted, and put any files that
1262
1353
                            raise
1263
1354
                    else:
1264
1355
                        self.rename_count += 1
1265
 
                if (trans_id in self._removed_id
1266
 
                    and inventory_delta is not None):
1267
 
                    if trans_id == self._new_root:
1268
 
                        file_id = self._tree.get_root_id()
1269
 
                    else:
1270
 
                        file_id = self.tree_file_id(trans_id)
1271
 
                    # File-id isn't really being deleted, just moved
1272
 
                    if file_id in self._r_new_id:
1273
 
                        continue
1274
 
                    inventory_delta.append((path, None, file_id, None))
1275
1356
        finally:
1276
1357
            child_pb.finished()
1277
1358
 
1278
 
    def _apply_insertions(self, inventory_delta, mover):
 
1359
    def _apply_insertions(self, mover):
1279
1360
        """Perform tree operations that insert directory/inventory names.
1280
1361
 
1281
1362
        That is, create any files that need to be created, and restore from
1285
1366
        If inventory_delta is None, no inventory delta is calculated, and
1286
1367
        no list of modified paths is returned.
1287
1368
        """
1288
 
        new_paths = self.new_paths(filesystem_only=(inventory_delta is None))
 
1369
        new_paths = self.new_paths(filesystem_only=True)
1289
1370
        modified_paths = []
1290
 
        completed_new = []
1291
1371
        new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1292
1372
                                 new_paths)
1293
 
        if inventory_delta is not None:
1294
 
            entries = self._tree.iter_entries_by_dir(
1295
 
                new_path_file_ids.values())
1296
 
            old_paths = dict((e.file_id, p) for p, e in entries)
1297
1373
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1298
1374
        try:
1299
1375
            for num, (path, trans_id) in enumerate(new_paths):
1300
 
                new_entry = None
1301
1376
                if (num % 10) == 0:
1302
1377
                    child_pb.update('adding file', num, len(new_paths))
1303
1378
                full_path = self._tree.abspath(path)
1310
1385
                            raise
1311
1386
                    else:
1312
1387
                        self.rename_count += 1
1313
 
                if inventory_delta is not None:
1314
 
                    if (trans_id in self._new_contents or
1315
 
                        self.path_changed(trans_id)):
1316
 
                        if trans_id in self._new_contents:
1317
 
                            modified_paths.append(full_path)
1318
 
                            completed_new.append(trans_id)
1319
 
                    file_id = new_path_file_ids[trans_id]
1320
 
                    if file_id is not None and (trans_id in self._new_id or
1321
 
                        trans_id in self._new_name or
1322
 
                        trans_id in self._new_parent
1323
 
                        or trans_id in self._new_executability):
1324
 
                        try:
1325
 
                            kind = self.final_kind(trans_id)
1326
 
                        except NoSuchFile:
1327
 
                            kind = self._tree.stored_kind(file_id)
1328
 
                        parent_trans_id = self.final_parent(trans_id)
1329
 
                        parent_file_id = new_path_file_ids.get(parent_trans_id)
1330
 
                        if parent_file_id is None:
1331
 
                            parent_file_id = self.final_file_id(
1332
 
                                parent_trans_id)
1333
 
                        if trans_id in self._new_reference_revision:
1334
 
                            new_entry = inventory.TreeReference(
1335
 
                                file_id,
1336
 
                                self._new_name[trans_id],
1337
 
                                self.final_file_id(self._new_parent[trans_id]),
1338
 
                                None, self._new_reference_revision[trans_id])
1339
 
                        else:
1340
 
                            new_entry = inventory.make_entry(kind,
1341
 
                                self.final_name(trans_id),
1342
 
                                parent_file_id, file_id)
1343
 
                        old_path = old_paths.get(new_entry.file_id)
1344
 
                        inventory_delta.append(
1345
 
                            (old_path, path, new_entry.file_id, new_entry))
1346
 
 
 
1388
                if (trans_id in self._new_contents or
 
1389
                    self.path_changed(trans_id)):
 
1390
                    if trans_id in self._new_contents:
 
1391
                        modified_paths.append(full_path)
1347
1392
                if trans_id in self._new_executability:
1348
 
                    self._set_executability(path, new_entry, trans_id)
 
1393
                    self._set_executability(path, trans_id)
1349
1394
        finally:
1350
1395
            child_pb.finished()
1351
 
        if inventory_delta is None:
1352
 
            self._new_contents.clear()
1353
 
        else:
1354
 
            for trans_id in completed_new:
1355
 
                del self._new_contents[trans_id]
 
1396
        self._new_contents.clear()
1356
1397
        return modified_paths
1357
1398
 
1358
1399
 
1366
1407
 
1367
1408
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1368
1409
        tree.lock_read()
1369
 
        limbodir = tempfile.mkdtemp(prefix='bzr-limbo-')
 
1410
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1370
1411
        TreeTransformBase.__init__(self, tree, limbodir, pb, case_sensitive)
1371
1412
 
1372
1413
    def canonical_path(self, path):
1410
1451
        self._transform = transform
1411
1452
        self._final_paths = FinalPaths(transform)
1412
1453
        self.__by_parent = None
 
1454
        self._parent_ids = []
1413
1455
 
1414
1456
    def _changes(self, file_id):
1415
1457
        for changes in self._transform.iter_changes():
1423
1465
        # InterTree.iter_changes.
1424
1466
        return (changes is not None and changes[2])
1425
1467
 
 
1468
    def _get_repository(self):
 
1469
        repo = getattr(self._transform._tree, '_repository', None)
 
1470
        if repo is None:
 
1471
            repo = self._transform._tree.branch.repository
 
1472
        return repo
 
1473
 
 
1474
    def _iter_parent_trees(self):
 
1475
        for revision_id in self.get_parent_ids():
 
1476
            try:
 
1477
                yield self.revision_tree(revision_id)
 
1478
            except errors.NoSuchRevisionInTree:
 
1479
                yield self._get_repository().revision_tree(revision_id)
 
1480
 
1426
1481
    def _get_file_revision(self, file_id, vf, tree_revision):
1427
 
        return self._transform._tree._get_file_revision(file_id, vf,
1428
 
                                                        tree_revision)
 
1482
        parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
 
1483
                       self._iter_parent_trees()]
 
1484
        vf.add_lines((file_id, tree_revision), parent_keys,
 
1485
                     self.get_file(file_id).readlines())
 
1486
        repo = self._get_repository()
 
1487
        base_vf = repo.texts
 
1488
        if base_vf not in vf.fallback_versionedfiles:
 
1489
            vf.fallback_versionedfiles.append(base_vf)
 
1490
        return tree_revision
1429
1491
 
1430
1492
    def _stat_limbo_file(self, file_id):
1431
1493
        trans_id = self._transform.trans_id_file_id(file_id)
1470
1532
        for (file_id, paths, changed, versioned, parent, name, kind,
1471
1533
             executable) in self._transform.iter_changes():
1472
1534
            if paths[1] in to_find:
1473
 
                result.append(file_id)
 
1535
                result.add(file_id)
1474
1536
                to_find.remove(paths[1])
1475
1537
        result.update(self._transform._tree.paths2ids(to_find,
1476
1538
                      trees=[], require_versioned=require_versioned))
1523
1585
                parent_file_id, file_id)
1524
1586
            yield new_entry, trans_id
1525
1587
 
1526
 
    def iter_entries_by_dir(self, specific_file_ids=None):
1527
 
        # This may not be a maximally efficient implementation, but it is
1528
 
        # reasonably straightforward.  An implementation that grafts the
1529
 
        # TreeTransform changes onto the tree's iter_entries_by_dir results
1530
 
        # might be more efficient, but requires tricky inferences about stack
1531
 
        # position.
 
1588
    def _list_files_by_dir(self):
1532
1589
        todo = [ROOT_PARENT]
1533
1590
        ordered_ids = []
1534
1591
        while len(todo) > 0:
1540
1597
            todo.extend(reversed(children))
1541
1598
            for trans_id in children:
1542
1599
                ordered_ids.append((trans_id, parent_file_id))
 
1600
        return ordered_ids
 
1601
 
 
1602
    def iter_entries_by_dir(self, specific_file_ids=None):
 
1603
        # This may not be a maximally efficient implementation, but it is
 
1604
        # reasonably straightforward.  An implementation that grafts the
 
1605
        # TreeTransform changes onto the tree's iter_entries_by_dir results
 
1606
        # might be more efficient, but requires tricky inferences about stack
 
1607
        # position.
 
1608
        ordered_ids = self._list_files_by_dir()
1543
1609
        for entry, trans_id in self._make_inv_entries(ordered_ids,
1544
1610
                                                      specific_file_ids):
1545
1611
            yield unicode(self._final_paths.get_path(trans_id)), entry
1546
1612
 
 
1613
    def list_files(self, include_root=False):
 
1614
        """See Tree.list_files."""
 
1615
        # XXX This should behave like WorkingTree.list_files, but is really
 
1616
        # more like RevisionTree.list_files.
 
1617
        for path, entry in self.iter_entries_by_dir():
 
1618
            if entry.name == '' and not include_root:
 
1619
                continue
 
1620
            yield path, 'V', entry.kind, entry.file_id, entry
 
1621
 
1547
1622
    def kind(self, file_id):
1548
1623
        trans_id = self._transform.trans_id_file_id(file_id)
1549
1624
        return self._transform.final_kind(trans_id)
1675
1750
        name = self._transform._limbo_name(trans_id)
1676
1751
        return os.readlink(name)
1677
1752
 
1678
 
    def list_files(self, include_root=False):
1679
 
        return self._transform._tree.list_files(include_root)
1680
 
 
1681
 
    def walkdirs(self, prefix=""):
1682
 
        return self._transform._tree.walkdirs(prefix)
 
1753
    def walkdirs(self, prefix=''):
 
1754
        pending = [self._transform.root]
 
1755
        while len(pending) > 0:
 
1756
            parent_id = pending.pop()
 
1757
            children = []
 
1758
            subdirs = []
 
1759
            prefix = prefix.rstrip('/')
 
1760
            parent_path = self._final_paths.get_path(parent_id)
 
1761
            parent_file_id = self._transform.final_file_id(parent_id)
 
1762
            for child_id in self._all_children(parent_id):
 
1763
                path_from_root = self._final_paths.get_path(child_id)
 
1764
                basename = self._transform.final_name(child_id)
 
1765
                file_id = self._transform.final_file_id(child_id)
 
1766
                try:
 
1767
                    kind = self._transform.final_kind(child_id)
 
1768
                    versioned_kind = kind
 
1769
                except NoSuchFile:
 
1770
                    kind = 'unknown'
 
1771
                    versioned_kind = self._transform._tree.stored_kind(file_id)
 
1772
                if versioned_kind == 'directory':
 
1773
                    subdirs.append(child_id)
 
1774
                children.append((path_from_root, basename, kind, None,
 
1775
                                 file_id, versioned_kind))
 
1776
            children.sort()
 
1777
            if parent_path.startswith(prefix):
 
1778
                yield (parent_path, parent_file_id), children
 
1779
            pending.extend(sorted(subdirs, key=self._final_paths.get_path,
 
1780
                                  reverse=True))
1683
1781
 
1684
1782
    def get_parent_ids(self):
1685
 
        return self._transform._tree.get_parent_ids()
 
1783
        return self._parent_ids
 
1784
 
 
1785
    def set_parent_ids(self, parent_ids):
 
1786
        self._parent_ids = parent_ids
1686
1787
 
1687
1788
    def get_revision_tree(self, revision_id):
1688
1789
        return self._transform._tree.get_revision_tree(revision_id)
2062
2163
    tt = TreeTransform(working_tree, pb)
2063
2164
    try:
2064
2165
        pp = ProgressPhase("Revert phase", 3, pb)
2065
 
        pp.next_phase()
2066
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2067
 
        try:
2068
 
            merge_modified = _alter_files(working_tree, target_tree, tt,
2069
 
                                          child_pb, filenames, backups)
2070
 
        finally:
2071
 
            child_pb.finished()
2072
 
        pp.next_phase()
2073
 
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2074
 
        try:
2075
 
            raw_conflicts = resolve_conflicts(tt, child_pb,
2076
 
                lambda t, c: conflict_pass(t, c, target_tree))
2077
 
        finally:
2078
 
            child_pb.finished()
2079
 
        conflicts = cook_conflicts(raw_conflicts, tt)
 
2166
        conflicts, merge_modified = _prepare_revert_transform(
 
2167
            working_tree, target_tree, tt, filenames, backups, pp)
2080
2168
        if change_reporter:
2081
2169
            change_reporter = delta._ChangeReporter(
2082
2170
                unversioned_filter=working_tree.is_ignored)
2093
2181
    return conflicts
2094
2182
 
2095
2183
 
 
2184
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
 
2185
                              backups, pp, basis_tree=None,
 
2186
                              merge_modified=None):
 
2187
    pp.next_phase()
 
2188
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2189
    try:
 
2190
        if merge_modified is None:
 
2191
            merge_modified = working_tree.merge_modified()
 
2192
        merge_modified = _alter_files(working_tree, target_tree, tt,
 
2193
                                      child_pb, filenames, backups,
 
2194
                                      merge_modified, basis_tree)
 
2195
    finally:
 
2196
        child_pb.finished()
 
2197
    pp.next_phase()
 
2198
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2199
    try:
 
2200
        raw_conflicts = resolve_conflicts(tt, child_pb,
 
2201
            lambda t, c: conflict_pass(t, c, target_tree))
 
2202
    finally:
 
2203
        child_pb.finished()
 
2204
    conflicts = cook_conflicts(raw_conflicts, tt)
 
2205
    return conflicts, merge_modified
 
2206
 
 
2207
 
2096
2208
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
2097
 
                 backups):
2098
 
    merge_modified = working_tree.merge_modified()
 
2209
                 backups, merge_modified, basis_tree=None):
 
2210
    if basis_tree is not None:
 
2211
        basis_tree.lock_read()
2099
2212
    change_list = target_tree.iter_changes(working_tree,
2100
2213
        specific_files=specific_files, pb=pb)
2101
 
    if target_tree.inventory.root is None:
 
2214
    if target_tree.get_root_id() is None:
2102
2215
        skip_root = True
2103
2216
    else:
2104
2217
        skip_root = False
2105
 
    basis_tree = None
2106
2218
    try:
2107
2219
        deferred_files = []
2108
2220
        for id_num, (file_id, path, changed_content, versioned, parent, name,
2144
2256
                        # contents
2145
2257
                        mode_id = trans_id
2146
2258
                        trans_id = new_trans_id
2147
 
                if kind[1] == 'directory':
 
2259
                if kind[1] in ('directory', 'tree-reference'):
2148
2260
                    tt.create_directory(trans_id)
 
2261
                    if kind[1] == 'tree-reference':
 
2262
                        revision = target_tree.get_reference_revision(file_id,
 
2263
                                                                      path[1])
 
2264
                        tt.set_tree_reference(revision, trans_id)
2149
2265
                elif kind[1] == 'symlink':
2150
2266
                    tt.create_symlink(target_tree.get_symlink_target(file_id),
2151
2267
                                      trans_id)
2171
2287
                tt.version_file(file_id, trans_id)
2172
2288
            if versioned == (True, False):
2173
2289
                tt.unversion_file(trans_id)
2174
 
            if (name[1] is not None and 
 
2290
            if (name[1] is not None and
2175
2291
                (name[0] != name[1] or parent[0] != parent[1])):
2176
 
                tt.adjust_path(
2177
 
                    name[1], tt.trans_id_file_id(parent[1]), trans_id)
 
2292
                if name[1] == '' and parent[1] is None:
 
2293
                    parent_trans = ROOT_PARENT
 
2294
                else:
 
2295
                    parent_trans = tt.trans_id_file_id(parent[1])
 
2296
                tt.adjust_path(name[1], parent_trans, trans_id)
2178
2297
            if executable[0] != executable[1] and kind[1] == "file":
2179
2298
                tt.set_executability(executable[1], trans_id)
2180
2299
        for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2288
2407
            if parent_file_id is not None:
2289
2408
                tt.unversion_file(parent_id)
2290
2409
            new_conflicts.add((c_type, 'Created directory', new_parent_id))
 
2410
        elif c_type == 'versioning no contents':
 
2411
            tt.cancel_versioning(conflict[1])
2291
2412
    return new_conflicts
2292
2413
 
2293
2414