/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: Martin Pool
  • Date: 2010-10-08 04:38:25 UTC
  • mfrom: (5462 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5478.
  • Revision ID: mbp@sourcefrog.net-20101008043825-b181r8bo5r3qwb6j
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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
17
17
import os
18
18
import errno
19
19
from stat import S_ISREG, S_IEXEC
 
20
import time
20
21
 
21
22
from bzrlib.lazy_import import lazy_import
22
23
lazy_import(globals(), """
24
25
    annotate,
25
26
    bencode,
26
27
    bzrdir,
 
28
    commit,
27
29
    delta,
28
30
    errors,
29
31
    inventory,
30
32
    multiparent,
31
33
    osutils,
32
34
    revision as _mod_revision,
 
35
    ui,
33
36
    )
34
37
""")
35
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
36
 
                           ReusingTransform, NotVersionedError, CantMoveRoot,
 
39
                           ReusingTransform, CantMoveRoot,
37
40
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
38
41
                           UnableCreateSymlink)
39
42
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
48
51
    splitpath,
49
52
    supports_executable,
50
53
)
51
 
from bzrlib.progress import DummyProgress, ProgressPhase
 
54
from bzrlib.progress import ProgressPhase
52
55
from bzrlib.symbol_versioning import (
53
56
        deprecated_function,
54
57
        deprecated_in,
78
81
class TreeTransformBase(object):
79
82
    """The base class for TreeTransform and its kin."""
80
83
 
81
 
    def __init__(self, tree, pb=DummyProgress(),
 
84
    def __init__(self, tree, pb=None,
82
85
                 case_sensitive=True):
83
86
        """Constructor.
84
87
 
85
88
        :param tree: The tree that will be transformed, but not necessarily
86
89
            the output tree.
87
 
        :param pb: A ProgressTask indicating how much progress is being made
 
90
        :param pb: ignored
88
91
        :param case_sensitive: If True, the target of the transform is
89
92
            case sensitive, not just case preserving.
90
93
        """
161
164
 
162
165
    def adjust_path(self, name, parent, trans_id):
163
166
        """Change the path that is assigned to a transaction id."""
 
167
        if parent is None:
 
168
            raise ValueError("Parent trans-id may not be None")
164
169
        if trans_id == self._new_root:
165
170
            raise CantMoveRoot
166
171
        self._new_name[trans_id] = name
167
172
        self._new_parent[trans_id] = parent
168
 
        if parent == ROOT_PARENT:
169
 
            if self._new_root is not None:
170
 
                raise ValueError("Cannot have multiple roots.")
171
 
            self._new_root = trans_id
172
173
 
173
174
    def adjust_root_path(self, name, parent):
174
175
        """Emulate moving the root by moving all children, instead.
202
203
        self.version_file(old_root_file_id, old_root)
203
204
        self.unversion_file(self._new_root)
204
205
 
 
206
    def fixup_new_roots(self):
 
207
        """Reinterpret requests to change the root directory
 
208
 
 
209
        Instead of creating a root directory, or moving an existing directory,
 
210
        all the attributes and children of the new root are applied to the
 
211
        existing root directory.
 
212
 
 
213
        This means that the old root trans-id becomes obsolete, so it is
 
214
        recommended only to invoke this after the root trans-id has become
 
215
        irrelevant.
 
216
        """
 
217
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
 
218
                     ROOT_PARENT]
 
219
        if len(new_roots) < 1:
 
220
            return
 
221
        if len(new_roots) != 1:
 
222
            raise ValueError('A tree cannot have two roots!')
 
223
        if self._new_root is None:
 
224
            self._new_root = new_roots[0]
 
225
            return
 
226
        old_new_root = new_roots[0]
 
227
        # TODO: What to do if a old_new_root is present, but self._new_root is
 
228
        #       not listed as being removed? This code explicitly unversions
 
229
        #       the old root and versions it with the new file_id. Though that
 
230
        #       seems like an incomplete delta
 
231
 
 
232
        # unversion the new root's directory.
 
233
        file_id = self.final_file_id(old_new_root)
 
234
        if old_new_root in self._new_id:
 
235
            self.cancel_versioning(old_new_root)
 
236
        else:
 
237
            self.unversion_file(old_new_root)
 
238
        # if, at this stage, root still has an old file_id, zap it so we can
 
239
        # stick a new one in.
 
240
        if (self.tree_file_id(self._new_root) is not None and
 
241
            self._new_root not in self._removed_id):
 
242
            self.unversion_file(self._new_root)
 
243
        self.version_file(file_id, self._new_root)
 
244
 
 
245
        # Now move children of new root into old root directory.
 
246
        # Ensure all children are registered with the transaction, but don't
 
247
        # use directly-- some tree children have new parents
 
248
        list(self.iter_tree_children(old_new_root))
 
249
        # Move all children of new root into old root directory.
 
250
        for child in self.by_parent().get(old_new_root, []):
 
251
            self.adjust_path(self.final_name(child), self._new_root, child)
 
252
 
 
253
        # Ensure old_new_root has no directory.
 
254
        if old_new_root in self._new_contents:
 
255
            self.cancel_creation(old_new_root)
 
256
        else:
 
257
            self.delete_contents(old_new_root)
 
258
 
 
259
        # prevent deletion of root directory.
 
260
        if self._new_root in self._removed_contents:
 
261
            self.cancel_deletion(self._new_root)
 
262
 
 
263
        # destroy path info for old_new_root.
 
264
        del self._new_parent[old_new_root]
 
265
        del self._new_name[old_new_root]
 
266
 
205
267
    def trans_id_tree_file_id(self, inventory_id):
206
268
        """Determine the transaction id of a working tree file.
207
269
 
253
315
 
254
316
    def delete_contents(self, trans_id):
255
317
        """Schedule the contents of a path entry for deletion"""
256
 
        self.tree_kind(trans_id)
257
 
        self._removed_contents.add(trans_id)
 
318
        kind = self.tree_kind(trans_id)
 
319
        if kind is not None:
 
320
            self._removed_contents.add(trans_id)
258
321
 
259
322
    def cancel_deletion(self, trans_id):
260
323
        """Cancel a scheduled deletion"""
325
388
        changed_kind = set(self._removed_contents)
326
389
        changed_kind.intersection_update(self._new_contents)
327
390
        changed_kind.difference_update(new_ids)
328
 
        changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
329
 
                        self.final_kind(t))
 
391
        changed_kind = (t for t in changed_kind
 
392
                        if self.tree_kind(t) != self.final_kind(t))
330
393
        new_ids.update(changed_kind)
331
394
        return sorted(FinalPaths(self).get_paths(new_ids))
332
395
 
333
396
    def final_kind(self, trans_id):
334
397
        """Determine the final file kind, after any changes applied.
335
398
 
336
 
        Raises NoSuchFile if the file does not exist/has no contents.
337
 
        (It is conceivable that a path would be created without the
338
 
        corresponding contents insertion command)
 
399
        :return: None if the file does not exist/has no contents.  (It is
 
400
            conceivable that a path would be created without the corresponding
 
401
            contents insertion command)
339
402
        """
340
403
        if trans_id in self._new_contents:
341
404
            return self._new_contents[trans_id]
342
405
        elif trans_id in self._removed_contents:
343
 
            raise NoSuchFile(None)
 
406
            return None
344
407
        else:
345
408
            return self.tree_kind(trans_id)
346
409
 
532
595
        """
533
596
        conflicts = []
534
597
        for trans_id in self._new_id.iterkeys():
535
 
            try:
536
 
                kind = self.final_kind(trans_id)
537
 
            except NoSuchFile:
 
598
            kind = self.final_kind(trans_id)
 
599
            if kind is None:
538
600
                conflicts.append(('versioning no contents', trans_id))
539
601
                continue
540
602
            if not InventoryEntry.versionable_kind(kind):
554
616
            if self.final_file_id(trans_id) is None:
555
617
                conflicts.append(('unversioned executability', trans_id))
556
618
            else:
557
 
                try:
558
 
                    non_file = self.final_kind(trans_id) != "file"
559
 
                except NoSuchFile:
560
 
                    non_file = True
561
 
                if non_file is True:
 
619
                if self.final_kind(trans_id) != "file":
562
620
                    conflicts.append(('non-file executability', trans_id))
563
621
        return conflicts
564
622
 
566
624
        """Check for overwrites (not permitted on Win32)"""
567
625
        conflicts = []
568
626
        for trans_id in self._new_contents:
569
 
            try:
570
 
                self.tree_kind(trans_id)
571
 
            except NoSuchFile:
 
627
            if self.tree_kind(trans_id) is None:
572
628
                continue
573
629
            if trans_id not in self._removed_contents:
574
630
                conflicts.append(('overwrite', trans_id,
588
644
            last_name = None
589
645
            last_trans_id = None
590
646
            for name, trans_id in name_ids:
591
 
                try:
592
 
                    kind = self.final_kind(trans_id)
593
 
                except NoSuchFile:
594
 
                    kind = None
 
647
                kind = self.final_kind(trans_id)
595
648
                file_id = self.final_file_id(trans_id)
596
649
                if kind is None and file_id is None:
597
650
                    continue
623
676
                continue
624
677
            if not self._any_contents(children):
625
678
                continue
626
 
            for child in children:
627
 
                try:
628
 
                    self.final_kind(child)
629
 
                except NoSuchFile:
630
 
                    continue
631
 
            try:
632
 
                kind = self.final_kind(parent_id)
633
 
            except NoSuchFile:
634
 
                kind = None
 
679
            kind = self.final_kind(parent_id)
635
680
            if kind is None:
636
681
                conflicts.append(('missing parent', parent_id))
637
682
            elif kind != "directory":
641
686
    def _any_contents(self, trans_ids):
642
687
        """Return true if any of the trans_ids, will have contents."""
643
688
        for trans_id in trans_ids:
644
 
            try:
645
 
                kind = self.final_kind(trans_id)
646
 
            except NoSuchFile:
647
 
                continue
648
 
            return True
 
689
            if self.final_kind(trans_id) is not None:
 
690
                return True
649
691
        return False
650
692
 
651
693
    def _set_executability(self, path, trans_id):
781
823
        Return a (name, parent, kind, executable) tuple
782
824
        """
783
825
        to_name = self.final_name(to_trans_id)
784
 
        try:
785
 
            to_kind = self.final_kind(to_trans_id)
786
 
        except NoSuchFile:
787
 
            to_kind = None
 
826
        to_kind = self.final_kind(to_trans_id)
788
827
        to_parent = self.final_file_id(self.final_parent(to_trans_id))
789
828
        if to_trans_id in self._new_executability:
790
829
            to_executable = self._new_executability[to_trans_id]
864
903
        """
865
904
        return _PreviewTree(self)
866
905
 
867
 
    def commit(self, branch, message, merge_parents=None, strict=False):
 
906
    def commit(self, branch, message, merge_parents=None, strict=False,
 
907
               timestamp=None, timezone=None, committer=None, authors=None,
 
908
               revprops=None, revision_id=None):
868
909
        """Commit the result of this TreeTransform to a branch.
869
910
 
870
911
        :param branch: The branch to commit to.
871
912
        :param message: The message to attach to the commit.
872
 
        :param merge_parents: Additional parents specified by pending merges.
 
913
        :param merge_parents: Additional parent revision-ids specified by
 
914
            pending merges.
 
915
        :param strict: If True, abort the commit if there are unversioned
 
916
            files.
 
917
        :param timestamp: if not None, seconds-since-epoch for the time and
 
918
            date.  (May be a float.)
 
919
        :param timezone: Optional timezone for timestamp, as an offset in
 
920
            seconds.
 
921
        :param committer: Optional committer in email-id format.
 
922
            (e.g. "J Random Hacker <jrandom@example.com>")
 
923
        :param authors: Optional list of authors in email-id format.
 
924
        :param revprops: Optional dictionary of revision properties.
 
925
        :param revision_id: Optional revision id.  (Specifying a revision-id
 
926
            may reduce performance for some non-native formats.)
873
927
        :return: The revision_id of the revision committed.
874
928
        """
875
929
        self._check_malformed()
892
946
        if self._tree.get_revision_id() != last_rev_id:
893
947
            raise ValueError('TreeTransform not based on branch basis: %s' %
894
948
                             self._tree.get_revision_id())
895
 
        builder = branch.get_commit_builder(parent_ids)
 
949
        revprops = commit.Commit.update_revprops(revprops, branch, authors)
 
950
        builder = branch.get_commit_builder(parent_ids,
 
951
                                            timestamp=timestamp,
 
952
                                            timezone=timezone,
 
953
                                            committer=committer,
 
954
                                            revprops=revprops,
 
955
                                            revision_id=revision_id)
896
956
        preview = self.get_preview_tree()
897
957
        list(builder.record_iter_changes(preview, last_rev_id,
898
958
                                         self.iter_changes()))
1000
1060
class DiskTreeTransform(TreeTransformBase):
1001
1061
    """Tree transform storing its contents on disk."""
1002
1062
 
1003
 
    def __init__(self, tree, limbodir, pb=DummyProgress(),
 
1063
    def __init__(self, tree, limbodir, pb=None,
1004
1064
                 case_sensitive=True):
1005
1065
        """Constructor.
1006
1066
        :param tree: The tree that will be transformed, but not necessarily
1007
1067
            the output tree.
1008
1068
        :param limbodir: A directory where new files can be stored until
1009
1069
            they are installed in their proper places
1010
 
        :param pb: A ProgressBar indicating how much progress is being made
 
1070
        :param pb: ignored
1011
1071
        :param case_sensitive: If True, the target of the transform is
1012
1072
            case sensitive, not just case preserving.
1013
1073
        """
1023
1083
        self._limbo_children_names = {}
1024
1084
        # List of transform ids that need to be renamed from limbo into place
1025
1085
        self._needs_rename = set()
 
1086
        self._creation_mtime = None
1026
1087
 
1027
1088
    def finalize(self):
1028
1089
        """Release the working tree lock, if held, clean up limbo dir.
1075
1136
        if (trans_id in self._limbo_files and
1076
1137
            trans_id not in self._needs_rename):
1077
1138
            self._rename_in_limbo([trans_id])
1078
 
            self._limbo_children[previous_parent].remove(trans_id)
1079
 
            del self._limbo_children_names[previous_parent][previous_name]
 
1139
            if previous_parent != parent:
 
1140
                self._limbo_children[previous_parent].remove(trans_id)
 
1141
            if previous_parent != parent or previous_name != name:
 
1142
                del self._limbo_children_names[previous_parent][previous_name]
1080
1143
 
1081
1144
    def _rename_in_limbo(self, trans_ids):
1082
1145
        """Fix limbo names so that the right final path is produced.
1133
1196
            f.writelines(contents)
1134
1197
        finally:
1135
1198
            f.close()
 
1199
        self._set_mtime(name)
1136
1200
        self._set_mode(trans_id, mode_id, S_ISREG)
1137
1201
 
1138
1202
    def _read_file_chunks(self, trans_id):
1145
1209
    def _read_symlink_target(self, trans_id):
1146
1210
        return os.readlink(self._limbo_name(trans_id))
1147
1211
 
 
1212
    def _set_mtime(self, path):
 
1213
        """All files that are created get the same mtime.
 
1214
 
 
1215
        This time is set by the first object to be created.
 
1216
        """
 
1217
        if self._creation_mtime is None:
 
1218
            self._creation_mtime = time.time()
 
1219
        os.utime(path, (self._creation_mtime, self._creation_mtime))
 
1220
 
1148
1221
    def create_hardlink(self, path, trans_id):
1149
1222
        """Schedule creation of a hard link"""
1150
1223
        name = self._limbo_name(trans_id)
1264
1337
    FileMover does not delete files until it is sure that a rollback will not
1265
1338
    happen.
1266
1339
    """
1267
 
    def __init__(self, tree, pb=DummyProgress()):
 
1340
    def __init__(self, tree, pb=None):
1268
1341
        """Note: a tree_write lock is taken on the tree.
1269
1342
 
1270
1343
        Use TreeTransform.finalize() to release the lock (can be omitted if
1321
1394
    def tree_kind(self, trans_id):
1322
1395
        """Determine the file kind in the working tree.
1323
1396
 
1324
 
        Raises NoSuchFile if the file does not exist
 
1397
        :returns: The file kind or None if the file does not exist
1325
1398
        """
1326
1399
        path = self._tree_id_paths.get(trans_id)
1327
1400
        if path is None:
1328
 
            raise NoSuchFile(None)
 
1401
            return None
1329
1402
        try:
1330
1403
            return file_kind(self._tree.abspath(path))
1331
 
        except OSError, e:
1332
 
            if e.errno != errno.ENOENT:
1333
 
                raise
1334
 
            else:
1335
 
                raise NoSuchFile(path)
 
1404
        except errors.NoSuchFile:
 
1405
            return None
1336
1406
 
1337
1407
    def _set_mode(self, trans_id, mode_id, typefunc):
1338
1408
        """Set the mode of new file contents.
1507
1577
                if file_id is None:
1508
1578
                    continue
1509
1579
                needs_entry = False
1510
 
                try:
1511
 
                    kind = self.final_kind(trans_id)
1512
 
                except NoSuchFile:
 
1580
                kind = self.final_kind(trans_id)
 
1581
                if kind is None:
1513
1582
                    kind = self._tree.stored_kind(file_id)
1514
1583
                parent_trans_id = self.final_parent(trans_id)
1515
1584
                parent_file_id = new_path_file_ids.get(parent_trans_id)
1553
1622
                child_pb.update('removing file', num, len(tree_paths))
1554
1623
                full_path = self._tree.abspath(path)
1555
1624
                if trans_id in self._removed_contents:
1556
 
                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
1557
 
                                     trans_id))
