Merge lp:~jelmer/brz/iterobjectss into lp:brz/3.1
- iterobjectss
- Merge into 3.1
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/iterobjectss |
Merge into: | lp:brz/3.1 |
Diff against target: |
419 lines (+90/-198) 3 files modified
breezy/git/tests/test_workingtree.py (+20/-2) breezy/git/tree.py (+69/-195) breezy/git/workingtree.py (+1/-1) |
To merge this branch: | bzr merge lp:~jelmer/brz/iterobjectss |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+388958@code.launchpad.net |
Commit message
Simplify InterTree handling for Git trees.
Description of the change
Simplify InterTree handling for Git trees.
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/git/tests/test_workingtree.py' |
2 | --- breezy/git/tests/test_workingtree.py 2020-08-06 01:40:59 +0000 |
3 | +++ breezy/git/tests/test_workingtree.py 2020-08-08 18:37:30 +0000 |
4 | @@ -23,8 +23,11 @@ |
5 | import stat |
6 | |
7 | from dulwich import __version__ as dulwich_version |
8 | -from dulwich.diff_tree import RenameDetector |
9 | +from dulwich.diff_tree import RenameDetector, tree_changes |
10 | from dulwich.index import IndexEntry |
11 | +from dulwich.object_store import ( |
12 | + OverlayObjectStore, |
13 | + ) |
14 | from dulwich.objects import ( |
15 | S_IFGITLINK, |
16 | Blob, |
17 | @@ -42,7 +45,6 @@ |
18 | default_mapping, |
19 | ) |
20 | from ..tree import ( |
21 | - changes_between_git_tree_and_working_copy, |
22 | tree_delta_from_git_changes, |
23 | ) |
24 | from ..workingtree import ( |
25 | @@ -54,6 +56,22 @@ |
26 | ) |
27 | |
28 | |
29 | +def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target, |
30 | + want_unchanged=False, |
31 | + want_unversioned=False, |
32 | + rename_detector=None, |
33 | + include_trees=True): |
34 | + """Determine the changes between a git tree and a working tree with index. |
35 | + |
36 | + """ |
37 | + to_tree_sha, extras = target.git_snapshot(want_unversioned=want_unversioned) |
38 | + store = OverlayObjectStore([source_store, target.store]) |
39 | + return tree_changes( |
40 | + store, from_tree_sha, to_tree_sha, include_trees=include_trees, |
41 | + rename_detector=rename_detector, |
42 | + want_unchanged=want_unchanged, change_type_same=True), extras |
43 | + |
44 | + |
45 | class GitWorkingTreeTests(TestCaseWithTransport): |
46 | |
47 | def setUp(self): |
48 | |
49 | === modified file 'breezy/git/tree.py' |
50 | --- breezy/git/tree.py 2020-08-07 00:29:47 +0000 |
51 | +++ breezy/git/tree.py 2020-08-08 18:37:30 +0000 |
52 | @@ -264,7 +264,24 @@ |
53 | return path |
54 | |
55 | |
56 | -class GitRevisionTree(revisiontree.RevisionTree): |
57 | +class GitTree(_mod_tree.Tree): |
58 | + |
59 | + def iter_git_objects(self): |
60 | + """Iterate over all the objects in the tree. |
61 | + |
62 | + :return :Yields tuples with (path, sha, mode) |
63 | + """ |
64 | + raise NotImplementedError(self.iter_git_objects) |
65 | + |
66 | + def git_snapshot(self): |
67 | + """Snapshot a tree, and return tree object. |
68 | + |
69 | + :return: Tree sha and set of extras |
70 | + """ |
71 | + raise NotImplementedError(self.snapshot) |
72 | + |
73 | + |
74 | +class GitRevisionTree(revisiontree.RevisionTree, GitTree): |
75 | """Revision tree implementation based on Git objects.""" |
76 | |
77 | def __init__(self, repository, revision_id): |
78 | @@ -286,6 +303,9 @@ |
79 | raise errors.NoSuchRevision(repository, revision_id) |
80 | self.tree = commit.tree |
81 | |
82 | + def git_snapshot(self, want_unversioned=False): |
83 | + return self.tree, set() |
84 | + |
85 | def _submodule_info(self): |
86 | if self._submodules is None: |
87 | try: |
88 | @@ -977,10 +997,18 @@ |
89 | _matching_to_tree_format = None |
90 | _test_mutable_trees_to_test_trees = None |
91 | |
92 | + def __init__(self, source, target): |
93 | + super(InterGitTrees, self).__init__(source, target) |
94 | + if self.source.store == self.target.store: |
95 | + self.store = self.source.store |
96 | + else: |
97 | + self.store = OverlayObjectStore( |
98 | + [self.source.store, self.target.store]) |
99 | + self.rename_detector = RenameDetector(self.store) |
100 | + |
101 | @classmethod |
102 | def is_compatible(cls, source, target): |
103 | - return (isinstance(source, GitRevisionTree) and |
104 | - isinstance(target, GitRevisionTree)) |
105 | + return isinstance(source, GitTree) and isinstance(target, GitTree) |
106 | |
107 | def compare(self, want_unchanged=False, specific_files=None, |
108 | extra_trees=None, require_versioned=False, include_root=False, |
109 | @@ -1018,7 +1046,25 @@ |
110 | def _iter_git_changes(self, want_unchanged=False, specific_files=None, |
111 | require_versioned=False, extra_trees=None, |
112 | want_unversioned=False, include_trees=True): |
113 | - raise NotImplementedError(self._iter_git_changes) |
114 | + trees = [self.source] |
115 | + if extra_trees is not None: |
116 | + trees.extend(extra_trees) |
117 | + if specific_files is not None: |
118 | + specific_files = self.target.find_related_paths_across_trees( |
119 | + specific_files, trees, |
120 | + require_versioned=require_versioned) |
121 | + # TODO(jelmer): Restrict to specific_files, for performance reasons. |
122 | + with self.lock_read(): |
123 | + from_tree_sha, from_extras = self.source.git_snapshot( |
124 | + want_unversioned=want_unversioned) |
125 | + to_tree_sha, to_extras = self.target.git_snapshot( |
126 | + want_unversioned=want_unversioned) |
127 | + changes = tree_changes( |
128 | + self.store, from_tree_sha, to_tree_sha, |
129 | + include_trees=include_trees, |
130 | + rename_detector=self.rename_detector, |
131 | + want_unchanged=want_unchanged, change_type_same=True) |
132 | + return changes, from_extras, to_extras |
133 | |
134 | def find_target_path(self, path, recurse='none'): |
135 | ret = self.find_target_paths([path], recurse=recurse) |
136 | @@ -1073,48 +1119,10 @@ |
137 | return ret |
138 | |
139 | |
140 | -class InterGitRevisionTrees(InterGitTrees): |
141 | - """InterTree that works between two git revision trees.""" |
142 | - |
143 | - _matching_from_tree_format = None |
144 | - _matching_to_tree_format = None |
145 | - _test_mutable_trees_to_test_trees = None |
146 | - |
147 | - @classmethod |
148 | - def is_compatible(cls, source, target): |
149 | - return (isinstance(source, GitRevisionTree) and |
150 | - isinstance(target, GitRevisionTree)) |
151 | - |
152 | - def _iter_git_changes(self, want_unchanged=False, specific_files=None, |
153 | - require_versioned=True, extra_trees=None, |
154 | - want_unversioned=False, include_trees=True): |
155 | - trees = [self.source] |
156 | - if extra_trees is not None: |
157 | - trees.extend(extra_trees) |
158 | - if specific_files is not None: |
159 | - specific_files = self.target.find_related_paths_across_trees( |
160 | - specific_files, trees, |
161 | - require_versioned=require_versioned) |
162 | - |
163 | - if (self.source._repository._git.object_store != |
164 | - self.target._repository._git.object_store): |
165 | - store = OverlayObjectStore( |
166 | - [self.source._repository._git.object_store, |
167 | - self.target._repository._git.object_store]) |
168 | - else: |
169 | - store = self.source._repository._git.object_store |
170 | - rename_detector = RenameDetector(store) |
171 | - changes = tree_changes( |
172 | - store, self.source.tree, self.target.tree, |
173 | - want_unchanged=want_unchanged, include_trees=include_trees, |
174 | - change_type_same=True, rename_detector=rename_detector) |
175 | - return changes, set(), set() |
176 | - |
177 | - |
178 | -_mod_tree.InterTree.register_optimiser(InterGitRevisionTrees) |
179 | - |
180 | - |
181 | -class MutableGitIndexTree(mutabletree.MutableTree): |
182 | +_mod_tree.InterTree.register_optimiser(InterGitTrees) |
183 | + |
184 | + |
185 | +class MutableGitIndexTree(mutabletree.MutableTree, GitTree): |
186 | |
187 | def __init__(self): |
188 | self._lock_mode = None |
189 | @@ -1123,6 +1131,9 @@ |
190 | self._index_dirty = False |
191 | self._submodules = None |
192 | |
193 | + def git_snapshot(self, want_unversioned=False): |
194 | + return snapshot_workingtree(self, want_unversioned=want_unversioned) |
195 | + |
196 | def is_versioned(self, path): |
197 | with self.lock_read(): |
198 | path = encode_git_path(path.rstrip('/')) |
199 | @@ -1142,7 +1153,7 @@ |
200 | if self._lock_mode is None: |
201 | raise errors.ObjectNotLocked(self) |
202 | self._versioned_dirs = set() |
203 | - for p, i in self._recurse_index_entries(): |
204 | + for p, sha, mode in self.iter_git_objects(): |
205 | self._ensure_versioned_dir(posixpath.dirname(p)) |
206 | |
207 | def _ensure_versioned_dir(self, dirname): |
208 | @@ -1301,6 +1312,10 @@ |
209 | if self._versioned_dirs is not None: |
210 | self._ensure_versioned_dir(index_path) |
211 | |
212 | + def iter_git_objects(self): |
213 | + for p, entry in self._recurse_index_entries(): |
214 | + yield p, entry.sha, entry.mode |
215 | + |
216 | def _recurse_index_entries(self, index=None, basepath=b"", |
217 | recurse_nested=False): |
218 | # Iterate over all index entries |
219 | @@ -1398,7 +1413,7 @@ |
220 | if data is None: |
221 | data = self.branch.repository._git.object_store[sha].data |
222 | ie.text_sha1 = osutils.sha_string(data) |
223 | - ie.text_size = len(data) |
224 | + ie.text_size = size |
225 | ie.executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode) |
226 | return ie |
227 | |
228 | @@ -1687,131 +1702,6 @@ |
229 | return True |
230 | |
231 | |
232 | -class InterToIndexGitTree(InterGitTrees): |
233 | - """InterTree that works between a Git revision tree and an index.""" |
234 | - |
235 | - def __init__(self, source, target): |
236 | - super(InterToIndexGitTree, self).__init__(source, target) |
237 | - if self.source.store == self.target.store: |
238 | - self.store = self.source.store |
239 | - else: |
240 | - self.store = OverlayObjectStore( |
241 | - [self.source.store, self.target.store]) |
242 | - self.rename_detector = RenameDetector(self.store) |
243 | - |
244 | - @classmethod |
245 | - def is_compatible(cls, source, target): |
246 | - return (isinstance(source, GitRevisionTree) and |
247 | - isinstance(target, MutableGitIndexTree)) |
248 | - |
249 | - def _iter_git_changes(self, want_unchanged=False, specific_files=None, |
250 | - require_versioned=False, extra_trees=None, |
251 | - want_unversioned=False, include_trees=True): |
252 | - trees = [self.source] |
253 | - if extra_trees is not None: |
254 | - trees.extend(extra_trees) |
255 | - if specific_files is not None: |
256 | - specific_files = self.target.find_related_paths_across_trees( |
257 | - specific_files, trees, |
258 | - require_versioned=require_versioned) |
259 | - # TODO(jelmer): Restrict to specific_files, for performance reasons. |
260 | - with self.lock_read(): |
261 | - changes, target_extras = changes_between_git_tree_and_working_copy( |
262 | - self.source.store, self.source.tree, |
263 | - self.target, want_unchanged=want_unchanged, |
264 | - want_unversioned=want_unversioned, |
265 | - rename_detector=self.rename_detector, |
266 | - include_trees=include_trees) |
267 | - return changes, set(), target_extras |
268 | - |
269 | - |
270 | -_mod_tree.InterTree.register_optimiser(InterToIndexGitTree) |
271 | - |
272 | - |
273 | -class InterFromIndexGitTree(InterGitTrees): |
274 | - """InterTree that works between a Git revision tree and an index.""" |
275 | - |
276 | - def __init__(self, source, target): |
277 | - super(InterFromIndexGitTree, self).__init__(source, target) |
278 | - if self.source.store == self.target.store: |
279 | - self.store = self.source.store |
280 | - else: |
281 | - self.store = OverlayObjectStore( |
282 | - [self.source.store, self.target.store]) |
283 | - self.rename_detector = RenameDetector(self.store) |
284 | - |
285 | - @classmethod |
286 | - def is_compatible(cls, source, target): |
287 | - return (isinstance(target, GitRevisionTree) and |
288 | - isinstance(source, MutableGitIndexTree)) |
289 | - |
290 | - def _iter_git_changes(self, want_unchanged=False, specific_files=None, |
291 | - require_versioned=False, extra_trees=None, |
292 | - want_unversioned=False, include_trees=True): |
293 | - trees = [self.source] |
294 | - if extra_trees is not None: |
295 | - trees.extend(extra_trees) |
296 | - if specific_files is not None: |
297 | - specific_files = self.target.find_related_paths_across_trees( |
298 | - specific_files, trees, |
299 | - require_versioned=require_versioned) |
300 | - # TODO(jelmer): Restrict to specific_files, for performance reasons. |
301 | - with self.lock_read(): |
302 | - from_tree_sha, extras = snapshot_workingtree(self.source, want_unversioned=want_unversioned) |
303 | - return tree_changes( |
304 | - self.store, from_tree_sha, self.target.tree, |
305 | - include_trees=include_trees, |
306 | - rename_detector=self.rename_detector, |
307 | - want_unchanged=want_unchanged, change_type_same=True), extras |
308 | - |
309 | - |
310 | -_mod_tree.InterTree.register_optimiser(InterFromIndexGitTree) |
311 | - |
312 | - |
313 | -class InterIndexGitTree(InterGitTrees): |
314 | - """InterTree that works between a Git revision tree and an index.""" |
315 | - |
316 | - def __init__(self, source, target): |
317 | - super(InterIndexGitTree, self).__init__(source, target) |
318 | - if self.source.store == self.target.store: |
319 | - self.store = self.source.store |
320 | - else: |
321 | - self.store = OverlayObjectStore( |
322 | - [self.source.store, self.target.store]) |
323 | - self.rename_detector = RenameDetector(self.store) |
324 | - |
325 | - @classmethod |
326 | - def is_compatible(cls, source, target): |
327 | - return (isinstance(target, MutableGitIndexTree) and |
328 | - isinstance(source, MutableGitIndexTree)) |
329 | - |
330 | - def _iter_git_changes(self, want_unchanged=False, specific_files=None, |
331 | - require_versioned=False, extra_trees=None, |
332 | - want_unversioned=False, include_trees=True): |
333 | - trees = [self.source] |
334 | - if extra_trees is not None: |
335 | - trees.extend(extra_trees) |
336 | - if specific_files is not None: |
337 | - specific_files = self.target.find_related_paths_across_trees( |
338 | - specific_files, trees, |
339 | - require_versioned=require_versioned) |
340 | - # TODO(jelmer): Restrict to specific_files, for performance reasons. |
341 | - with self.lock_read(): |
342 | - from_tree_sha, from_extras = snapshot_workingtree( |
343 | - self.source, want_unversioned=want_unversioned) |
344 | - to_tree_sha, to_extras = snapshot_workingtree( |
345 | - self.target, want_unversioned=want_unversioned) |
346 | - changes = tree_changes( |
347 | - self.store, from_tree_sha, to_tree_sha, |
348 | - include_trees=include_trees, |
349 | - rename_detector=self.rename_detector, |
350 | - want_unchanged=want_unchanged, change_type_same=True) |
351 | - return changes, from_extras, to_extras |
352 | - |
353 | - |
354 | -_mod_tree.InterTree.register_optimiser(InterIndexGitTree) |
355 | - |
356 | - |
357 | def snapshot_workingtree(target, want_unversioned=False): |
358 | extras = set() |
359 | blobs = {} |
360 | @@ -1859,21 +1749,21 @@ |
361 | target.store.add_object(blob) |
362 | blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode)) |
363 | if want_unversioned: |
364 | - for e in target._iter_files_recursive(include_dirs=False): |
365 | + for extra in target._iter_files_recursive(include_dirs=False): |
366 | try: |
367 | - e, accessible = osutils.normalized_filename(e) |
368 | + extra, accessible = osutils.normalized_filename(extra) |
369 | except UnicodeDecodeError: |
370 | raise errors.BadFilenameEncoding( |
371 | - e, osutils._fs_enc) |
372 | - np = encode_git_path(e) |
373 | + extra, osutils._fs_enc) |
374 | + np = encode_git_path(extra) |
375 | if np in blobs: |
376 | continue |
377 | - st = target._lstat(e) |
378 | + st = target._lstat(extra) |
379 | if stat.S_ISDIR(st.st_mode): |
380 | blob = Tree() |
381 | elif stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode): |
382 | blob = blob_from_path_and_stat( |
383 | - target.abspath(e).encode(osutils._fs_enc), st) |
384 | + target.abspath(extra).encode(osutils._fs_enc), st) |
385 | else: |
386 | continue |
387 | target.store.add_object(blob) |
388 | @@ -1881,19 +1771,3 @@ |
389 | extras.add(np) |
390 | return commit_tree( |
391 | target.store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()]), extras |
392 | - |
393 | - |
394 | -def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target, |
395 | - want_unchanged=False, |
396 | - want_unversioned=False, |
397 | - rename_detector=None, |
398 | - include_trees=True): |
399 | - """Determine the changes between a git tree and a working tree with index. |
400 | - |
401 | - """ |
402 | - to_tree_sha, extras = snapshot_workingtree(target, want_unversioned=want_unversioned) |
403 | - store = OverlayObjectStore([source_store, target.store]) |
404 | - return tree_changes( |
405 | - store, from_tree_sha, to_tree_sha, include_trees=include_trees, |
406 | - rename_detector=rename_detector, |
407 | - want_unchanged=want_unchanged, change_type_same=True), extras |
408 | |
409 | === modified file 'breezy/git/workingtree.py' |
410 | --- breezy/git/workingtree.py 2020-08-06 22:01:46 +0000 |
411 | +++ breezy/git/workingtree.py 2020-08-08 18:37:30 +0000 |
412 | @@ -572,7 +572,7 @@ |
413 | """ |
414 | with self.lock_read(): |
415 | index_paths = set( |
416 | - [decode_git_path(p) for p, i in self._recurse_index_entries()]) |
417 | + [decode_git_path(p) for p, sha, mode in self.iter_git_objects()]) |
418 | all_paths = set(self._iter_files_recursive(include_dirs=False)) |
419 | return iter(all_paths - index_paths) |
420 |