~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Andrew Bennetts
  • Date: 2008-03-12 20:13:07 UTC
  • mfrom: (3267 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3756.
  • Revision ID: andrew.bennetts@canonical.com-20080312201307-ngd5bynt2nvhnlb7
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
250
250
        This reflects only files that already exist, not ones that will be
251
251
        added by transactions.
252
252
        """
253
 
        path = self._tree.inventory.id2path(inventory_id)
 
253
        path = self._tree.id2path(inventory_id)
254
254
        return self.trans_id_tree_path(path)
255
255
 
256
256
    def trans_id_file_id(self, file_id):
354
354
        if typefunc(mode):
355
355
            os.chmod(self._limbo_name(trans_id), mode)
356
356
 
 
357
    def create_hardlink(self, path, trans_id):
 
358
        """Schedule creation of a hard link"""
 
359
        name = self._limbo_name(trans_id)
 
360
        try:
 
361
            os.link(path, name)
 
362
        except OSError, e:
 
363
            if e.errno != errno.EPERM:
 
364
                raise
 
365
            raise errors.HardLinkNotSupported(path)
 
366
        try:
 
367
            unique_add(self._new_contents, trans_id, 'file')
 
368
        except:
 
369
            # Clean up the file, it never got registered so
 
370
            # TreeTransform.finalize() won't clean it up.
 
371
            os.unlink(name)
 
372
            raise
 
373
 
357
374
    def create_directory(self, trans_id):
358
375
        """Schedule creation of a new directory.
359
376
        
753
770
        conflicts = []
754
771
        removed_tree_ids = set((self.tree_file_id(trans_id) for trans_id in
755
772
                                self._removed_id))
756
 
        active_tree_ids = set((f for f in self._tree.inventory if
757
 
                               f not in removed_tree_ids))
 
773
        all_ids = self._tree.all_file_ids()
 
774
        active_tree_ids = all_ids.difference(removed_tree_ids)
758
775
        for trans_id, file_id in self._new_id.iteritems():
759
776
            if file_id in active_tree_ids:
760
777
                old_trans_id = self.trans_id_tree_file_id(file_id)
985
1002
            to_executable = False
986
1003
        return to_name, to_parent, to_kind, to_executable
987
1004
 
988
 
    def _iter_changes(self):
989
 
        """Produce output in the same format as Tree._iter_changes.
 
1005
    def iter_changes(self):
 
1006
        """Produce output in the same format as Tree.iter_changes.
990
1007
 
991
1008
        Will produce nonsensical results if invoked while inventory/filesystem
992
1009
        conflicts (as reported by TreeTransform.find_conflicts()) are present.
1169
1186
            conflicts = self.find_conflicts()
1170
1187
            if len(conflicts) != 0:
1171
1188
                raise MalformedTransform(conflicts=conflicts)
1172
 
        inv = self._tree.inventory
1173
1189
        inventory_delta = []
1174
1190
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1175
1191
        try:
1179
1195
                mover = _mover
1180
1196
            try:
1181
1197
                child_pb.update('Apply phase', 0, 2)
1182
 
                self._apply_removals(inv, inventory_delta, mover)
 
1198
                self._apply_removals(inventory_delta, mover)
1183
1199
                child_pb.update('Apply phase', 1, 2)
1184
 
                modified_paths = self._apply_insertions(inv, inventory_delta,
1185
 
                                                        mover)
 
1200
                modified_paths = self._apply_insertions(inventory_delta, mover)
1186
1201
            except:
1187
1202
                mover.rollback()
1188
1203
                raise
1195
1210
        self.finalize()
1196
1211
        return _TransformResults(modified_paths, self.rename_count)
1197
1212
 
1198
 
    def _apply_removals(self, inv, inventory_delta, mover):
 
1213
    def _apply_removals(self, inventory_delta, mover):
1199
1214
        """Perform tree operations that remove directory/inventory names.
1200
1215
 
1201
1216
        That is, delete files that are to be deleted, and put any files that
1227
1242
                        file_id = self._tree.get_root_id()
1228
1243
                    else:
1229
1244
                        file_id = self.tree_file_id(trans_id)
1230
 
                    if file_id is not None:
1231
 
                        inventory_delta.append((path, None, file_id, None))
 
1245
                    assert file_id is not None
 
1246
                    # File-id isn't really being deleted, just moved
 
1247
                    if file_id in self._r_new_id:
 
1248
                        continue
 
1249
                    inventory_delta.append((path, None, file_id, None))
1232
1250
        finally:
1233
1251
            child_pb.finished()
1234
1252
 
1235
 
    def _apply_insertions(self, inv, inventory_delta, mover):
 
1253
    def _apply_insertions(self, inventory_delta, mover):
1236
1254
        """Perform tree operations that insert directory/inventory names.
1237
1255
 
1238
1256
        That is, create any files that need to be created, and restore from
1247
1265
            for num, (path, trans_id) in enumerate(new_paths):
1248
1266
                new_entry = None
1249
1267
                child_pb.update('adding file', num, len(new_paths))
1250
 
                try:
1251
 
                    kind = self._new_contents[trans_id]
1252
 
                except KeyError:
1253
 
                    kind = contents = None
1254
1268
                if trans_id in self._new_contents or \
1255
1269
                    self.path_changed(trans_id):
1256
1270
                    full_path = self._tree.abspath(path)
1266
1280
                    if trans_id in self._new_contents:
1267
1281
                        modified_paths.append(full_path)
1268
1282
                        completed_new.append(trans_id)
1269
 
 
1270
 
                if trans_id in self._new_id:
1271
 
                    if kind is None:
1272
 
                        kind = file_kind(self._tree.abspath(path))
 
1283
                file_id = self.final_file_id(trans_id)
 
1284
                if file_id is not None and (trans_id in self._new_id or
 
1285
                    trans_id in self._new_name or trans_id in self._new_parent
 
1286
                    or trans_id in self._new_executability):
 
1287
                    try:
 
1288
                        kind = self.final_kind(trans_id)
 
1289
                    except NoSuchFile:
 
1290
                        kind = self._tree.stored_kind(file_id)
1273
1291
                    if trans_id in self._new_reference_revision:
1274
1292
                        new_entry = inventory.TreeReference(
1275
 
                            self._new_id[trans_id],
 
1293
                            self.final_file_id(trans_id),
1276
1294
                            self._new_name[trans_id],
1277
1295
                            self.final_file_id(self._new_parent[trans_id]),
1278
1296
                            None, self._new_reference_revision[trans_id])
1280
1298
                        new_entry = inventory.make_entry(kind,
1281
1299
                            self.final_name(trans_id),
1282
1300
                            self.final_file_id(self.final_parent(trans_id)),
1283
 
                            self._new_id[trans_id])
1284
 
                else:
1285
 
                    if trans_id in self._new_name or trans_id in\
1286
 
                        self._new_parent or\
1287
 
                        trans_id in self._new_executability:
1288
 
                        file_id = self.final_file_id(trans_id)
1289
 
                        if file_id is not None:
1290
 
                            entry = inv[file_id]
1291
 
                            new_entry = entry.copy()
1292
 
 
1293
 
                    if trans_id in self._new_name or trans_id in\
1294
 
                        self._new_parent:
1295
 
                            if new_entry is not None:
1296
 
                                new_entry.name = self.final_name(trans_id)
1297
 
                                parent = self.final_parent(trans_id)
1298
 
                                parent_id = self.final_file_id(parent)
1299
 
                                new_entry.parent_id = parent_id
 
1301
                            self.final_file_id(trans_id))
 