1558
 
                elif trans_id in self._new_name or trans_id in \
1559
 
                    self._new_parent:
 
1625
                    delete_path = os.path.join(self._deletiondir, trans_id)
 
1626
                    mover.pre_delete(full_path, delete_path)
 
1627
                elif (trans_id in self._new_name
 
1628
                      or trans_id in self._new_parent):
1560
1629
                    try:
1561
1630
                        mover.rename(full_path, self._limbo_name(trans_id))
1562
 
                    except OSError, e:
 
1631
                    except errors.TransformRenameFailed, e:
1563
1632
                        if e.errno != errno.ENOENT:
1564
1633
                            raise
1565
1634
                    else:
1590
1659
                if trans_id in self._needs_rename:
1591
1660
                    try:
1592
1661
                        mover.rename(self._limbo_name(trans_id), full_path)
1593
 
                    except OSError, e:
 
1662
                    except errors.TransformRenameFailed, e:
1594
1663
                        # We may be renaming a dangling inventory id
1595
1664
                        if e.errno != errno.ENOENT:
1596
1665
                            raise
1616
1685
    unversioned files in the input tree.
1617
1686
    """
1618
1687
 
1619
 
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
 
1688
    def __init__(self, tree, pb=None, case_sensitive=True):
1620
1689
        tree.lock_read()
1621
1690
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1622
1691
        DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1627
1696
    def tree_kind(self, trans_id):
1628
1697
        path = self._tree_id_paths.get(trans_id)
1629
1698
        if path is None:
1630
 
            raise NoSuchFile(None)
 
1699
            return None
1631
1700
        file_id = self._tree.path2id(path)
1632
 
        return self._tree.kind(file_id)
 
1701
        try:
 
1702
            return self._tree.kind(file_id)
 
1703
        except errors.NoSuchFile:
 
1704
            return None
1633
1705
 
1634
1706
    def _set_mode(self, trans_id, mode_id, typefunc):
1635
1707
        """Set the mode of new file contents.
