Merge lp:~jelmer/brz/workspace into lp:brz/3.2

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/workspace
Merge into: lp:brz/3.2
Diff against target: 199 lines (+105/-31)
2 files modified
breezy/tests/test_workspace.py (+23/-0)
breezy/workspace.py (+82/-31)
To merge this branch: bzr merge lp:~jelmer/brz/workspace
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+414213@code.launchpad.net

Commit message

Import updates to workspace helper functions from lintian-brush.

Description of the change

Import updates to Workspace helper functions from lintian-brush.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'breezy/tests/test_workspace.py'
--- breezy/tests/test_workspace.py 2020-08-06 21:52:51 +0000
+++ breezy/tests/test_workspace.py 2022-01-17 02:08:04 +0000
@@ -96,6 +96,29 @@
96 WorkspaceDirty, check_clean_tree,96 WorkspaceDirty, check_clean_tree,
97 tree)97 tree)
9898
99 def test_subpath(self):
100 tree = self.make_test_tree()
101 self.build_tree_contents([("debian/foo", "blah"), ("foo/",)])
102 tree.add("foo")
103 tree.commit("add foo")
104 with tree.lock_write():
105 check_clean_tree(tree, tree.basis_tree(), subpath="foo")
106 self.assertRaises(
107 WorkspaceDirty, check_clean_tree, tree, tree.basis_tree(), subpath=""
108 )
109
110 def test_subpath_changed(self):
111 tree = self.make_test_tree()
112 self.build_tree_contents([("foo/",)])
113 tree.add("foo")
114 tree.commit("add foo")
115 self.build_tree_contents([("debian/control", "blah")])
116 with tree.lock_write():
117 check_clean_tree(tree, tree.basis_tree(), subpath="foo")
118 self.assertRaises(
119 WorkspaceDirty, check_clean_tree, tree, tree.basis_tree(), subpath=""
120 )
121
99122
100def vary_by_inotify():123def vary_by_inotify():
101 return [124 return [
102125
=== modified file 'breezy/workspace.py'
--- breezy/workspace.py 2021-07-04 16:44:42 +0000
+++ breezy/workspace.py 2022-01-17 02:08:04 +0000
@@ -22,15 +22,18 @@
2222
23from __future__ import absolute_import23from __future__ import absolute_import
2424
25from contextlib import ExitStack
25import errno26import errno
26import os27import os
27import shutil28import shutil
2829from typing import Optional, List
2930
30from .clean_tree import iter_deletables31
31from .errors import BzrError, DependencyNotPresent32from .errors import BzrError, DependencyNotPresent, NoSuchFile
33from .osutils import is_inside
32from .trace import warning34from .trace import warning
33from .transform import revert35from .transform import revert
36from .tree import Tree
34from .workingtree import WorkingTree37from .workingtree import WorkingTree
3538
3639
@@ -42,49 +45,48 @@
4245
4346
44# TODO(jelmer): Move to .clean_tree?47# TODO(jelmer): Move to .clean_tree?
45def reset_tree(local_tree, subpath=''):48def reset_tree(
49 local_tree: WorkingTree,
50 basis_tree: Optional[Tree] = None,
51 subpath: str = "",
52 dirty_tracker: "DirtyTracker" = None,
53) -> None:
46 """Reset a tree back to its basis tree.54 """Reset a tree back to its basis tree.
4755
48 This will leave ignored and detritus files alone.56 This will leave ignored and detritus files alone.
4957
50 Args:58 Args:
51 local_tree: tree to work on59 local_tree: tree to work on
60 dirty_tracker: Optional dirty tracker
52 subpath: Subpath to operate on61 subpath: Subpath to operate on
53 """62 """
54 revert(local_tree, local_tree.branch.basis_tree(),63 if dirty_tracker and not dirty_tracker.is_dirty():
55 [subpath] if subpath else None)64 return
56 deletables = list(iter_deletables(65 if basis_tree is None:
57 local_tree, unknown=True, ignored=False, detritus=False))66 basis_tree = local_tree.branch.basis_tree()
67 revert(local_tree, basis_tree, [subpath] if subpath else None)
68 deletables: List[str] = []
69 # TODO(jelmer): Use basis tree
70 for p in local_tree.extras():
71 if not is_inside(subpath, p):
72 continue
73 if not local_tree.is_ignored(p):
74 deletables.append(local_tree.abspath(p))
58 delete_items(deletables)75 delete_items(deletables)
5976
6077
61# TODO(jelmer): Move to .clean_tree?78def delete_items(deletables, dry_run: bool = False):
62def check_clean_tree(local_tree):
63 """Check that a tree is clean and has no pending changes or unknown files.
64
65 Args:
66 local_tree: The tree to check
67 Raises:
68 PendingChanges: When there are pending changes
69 """
70 # Just check there are no changes to begin with
71 if local_tree.has_changes():
72 raise WorkspaceDirty(local_tree.abspath('.'))
73 if list(local_tree.unknowns()):
74 raise WorkspaceDirty(local_tree.abspath('.'))
75
76
77def delete_items(deletables, dry_run=False):
78 """Delete files in the deletables iterable"""79 """Delete files in the deletables iterable"""
80
79 def onerror(function, path, excinfo):81 def onerror(function, path, excinfo):
80 """Show warning for errors seen by rmtree.82 """Show warning for errors seen by rmtree."""
81 """
82 # Handle only permission error while removing files.83 # Handle only permission error while removing files.
83 # Other errors are re-raised.84 # Other errors are re-raised.
84 if function is not os.remove or excinfo[1].errno != errno.EACCES:85 if function is not os.remove or excinfo[1].errno != errno.EACCES:
85 raise86 raise
86 warning('unable to remove %s' % path)87 warning("unable to remove %s", path)
87 for path, subp in deletables:88
89 for path in deletables:
88 if os.path.isdir(path):90 if os.path.isdir(path):
89 shutil.rmtree(path, onerror=onerror)91 shutil.rmtree(path, onerror=onerror)
90 else:92 else:
@@ -97,6 +99,55 @@
97 warning('unable to remove "%s": %s.', path, e.strerror)99 warning('unable to remove "%s": %s.', path, e.strerror)
98100
99101
102# TODO(jelmer): Move to .clean_tree?
103def check_clean_tree(
104 local_tree: WorkingTree, basis_tree: Optional[Tree] = None,
105 subpath: str = ""
106) -> None:
107 """Check that a tree is clean and has no pending changes or unknown files.
108
109 Args:
110 local_tree: The tree to check
111 basis_tree: Tree to check against
112 subpath: Subpath of the tree to check
113 Raises:
114 PendingChanges: When there are pending changes
115 """
116 with ExitStack() as es:
117 if basis_tree is None:
118 es.enter_context(local_tree.lock_read())
119 basis_tree = local_tree.basis_tree()
120 # Just check there are no changes to begin with
121 changes = local_tree.iter_changes(
122 basis_tree,
123 include_unchanged=False,
124 require_versioned=False,
125 want_unversioned=True,
126 specific_files=[subpath],
127 )
128
129 def relevant(p, t):
130 if not p:
131 return False
132 if not is_inside(subpath, p):
133 return False
134 if t.is_ignored(p):
135 return False
136 try:
137 if not t.has_versioned_directories() and t.kind(p) == "directory":
138 return False
139 except NoSuchFile:
140 return True
141 return True
142
143 if any(
144 change
145 for change in changes
146 if relevant(change.path[0], basis_tree) or relevant(change.path[1], local_tree)
147 ):
148 raise WorkspaceDirty(local_tree.abspath(subpath))
149
150
100def get_dirty_tracker(local_tree, subpath='', use_inotify=None):151def get_dirty_tracker(local_tree, subpath='', use_inotify=None):
101 """Create a dirty tracker object."""152 """Create a dirty tracker object."""
102 if use_inotify is True:153 if use_inotify is True:
@@ -158,7 +209,7 @@
158 """209 """
159 if self._dirty_tracker and not self._dirty_tracker.is_dirty():210 if self._dirty_tracker and not self._dirty_tracker.is_dirty():
160 return211 return
161 reset_tree(self.tree, self.subpath)212 reset_tree(self.tree, subpath=self.subpath)
162 if self._dirty_tracker is not None:213 if self._dirty_tracker is not None:
163 self._dirty_tracker.mark_clean()214 self._dirty_tracker.mark_clean()
164215

Subscribers

People subscribed via source and target branches