~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: 2010-09-29 22:03:03 UTC
  • mfrom: (5416.2.6 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100929220303-cr95h8iwtggco721
(mbp) Add 'break-lock --force'

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
    annotate,
26
26
    bencode,
27
27
    bzrdir,
 
28
    commit,
28
29
    delta,
29
30
    errors,
30
31
    inventory,
35
36
    )
36
37
""")
37
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, NotVersionedError, CantMoveRoot,
 
39
                           ReusingTransform, CantMoveRoot,
39
40
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
40
41
                           UnableCreateSymlink)
41
42
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
314
315
 
315
316
    def delete_contents(self, trans_id):
316
317
        """Schedule the contents of a path entry for deletion"""
317
 
        # Ensure that the object exists in the WorkingTree, this will raise an
318
 
        # exception if there is a problem
319
 
        self.tree_kind(trans_id)
320
 
        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)
321
321
 
322
322
    def cancel_deletion(self, trans_id):
323
323
        """Cancel a scheduled deletion"""
388
388
        changed_kind = set(self._removed_contents)
389
389
        changed_kind.intersection_update(self._new_contents)
390
390
        changed_kind.difference_update(new_ids)
391
 
        changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
392
 
                        self.final_kind(t))
 
391
        changed_kind = (t for t in changed_kind
 
392
                        if self.tree_kind(t) != self.final_kind(t))
393
393
        new_ids.update(changed_kind)
394
394
        return sorted(FinalPaths(self).get_paths(new_ids))
395
395
 
396
396
    def final_kind(self, trans_id):
397
397
        """Determine the final file kind, after any changes applied.
398
398
 
399
 
        Raises NoSuchFile if the file does not exist/has no contents.
400
 
        (It is conceivable that a path would be created without the
401
 
        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)
402
402
        """
403
403
        if trans_id in self._new_contents:
404
404
            return self._new_contents[trans_id]
405
405
        elif trans_id in self._removed_contents:
406
 
            raise NoSuchFile(None)
 
406
            return None
407
407
        else:
408
408
            return self.tree_kind(trans_id)
409
409
 
595
595
        """
596
596
        conflicts = []
597
597
        for trans_id in self._new_id.iterkeys():
598
 
            try:
599
 
                kind = self.final_kind(trans_id)
600
 
            except NoSuchFile:
 
598
            kind = self.final_kind(trans_id)
 
599
            if kind is None:
601
600
                conflicts.append(('versioning no contents', trans_id))
602
601
                continue
603
602
            if not InventoryEntry.versionable_kind(kind):
617
616
            if self.final_file_id(trans_id) is None:
618
617
                conflicts.append(('unversioned executability', trans_id))
619
618
            else:
620
 
                try:
621
 
                    non_file = self.final_kind(trans_id) != "file"
622
 
                except NoSuchFile:
623
 
                    non_file = True
624
 
                if non_file is True:
 
619
                if self.final_kind(trans_id) != "file":
625
620
                    conflicts.append(('non-file executability', trans_id))
626
621
        return conflicts
627
622
 
629
624
        """Check for overwrites (not permitted on Win32)"""
630
625
        conflicts = []
631
626
        for trans_id in self._new_contents:
632
 
            try:
633
 
                self.tree_kind(trans_id)
634
 
            except NoSuchFile:
 
627
            if self.tree_kind(trans_id) is None:
635
628
                continue
636
629
            if trans_id not in self._removed_contents:
637
630
                conflicts.append(('overwrite', trans_id,
651
644
            last_name = None
652
645
            last_trans_id = None
653
646
            for name, trans_id in name_ids:
654
 
                try:
655
 
                    kind = self.final_kind(trans_id)
656
 
                except NoSuchFile:
657
 
                    kind = None
 
647
                kind = self.final_kind(trans_id)
658
648
                file_id = self.final_file_id(trans_id)
659
649
                if kind is None and file_id is None:
660
650
                    continue
686
676
                continue
687
677
            if not self._any_contents(children):
688
678
                continue
689
 
            for child in children:
690
 
                try:
691
 
                    self.final_kind(child)
692
 
                except NoSuchFile:
693
 
                    continue
694
 
            try:
695
 
                kind = self.final_kind(parent_id)
696
 
            except NoSuchFile:
697
 
                kind = None
 
679
            kind = self.final_kind(parent_id)
698
680
            if kind is None:
699
681
                conflicts.append(('missing parent', parent_id))
700
682
            elif kind != "directory":
704
686
    def _any_contents(self, trans_ids):
705
687
        """Return true if any of the trans_ids, will have contents."""
706
688
        for trans_id in trans_ids:
707
 
            try:
708
 
                kind = self.final_kind(trans_id)
709
 
            except NoSuchFile:
710
 
                continue
711
 
            return True
 
689
            if self.final_kind(trans_id) is not None:
 
690
                return True
712
691
        return False
713
692
 
714
693
    def _set_executability(self, path, trans_id):
844
823
        Return a (name, parent, kind, executable) tuple
845
824
        """
846
825
        to_name = self.final_name(to_trans_id)
847
 
        try:
848
 
            to_kind = self.final_kind(to_trans_id)
849
 
        except NoSuchFile:
850
 
            to_kind = None
 
826
        to_kind = self.final_kind(to_trans_id)
851
827
        to_parent = self.final_file_id(self.final_parent(to_trans_id))
852
828
        if to_trans_id in self._new_executability:
853
829
            to_executable = self._new_executability[to_trans_id]
927
903
        """
928
904
        return _PreviewTree(self)
929
905
 
930
 
    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):
931
909
        """Commit the result of this TreeTransform to a branch.
932
910
 
933
911
        :param branch: The branch to commit to.
934
912
        :param message: The message to attach to the commit.
935
 
        :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.)
936
927
        :return: The revision_id of the revision committed.
937
928
        """
938
929
        self._check_malformed()
955
946
        if self._tree.get_revision_id() != last_rev_id:
956
947
            raise ValueError('TreeTransform not based on branch basis: %s' %
957
948
                             self._tree.get_revision_id())
958
 
        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)