1694
1766
        parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1695
1767
                       self._iter_parent_trees()]
1696
1768
        vf.add_lines((file_id, tree_revision), parent_keys,
1697
 
                     self.get_file(file_id).readlines())
 
1769
                     self.get_file_lines(file_id))
1698
1770
        repo = self._get_repository()
1699
1771
        base_vf = repo.texts
1700
1772
        if base_vf not in vf.fallback_versionedfiles:
1722
1794
            executable = self.is_executable(file_id, path)
1723
1795
        return kind, executable, None
1724
1796
 
 
1797
    def is_locked(self):
 
1798
        return False
 
1799
 
1725
1800
    def lock_read(self):
1726
1801
        # Perhaps in theory, this should lock the TreeTransform?
1727
 
        pass
 
1802
        return self
1728
1803
 
1729
1804
    def unlock(self):
1730
1805
        pass
1828
1903
            if (specific_file_ids is not None
1829
1904
                and file_id not in specific_file_ids):
1830
1905
                continue
1831
 
            try:
1832
 
                kind = self._transform.final_kind(trans_id)
1833
 
            except NoSuchFile:
 
1906
            kind = self._transform.final_kind(trans_id)
 
1907
            if kind is None:
1834
1908
                kind = self._transform._tree.stored_kind(file_id)