1302
                    try:
 
1303
                        old_path = self._tree.id2path(new_entry.file_id)
 
1304
                    except errors.NoSuchId:
 
1305
                        old_path = None
 
1306
                    inventory_delta.append((old_path, path, new_entry.file_id,
 
1307
                                            new_entry))
1300
1308
 
1301
1309
                if trans_id in self._new_executability:
1302
1310
                    self._set_executability(path, new_entry, trans_id)
1303
 
                if new_entry is not None:
1304
 
                    if new_entry.file_id in inv:
1305
 
                        old_path = inv.id2path(new_entry.file_id)
1306
 
                    else:
1307
 
                        old_path = None
1308
 
                    inventory_delta.append((old_path, path,
1309
 
                                            new_entry.file_id,
1310
 
                                            new_entry))
1311
1311
        finally:
1312
1312
            child_pb.finished()
1313
1313
        for trans_id in completed_new:
1372
1372
    def unlock(self):
1373
1373
        pass
1374
1374
 
1375
 
    def _iter_changes(self, from_tree, include_unchanged=False,
 
1375
    def iter_changes(self, from_tree, include_unchanged=False,
1376
1376
                      specific_files=None, pb=None, extra_trees=None,
1377
1377
                      require_versioned=True, want_unversioned=False):
1378
 
        """See InterTree._iter_changes.
 
1378
        """See InterTree.iter_changes.
1379
1379
 
1380
1380
        This implementation does not support include_unchanged, specific_files,
1381
1381
        or want_unversioned.  extra_trees, require_versioned, and pb are
1389
1389
            raise ValueError('specific_files is not supported')
1390
1390
        if want_unversioned:
1391
1391
            raise ValueError('want_unversioned is not supported')
1392
 
        return self._transform._iter_changes()
 
1392
        return self._transform.iter_changes()
1393
1393
 
1394
1394
    def kind(self, file_id):
1395
1395
        trans_id = self._transform.trans_id_file_id(file_id)
1407
1407
        name = self._transform._limbo_name(trans_id)
1408
1408
        return open(name, 'rb')
1409
1409
 
 
1410
    def get_symlink_target(self, file_id):
 
1411
        """See Tree.get_symlink_target"""
 
1412
        trans_id = self._transform.trans_id_file_id(file_id)
 
1413
        name = self._transform._limbo_name(trans_id)
 
1414
        return os.readlink(name)
 
1415
 
1410
1416
    def paths2ids(self, specific_files, trees=None, require_versioned=False):
1411
1417
        """See Tree.paths2ids"""
1412
1418
        return 'not_empty'
1455
1461
    return file_ids
1456
1462
 
1457
1463
 
1458
 
def build_tree(tree, wt, accelerator_tree=None):
 
1464
def build_tree(tree, wt, accelerator_tree=None, hardlink=False):
1459
1465
    """Create working tree for a branch, using a TreeTransform.
1460
1466
    
1461
1467
    This function should be used on empty trees, having a tree root at most.
1474
1480
    :param accelerator_tree: A tree which can be used for retrieving file
1475
1481
        contents more quickly than tree itself, i.e. a workingtree.  tree
1476
1482
        will be used for cases where accelerator_tree's content is different.
 
1483
    :param hardlink: If true, hard-link files to accelerator_tree, where
 
1484
        possible.  accelerator_tree must implement abspath, i.e. be a
 
1485
        working tree.
1477
1486
    """
1478
1487
    wt.lock_tree_write()
1479
1488
    try:
1482
1491
            if accelerator_tree is not None:
1483
1492
                accelerator_tree.lock_read()
1484
1493
            try:
1485
 
                return _build_tree(tree, wt, accelerator_tree)
 
1494
                return _build_tree(tree, wt, accelerator_tree, hardlink)
1486
1495
            finally:
1487
1496
                if accelerator_tree is not None:
1488
1497
                    accelerator_tree.unlock()
1492
1501
        wt.unlock()
1493
1502
 
1494
1503
 
1495
 
def _build_tree(tree, wt, accelerator_tree):
 
1504
def _build_tree(tree, wt, accelerator_tree, hardlink):
1496
1505
    """See build_tree."""
1497
 
    if len(wt.inventory) > 1:  # more than just a root
1498
 
        raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
 
1506
    for num, _unused in enumerate(wt.all_file_ids()):
 
1507
        if num > 0:  # more than just a root
 
1508
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
1499
1509
    file_trans_id = {}
1500
1510
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1501
1511
    pp = ProgressPhase("Build phase", 2, top_pb)
1519
1529
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
1520
1530
        try:
1521
1531
            deferred_contents = []
 
1532
            num = 0
1522
1533
            for num, (tree_path, entry) in \
1523
1534
                enumerate(tree.inventory.iter_entries_by_dir()):
1524
1535
                pb.update("Building tree", num - len(deferred_contents),
1568
1579
                    new_trans_id = file_trans_id[file_id]
1569
1580
                    old_parent = tt.trans_id_tree_path(tree_path)
1570
1581
                    _reparent_children(tt, old_parent, new_trans_id)
1571
 
            for num, (trans_id, bytes) in enumerate(
1572
 
                _iter_files_bytes_accelerated(tree, accelerator_tree,
1573
 
                                              deferred_contents)):
1574
 
                tt.create_file(bytes, trans_id)
1575
 
                pb.update('Adding file contents',
1576
 
                          (num + len(tree.inventory) - len(deferred_contents)),
1577
 
                          len(tree.inventory))
 
1582
            offset = num + 1 - len(deferred_contents)
 
1583
            _create_files(tt, tree, deferred_contents, pb, offset,
 
1584
                          accelerator_tree, hardlink)
1578
1585
        finally:
1579
1586
            pb.finished()
1580
1587
        pp.next_phase()
1595
1602
    return result
1596
1603
 
1597
1604
 
1598
 
def _iter_files_bytes_accelerated(tree, accelerator_tree, desired_files):
 
1605
def _create_files(tt, tree, desired_files, pb, offset, accelerator_tree,
 
1606
                  hardlink):
 
1607
    total = len(desired_files) + offset
1599
1608
    if accelerator_tree is None:
1600
1609
        new_desired_files = desired_files
1601
1610
    else:
1602
 
        iter = accelerator_tree._iter_changes(tree, include_unchanged=True)
 
1611
        iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
1603
1612
        unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
1604
 
                         in iter if not c)
 
1613
                         in iter if not (c or e[0] != e[1]))