959
956
        preview = self.get_preview_tree()
960
957
        list(builder.record_iter_changes(preview, last_rev_id,
961
958
                                         self.iter_changes()))
1397
1394
    def tree_kind(self, trans_id):
1398
1395
        """Determine the file kind in the working tree.
1399
1396
 
1400
 
        Raises NoSuchFile if the file does not exist
 
1397
        :returns: The file kind or None if the file does not exist
1401
1398
        """
1402
1399
        path = self._tree_id_paths.get(trans_id)
1403
1400
        if path is None:
1404
 
            raise NoSuchFile(None)
 
1401
            return None
1405
1402
        try:
1406
1403
            return file_kind(self._tree.abspath(path))
1407
 
        except OSError, e:
1408
 
            if e.errno != errno.ENOENT:
1409
 
                raise
1410
 
            else:
1411
 
                raise NoSuchFile(path)
 
1404
        except errors.NoSuchFile:
 
1405
            return None
1412
1406
 
1413
1407
    def _set_mode(self, trans_id, mode_id, typefunc):
1414
1408
        """Set the mode of new file contents.
1583
1577
                if file_id is None:
1584
1578
                    continue
1585
1579
                needs_entry = False
1586
 
                try:
1587
 
                    kind = self.final_kind(trans_id)
1588
 
                except NoSuchFile:
 
1580
                kind = self.final_kind(trans_id)
 
1581
                if kind is None:
1589
1582
                    kind = self._tree.stored_kind(file_id)
1590
1583
                parent_trans_id = self.final_parent(trans_id)
1591
1584
                parent_file_id = new_path_file_ids.get(parent_trans_id)
1635
1628
                      or trans_id in self._new_parent):
1636
1629
                    try:
1637
1630
                        mover.rename(full_path, self._limbo_name(trans_id))
1638
 
                    except OSError, e:
 
1631
                    except errors.TransformRenameFailed, e:
1639
1632
                        if e.errno != errno.ENOENT:
1640
1633
                            raise
1641
1634
                    else:
1666
1659
                if trans_id in self._needs_rename:
1667
1660
                    try:
1668
1661
                        mover.rename(self._limbo_name(trans_id), full_path)
1669
 
                    except OSError, e:
 
1662
                    except errors.TransformRenameFailed, e:
1670
1663
                        # We may be renaming a dangling inventory id
1671
1664
                        if e.errno != errno.ENOENT:
1672
1665
                            raise
1703
1696
    def tree_kind(self, trans_id):
1704
1697
        path = self._tree_id_paths.get(trans_id)
1705
1698
        if path is None:
1706
 
            raise NoSuchFile(None)
 
1699
            return None
1707
1700
        file_id = self._tree.path2id(path)
1708
 
        return self._tree.kind(file_id)
 
1701
        try:
 
1702
            return self._tree.kind(file_id)
 
1703
        except errors.NoSuchFile:
 
1704
            return None
1709
1705
 
1710
1706
    def _set_mode(self, trans_id, mode_id, typefunc):
1711
1707
        """Set the mode of new file contents.
1770
1766
        parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1771
1767
                       self._iter_parent_trees()]
1772
1768
        vf.add_lines((file_id, tree_revision), parent_keys,
1773
 
                     self.get_file(file_id).readlines())
 
1769
                     self.get_file_lines(file_id))
1774
1770
        repo = self._get_repository()
1775
1771
        base_vf = repo.texts
1776
1772
        if base_vf not in vf.fallback_versionedfiles:
1798
1794
            executable = self.is_executable(file_id, path)
1799
1795
        return kind, executable, None
1800
1796
 
 
1797
    def is_locked(self):
 
1798
        return False
 
1799
 
1801
1800
    def lock_read(self):
1802
1801
        # Perhaps in theory, this should lock the TreeTransform?
1803
 
        pass
 
1802
        return self
1804
1803
 
1805
1804
    def unlock(self):
1806
1805
        pass
1904
1903
            if (specific_file_ids is not None
1905
1904
                and file_id not in specific_file_ids):
1906
1905
                continue
1907
 
            try:
1908
 
                kind = self._transform.final_kind(trans_id)
1909
 
            except NoSuchFile:
 
1906
            kind = self._transform.final_kind(trans_id)
 
1907
            if kind is None:
1910
1908
                kind = self._transform._tree.stored_kind(file_id)
1911
1909
            new_entry = inventory.make_entry(
1912
1910
                kind,
2144
2142
                path_from_root = self._final_paths.get_path(child_id)
2145
2143
                basename = self._transform.final_name(child_id)
2146
2144
                file_id = self._transform.final_file_id(child_id)
2147
 
                try:
2148
 
                    kind = self._transform.final_kind(child_id)
 
2145
                kind  = self._transform.final_kind(child_id)
 
2146
                if kind is not None:
2149
2147
                    versioned_kind = kind
2150
 
                except NoSuchFile:
 
2148
                else:
2151
2149
                    kind = 'unknown'
2152
2150
                    versioned_kind = self._transform._tree.stored_kind(file_id)
2153
2151
                if versioned_kind == 'directory':
2266
2264
    for num, _unused in enumerate(wt.all_file_ids()):
2267
2265
        if num > 0:  # more than just a root
2268
2266
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2269
 
    existing_files = set()
2270
 
    for dir, files in wt.walkdirs():
2271
 
        existing_files.update(f[0] for f in files)
2272
2267
    file_trans_id = {}
2273
2268
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2274
2269
    pp = ProgressPhase("Build phase", 2, top_pb)
2298
2293
                precomputed_delta = []
2299
2294
            else:
2300
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)
2301
2305
            for num, (tree_path, entry) in \
2302
2306
                enumerate(tree.inventory.iter_entries_by_dir()):
2303
2307
                pb.update("Building tree", num - len(deferred_contents), total)
2435
2439
    if entry.kind == "directory":
2436
2440
        return True
2437
2441
    if entry.kind == "file":
2438
 
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2439
 
            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()
2440
2448
    elif entry.kind == "symlink":
2441
2449
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
2442
2450
            return True
2494
2502
        raise errors.BadFileKindError(name, kind)
2495
2503
 
2496
2504
 
2497
 
@deprecated_function(deprecated_in((1, 9, 0)))
2498
 
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2499
 
    """Create new file contents according to an inventory entry.
2500
 
 
2501
 
    DEPRECATED.  Use create_from_tree instead.
2502
 
    """
2503
 
    if entry.kind == "file":
2504
 
        if lines is None:
2505
 
            lines = tree.get_file(entry.file_id).readlines()
2506
 
        tt.create_file(lines, trans_id, mode_id=mode_id)
2507
 
    elif entry.kind == "symlink":
2508
 
        tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2509
 
    elif entry.kind == "directory":
2510
 
        tt.create_directory(trans_id)
2511
 
 
2512
 
 
2513
2505
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2514
2506
    filter_tree_path=None):
2515
2507
    """Create new file contents according to tree contents.
2824
2816
                        # special-case the other tree root (move its
2825
2817
                        # children to current root)
2826
2818
                        if entry.parent_id is None:
2827
 
                            create=False
 
2819
                            create = False
2828
2820
                            moved = _reparent_transform_children(
2829
2821
                                tt, trans_id, tt.root)
2830
2822
                            for child in moved:
2898
2890
        self.pending_deletions = []
2899
2891
 
2900
2892
    def rename(self, from_, to):
2901
 
        """Rename a file from one path to another.  Functions like os.rename"""
 
2893
        """Rename a file from one path to another."""
2902
2894
        try:
2903
2895
            os.rename(from_, to)
2904
2896
        except OSError, e:
2905
2897
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2906
2898
                raise errors.FileExists(to, str(e))
2907
 
            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)
2908
2902
        self.past_renames.append((from_, to))
2909
2903
 
2910
2904
    def pre_delete(self, from_, to):
2920
2914
    def rollback(self):
2921
2915
        """Reverse all renames that have been performed"""
2922
2916
        for from_, to in reversed(self.past_renames):
2923
 
            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)                
2924
2921
        # after rollback, don't reuse _FileMover
2925
2922
        past_renames = None
2926
2923
        pending_deletions = None