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
1=== modified file 'breezy/tests/test_workspace.py'
2--- breezy/tests/test_workspace.py 2020-08-06 21:52:51 +0000
3+++ breezy/tests/test_workspace.py 2022-01-17 02:08:04 +0000
4@@ -96,6 +96,29 @@
5 WorkspaceDirty, check_clean_tree,
6 tree)
7
8+ def test_subpath(self):
9+ tree = self.make_test_tree()
10+ self.build_tree_contents([("debian/foo", "blah"), ("foo/",)])
11+ tree.add("foo")
12+ tree.commit("add foo")
13+ with tree.lock_write():
14+ check_clean_tree(tree, tree.basis_tree(), subpath="foo")
15+ self.assertRaises(
16+ WorkspaceDirty, check_clean_tree, tree, tree.basis_tree(), subpath=""
17+ )
18+
19+ def test_subpath_changed(self):
20+ tree = self.make_test_tree()
21+ self.build_tree_contents([("foo/",)])
22+ tree.add("foo")
23+ tree.commit("add foo")
24+ self.build_tree_contents([("debian/control", "blah")])
25+ with tree.lock_write():
26+ check_clean_tree(tree, tree.basis_tree(), subpath="foo")
27+ self.assertRaises(
28+ WorkspaceDirty, check_clean_tree, tree, tree.basis_tree(), subpath=""
29+ )
30+
31
32 def vary_by_inotify():
33 return [
34
35=== modified file 'breezy/workspace.py'
36--- breezy/workspace.py 2021-07-04 16:44:42 +0000
37+++ breezy/workspace.py 2022-01-17 02:08:04 +0000
38@@ -22,15 +22,18 @@
39
40 from __future__ import absolute_import
41
42+from contextlib import ExitStack
43 import errno
44 import os
45 import shutil
46-
47-
48-from .clean_tree import iter_deletables
49-from .errors import BzrError, DependencyNotPresent
50+from typing import Optional, List
51+
52+
53+from .errors import BzrError, DependencyNotPresent, NoSuchFile
54+from .osutils import is_inside
55 from .trace import warning
56 from .transform import revert
57+from .tree import Tree
58 from .workingtree import WorkingTree
59
60
61@@ -42,49 +45,48 @@
62
63
64 # TODO(jelmer): Move to .clean_tree?
65-def reset_tree(local_tree, subpath=''):
66+def reset_tree(
67+ local_tree: WorkingTree,
68+ basis_tree: Optional[Tree] = None,
69+ subpath: str = "",
70+ dirty_tracker: "DirtyTracker" = None,
71+) -> None:
72 """Reset a tree back to its basis tree.
73
74 This will leave ignored and detritus files alone.
75
76 Args:
77 local_tree: tree to work on
78+ dirty_tracker: Optional dirty tracker
79 subpath: Subpath to operate on
80 """
81- revert(local_tree, local_tree.branch.basis_tree(),
82- [subpath] if subpath else None)
83- deletables = list(iter_deletables(
84- local_tree, unknown=True, ignored=False, detritus=False))
85+ if dirty_tracker and not dirty_tracker.is_dirty():
86+ return
87+ if basis_tree is None:
88+ basis_tree = local_tree.branch.basis_tree()
89+ revert(local_tree, basis_tree, [subpath] if subpath else None)
90+ deletables: List[str] = []
91+ # TODO(jelmer): Use basis tree
92+ for p in local_tree.extras():
93+ if not is_inside(subpath, p):
94+ continue
95+ if not local_tree.is_ignored(p):
96+ deletables.append(local_tree.abspath(p))
97 delete_items(deletables)
98
99
100-# TODO(jelmer): Move to .clean_tree?
101-def check_clean_tree(local_tree):
102- """Check that a tree is clean and has no pending changes or unknown files.
103-
104- Args:
105- local_tree: The tree to check
106- Raises:
107- PendingChanges: When there are pending changes
108- """
109- # Just check there are no changes to begin with
110- if local_tree.has_changes():
111- raise WorkspaceDirty(local_tree.abspath('.'))
112- if list(local_tree.unknowns()):
113- raise WorkspaceDirty(local_tree.abspath('.'))
114-
115-
116-def delete_items(deletables, dry_run=False):
117+def delete_items(deletables, dry_run: bool = False):
118 """Delete files in the deletables iterable"""
119+
120 def onerror(function, path, excinfo):
121- """Show warning for errors seen by rmtree.
122- """
123+ """Show warning for errors seen by rmtree."""
124 # Handle only permission error while removing files.
125 # Other errors are re-raised.
126 if function is not os.remove or excinfo[1].errno != errno.EACCES:
127 raise
128- warning('unable to remove %s' % path)
129- for path, subp in deletables:
130+ warning("unable to remove %s", path)
131+
132+ for path in deletables:
133 if os.path.isdir(path):
134 shutil.rmtree(path, onerror=onerror)
135 else:
136@@ -97,6 +99,55 @@
137 warning('unable to remove "%s": %s.', path, e.strerror)
138
139
140+# TODO(jelmer): Move to .clean_tree?
141+def check_clean_tree(
142+ local_tree: WorkingTree, basis_tree: Optional[Tree] = None,
143+ subpath: str = ""
144+) -> None:
145+ """Check that a tree is clean and has no pending changes or unknown files.
146+
147+ Args:
148+ local_tree: The tree to check
149+ basis_tree: Tree to check against
150+ subpath: Subpath of the tree to check
151+ Raises:
152+ PendingChanges: When there are pending changes
153+ """
154+ with ExitStack() as es:
155+ if basis_tree is None:
156+ es.enter_context(local_tree.lock_read())
157+ basis_tree = local_tree.basis_tree()
158+ # Just check there are no changes to begin with
159+ changes = local_tree.iter_changes(
160+ basis_tree,
161+ include_unchanged=False,
162+ require_versioned=False,
163+ want_unversioned=True,
164+ specific_files=[subpath],
165+ )
166+
167+ def relevant(p, t):
168+ if not p:
169+ return False
170+ if not is_inside(subpath, p):
171+ return False
172+ if t.is_ignored(p):
173+ return False
174+ try:
175+ if not t.has_versioned_directories() and t.kind(p) == "directory":
176+ return False
177+ except NoSuchFile:
178+ return True
179+ return True
180+
181+ if any(
182+ change
183+ for change in changes
184+ if relevant(change.path[0], basis_tree) or relevant(change.path[1], local_tree)
185+ ):
186+ raise WorkspaceDirty(local_tree.abspath(subpath))
187+
188+
189 def get_dirty_tracker(local_tree, subpath='', use_inotify=None):
190 """Create a dirty tracker object."""
191 if use_inotify is True:
192@@ -158,7 +209,7 @@
193 """
194 if self._dirty_tracker and not self._dirty_tracker.is_dirty():
195 return
196- reset_tree(self.tree, self.subpath)
197+ reset_tree(self.tree, subpath=self.subpath)
198 if self._dirty_tracker is not None:
199 self._dirty_tracker.mark_clean()
200

Subscribers

People subscribed via source and target branches