1835
1909
            new_entry = inventory.make_entry(
1836
1910
                kind,
2068
2142
                path_from_root = self._final_paths.get_path(child_id)
2069
2143
                basename = self._transform.final_name(child_id)
2070
2144
                file_id = self._transform.final_file_id(child_id)
2071
 
                try:
2072
 
                    kind = self._transform.final_kind(child_id)
 
2145
                kind  = self._transform.final_kind(child_id)
 
2146
                if kind is not None:
2073
2147
                    versioned_kind = kind
2074
 
                except NoSuchFile:
 
2148
                else:
2075
2149
                    kind = 'unknown'
2076
2150
                    versioned_kind = self._transform._tree.stored_kind(file_id)
2077
2151
                if versioned_kind == 'directory':
2190
2264
    for num, _unused in enumerate(wt.all_file_ids()):
2191
2265
        if num > 0:  # more than just a root
2192
2266
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2193
 
    existing_files = set()
2194
 
    for dir, files in wt.walkdirs():
2195
 
        existing_files.update(f[0] for f in files)
2196
2267
    file_trans_id = {}
2197
2268
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2198
2269
    pp = ProgressPhase("Build phase", 2, top_pb)
2222
2293
                precomputed_delta = []
2223
2294
            else:
2224
2295
                precomputed_delta = None
 
2296
            # Check if tree inventory has content. If so, we populate
 
2297
            # existing_files with the directory content. If there are no
 
2298
            # entries we skip populating existing_files as its not used.
 
2299
            # This improves performance and unncessary work on large
 
2300
            # directory trees. (#501307)
 
2301
            if total > 0:
 
2302
                existing_files = set()
 
2303
                for dir, files in wt.walkdirs():
 
2304
                    existing_files.update(f[0] for f in files)
2225
2305
            for num, (tree_path, entry) in \
2226
2306
                enumerate(tree.inventory.iter_entries_by_dir()):
2227
2307
                pb.update("Building tree", num - len(deferred_contents), total)
2359
2439
    if entry.kind == "directory":
2360
2440
        return True
2361
2441
    if entry.kind == "file":
2362
 
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2363
 
            return True
 
2442
        f = file(target_path, 'rb')
 
2443
        try:
 
2444
            if tree.get_file_text(file_id) == f.read():
 
2445
                return True
 
2446
        finally:
 
2447
            f.close()
2364
2448
    elif entry.kind == "symlink":
2365
2449
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
2366
2450
            return True
2418
2502
        raise errors.BadFileKindError(name, kind)
2419
2503
 
2420
2504
 
2421
 
@deprecated_function(deprecated_in((1, 9, 0)))
2422
 
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2423
 
    """Create new file contents according to an inventory entry.
2424
 
 
2425
 
    DEPRECATED.  Use create_from_tree instead.
2426
 
    """
2427
 
    if entry.kind == "file":
2428
 
        if lines is None:
2429
 
            lines = tree.get_file(entry.file_id).readlines()
2430
 
        tt.create_file(lines, trans_id, mode_id=mode_id)
2431
 
    elif entry.kind == "symlink":
2432
 
        tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2433
 
    elif entry.kind == "directory":
2434
 
        tt.create_directory(trans_id)
2435
 
 
2436
 
 
2437
2505
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2438
2506
    filter_tree_path=None):