1605
1614
        new_desired_files = []
1606
 
        for file_id, identifier in desired_files:
 
1615
        count = 0
 
1616
        for file_id, trans_id in desired_files:
1607
1617
            accelerator_path = unchanged.get(file_id)
1608
1618
            if accelerator_path is None:
1609
 
                new_desired_files.append((file_id, identifier))
 
1619
                new_desired_files.append((file_id, trans_id))
1610
1620
                continue
1611
 
            contents = accelerator_tree.get_file(file_id, accelerator_path)
1612
 
            try:
1613
 
                want_new = False
1614
 
                contents_bytes = (contents.read(),)
1615
 
            finally:
1616
 
                contents.close()
1617
 
            yield identifier, contents_bytes
1618
 
    for result in tree.iter_files_bytes(new_desired_files):
1619
 
        yield result
 
1621
            pb.update('Adding file contents', count + offset, total)
 
1622
            if hardlink:
 
1623
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
 
1624
                                   trans_id)
 
1625
            else:
 
1626
                contents = accelerator_tree.get_file(file_id, accelerator_path)
 
1627
                try:
 
1628
                    tt.create_file(contents, trans_id)
 
1629
                finally:
 
1630
                    contents.close()
 
1631
            count += 1
 
1632
        offset += count
 
1633
    for count, (trans_id, contents) in enumerate(tree.iter_files_bytes(
 
1634
                                                 new_desired_files)):
 
