Merge lp:~jelmer/brz/iterobjectss into lp:brz/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
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

Subscribers

People subscribed via source and target branches