2439
2507
    """Create new file contents according to tree contents.
2514
2582
 
2515
2583
 
2516
2584
def revert(working_tree, target_tree, filenames, backups=False,
2517
 
           pb=DummyProgress(), change_reporter=None):
 
2585
           pb=None, change_reporter=None):
2518
2586
    """Revert a working tree's contents to those of a target tree."""
2519
2587
    target_tree.lock_read()
 
2588
    pb = ui.ui_factory.nested_progress_bar()
2520
2589
    tt = TreeTransform(working_tree, pb)
2521
2590
    try:
2522
2591
        pp = ProgressPhase("Revert phase", 3, pb)
2541
2610
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2542
2611
                              backups, pp, basis_tree=None,
2543
2612
                              merge_modified=None):
2544
 
    pp.next_phase()
2545
2613
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2546
2614
    try:
2547
2615
        if merge_modified is None:
2551
2619
                                      merge_modified, basis_tree)
2552
2620
    finally:
2553
2621
        child_pb.finished()
2554
 
    pp.next_phase()
2555
2622
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2556
2623
    try:
2557
2624
        raw_conflicts = resolve_conflicts(tt, child_pb,
2650
2717
                    parent_trans = ROOT_PARENT
2651
2718
                else:
2652
2719
                    parent_trans = tt.trans_id_file_id(parent[1])
2653
 
                tt.adjust_path(name[1], parent_trans, trans_id)
 
2720
                if parent[0] is None and versioned[0]:
 
2721
                    tt.adjust_root_path(name[1], parent_trans)
 
2722
                else:
 
2723
                    tt.adjust_path(name[1], parent_trans, trans_id)
2654
2724
            if executable[0] != executable[1] and kind[1] == "file":
2655
2725
                tt.set_executability(executable[1], trans_id)
2656
2726
        if working_tree.supports_content_filtering():
2669
2739
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2670
2740
                deferred_files):