1635
        tt.create_file(contents, trans_id)
 
1636
        pb.update('Adding file contents', count + offset, total)
1620
1637
 
1621
1638
 
1622
1639
def _reparent_children(tt, old_parent, new_parent):
1839
1856
        if change_reporter:
1840
1857
            change_reporter = delta._ChangeReporter(
1841
1858
                unversioned_filter=working_tree.is_ignored)
1842
 
            delta.report_changes(tt._iter_changes(), change_reporter)
 
1859
            delta.report_changes(tt.iter_changes(), change_reporter)
1843
1860
        for conflict in conflicts:
1844
1861
            warning(conflict)
1845
1862
        pp.next_phase()
1855
1872
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1856
1873
                 backups):
1857
1874
    merge_modified = working_tree.merge_modified()
1858
 
    change_list = target_tree._iter_changes(working_tree,
 
1875
    change_list = target_tree.iter_changes(working_tree,
1859
1876
        specific_files=specific_files, pb=pb)
1860
1877
    if target_tree.inventory.root is None:
1861
1878
        skip_root = True
2023
2040
            new_parent_id = tt.new_directory(parent_name + '.new',
2024
2041
                parent_parent, parent_file_id)
2025
2042
            _reparent_transform_children(tt, parent_id, new_parent_id)
2026
 
            tt.unversion_file(parent_id)
 
2043
            if parent_file_id is not None:
 
2044
                tt.unversion_file(parent_id)
2027
2045
            new_conflicts.add((c_type, 'Created directory', new_parent_id))
2028
2046
    return new_conflicts
2029
2047