2671
2741
                tt.create_file(bytes, trans_id, mode_id)
 
2742
        tt.fixup_new_roots()
2672
2743
    finally:
2673
2744
        if basis_tree is not None:
2674
2745
            basis_tree.unlock()
2675
2746
    return merge_modified
2676
2747
 
2677
2748
 
2678
 
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
 
2749
def resolve_conflicts(tt, pb=None, pass_func=None):
2679
2750
    """Make many conflict-resolution attempts, but die if they fail"""
2680
2751
    if pass_func is None:
2681
2752
        pass_func = conflict_pass
2682
2753
    new_conflicts = set()
 
2754
    pb = ui.ui_factory.nested_progress_bar()
2683
2755
    try:
2684
2756
        for n in range(10):
2685
2757
            pb.update('Resolution pass', n+1, 10)
2689
2761
            new_conflicts.update(pass_func(tt, conflicts))
2690
2762
        raise MalformedTransform(conflicts=conflicts)
2691
2763
    finally:
2692
 
        pb.clear()
 
2764
        pb.finished()
2693
2765
 
2694
2766
 
2695
2767
def conflict_pass(tt, conflicts, path_tree=None):
2744
2816
                        # special-case the other tree root (move its
2745
2817
                        # children to current root)
2746
2818
                        if entry.parent_id is None:
2747
 
                            create=False
 
2819
                            create = False
2748
2820
                            moved = _reparent_transform_children(
2749
2821
                                tt, trans_id, tt.root)
2750
2822
                            for child in moved:
2818
2890
        self.pending_deletions = []
2819
2891
 
2820
2892
    def rename(self, from_, to):
2821
 
        """Rename a file from one path to another.  Functions like os.rename"""
 
2893
        """Rename a file from one path to another."""
2822
2894
        try:
2823
2895
            os.rename(from_, to)
2824
2896
        except OSError, e:
2825
2897
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2826
2898
                raise errors.FileExists(to, str(e))
2827
 
            raise
 
2899
            # normal OSError doesn't include filenames so it's hard to see where
 
2900
            # the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
 
2901
            raise errors.TransformRenameFailed(from_, to, str(e), e.errno)
2828
2902
        self.past_renames.append((from_, to))
2829
2903
 
2830
2904
    def pre_delete(self, from_, to):
2840
2914
    def rollback(self):
2841
2915
        """Reverse all renames that have been performed"""
2842
2916
        for from_, to in reversed(self.past_renames):
2843
 
            os.rename(to, from_)
 
2917
            try:
 
2918
                os.rename(to, from_)
 
2919
            except OSError, e:
 
2920
                raise errors.TransformRenameFailed(to, from_, str(e), e.errno)                
2844
2921
        # after rollback, don't reuse _FileMover
2845
2922
        past_renames = None
2846
2923
        pending_deletions = None