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/renames | ||||
Merge into: | lp:brz/3.1 | ||||
Diff against target: |
554 lines (+191/-66) 7 files modified
breezy/builtins.py (+1/-1) breezy/commit.py (+2/-2) breezy/git/commit.py (+8/-5) breezy/git/tests/test_workingtree.py (+100/-27) breezy/git/tree.py (+56/-19) breezy/tests/per_repository/test_commit_builder.py (+21/-8) doc/en/release-notes/brz-3.1.txt (+3/-4) |
||||
To merge this branch: | bzr merge lp:~jelmer/brz/renames | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+381006@code.launchpad.net |
This proposal supersedes a proposal from 2020-01-26.
Commit message
Support Git rename tracking.
Description of the change
Support Git rename tracking.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : Posted in a previous version of this proposal | # |
review:
Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : Posted in a previous version of this proposal | # |
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : Posted in a previous version of this proposal | # |
Merging failed
https:/
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Voting criteria not met
https:/
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Running landing tests failed
https:/
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Merging failed
https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/builtins.py' | |||
2 | --- breezy/builtins.py 2020-04-02 01:37:43 +0000 | |||
3 | +++ breezy/builtins.py 2020-06-16 23:49:21 +0000 | |||
4 | @@ -4233,7 +4233,7 @@ | |||
5 | 4233 | 4233 | ||
6 | 4234 | try: | 4234 | try: |
7 | 4235 | from . import tests | 4235 | from . import tests |
9 | 4236 | except ImportError: | 4236 | except ImportError as e: |
10 | 4237 | raise errors.BzrCommandError("tests not available. Install the " | 4237 | raise errors.BzrCommandError("tests not available. Install the " |
11 | 4238 | "breezy tests to run the breezy testsuite.") | 4238 | "breezy tests to run the breezy testsuite.") |
12 | 4239 | 4239 | ||
13 | 4240 | 4240 | ||
14 | === modified file 'breezy/commit.py' | |||
15 | --- breezy/commit.py 2019-09-21 17:08:09 +0000 | |||
16 | +++ breezy/commit.py 2020-06-16 23:49:21 +0000 | |||
17 | @@ -440,8 +440,8 @@ | |||
18 | 440 | # as updating its basis and unversioning paths that were missing. | 440 | # as updating its basis and unversioning paths that were missing. |
19 | 441 | self.work_tree.unversion(self.deleted_paths) | 441 | self.work_tree.unversion(self.deleted_paths) |
20 | 442 | self._set_progress_stage("Updating the working tree") | 442 | self._set_progress_stage("Updating the working tree") |
23 | 443 | self.work_tree.update_basis_by_delta(self.rev_id, | 443 | self.work_tree.update_basis_by_delta( |
24 | 444 | self.builder.get_basis_delta()) | 444 | self.rev_id, self.builder.get_basis_delta()) |
25 | 445 | self.reporter.completed(new_revno, self.rev_id) | 445 | self.reporter.completed(new_revno, self.rev_id) |
26 | 446 | self._process_post_hooks(old_revno, new_revno) | 446 | self._process_post_hooks(old_revno, new_revno) |
27 | 447 | return self.rev_id | 447 | return self.rev_id |
28 | 448 | 448 | ||
29 | === modified file 'breezy/git/commit.py' | |||
30 | --- breezy/git/commit.py 2020-01-30 15:48:47 +0000 | |||
31 | +++ breezy/git/commit.py 2020-06-16 23:49:21 +0000 | |||
32 | @@ -69,6 +69,7 @@ | |||
33 | 69 | self.store = self.repository._git.object_store | 69 | self.store = self.repository._git.object_store |
34 | 70 | self._blobs = {} | 70 | self._blobs = {} |
35 | 71 | self._inv_delta = [] | 71 | self._inv_delta = [] |
36 | 72 | self._deleted_paths = set() | ||
37 | 72 | self._any_changes = False | 73 | self._any_changes = False |
38 | 73 | self._mapping = self.repository.get_mapping() | 74 | self._mapping = self.repository.get_mapping() |
39 | 74 | 75 | ||
40 | @@ -92,7 +93,7 @@ | |||
41 | 92 | self._any_changes = True | 93 | self._any_changes = True |
42 | 93 | if change.path[1] is None: | 94 | if change.path[1] is None: |
43 | 94 | self._inv_delta.append((change.path[0], change.path[1], change.file_id, None)) | 95 | self._inv_delta.append((change.path[0], change.path[1], change.file_id, None)) |
45 | 95 | self._blobs[change.path[0].encode("utf-8")] = None | 96 | self._deleted_paths.add(change.path[0].encode("utf-8")) |
46 | 96 | continue | 97 | continue |
47 | 97 | try: | 98 | try: |
48 | 98 | entry_kls = entry_factory[change.kind[1]] | 99 | entry_kls = entry_factory[change.kind[1]] |
49 | @@ -128,8 +129,9 @@ | |||
50 | 128 | raise AssertionError("Unknown kind %r" % change.kind[1]) | 129 | raise AssertionError("Unknown kind %r" % change.kind[1]) |
51 | 129 | mode = object_mode(change.kind[1], change.executable[1]) | 130 | mode = object_mode(change.kind[1], change.executable[1]) |
52 | 130 | self._inv_delta.append((change.path[0], change.path[1], change.file_id, entry)) | 131 | self._inv_delta.append((change.path[0], change.path[1], change.file_id, entry)) |
55 | 131 | encoded_new_path = change.path[1].encode("utf-8") | 132 | if change.path[0] is not None: |
56 | 132 | self._blobs[encoded_new_path] = (mode, sha) | 133 | self._deleted_paths.add(change.path[0].encode("utf-8")) |
57 | 134 | self._blobs[change.path[1].encode("utf-8")] = (mode, sha) | ||
58 | 133 | if st is not None: | 135 | if st is not None: |
59 | 134 | yield change.path[1], (entry.text_sha1, st) | 136 | yield change.path[1], (entry.text_sha1, st) |
60 | 135 | if not seen_root and len(self.parents) == 0: | 137 | if not seen_root and len(self.parents) == 0: |
61 | @@ -146,6 +148,8 @@ | |||
62 | 146 | for entry in basis_tree._iter_tree_contents(include_trees=False): | 148 | for entry in basis_tree._iter_tree_contents(include_trees=False): |
63 | 147 | if entry.path in self._blobs: | 149 | if entry.path in self._blobs: |
64 | 148 | continue | 150 | continue |
65 | 151 | if entry.path in self._deleted_paths: | ||
66 | 152 | continue | ||
67 | 149 | self._blobs[entry.path] = (entry.mode, entry.sha) | 153 | self._blobs[entry.path] = (entry.mode, entry.sha) |
68 | 150 | self.new_inventory = None | 154 | self.new_inventory = None |
69 | 151 | 155 | ||
70 | @@ -155,8 +159,7 @@ | |||
71 | 155 | 159 | ||
72 | 156 | def finish_inventory(self): | 160 | def finish_inventory(self): |
73 | 157 | # eliminate blobs that were removed | 161 | # eliminate blobs that were removed |
76 | 158 | self._blobs = {k: v for (k, v) in viewitems( | 162 | self._blobs = {k: v for (k, v) in viewitems(self._blobs)} |
75 | 159 | self._blobs) if v is not None} | ||
77 | 160 | 163 | ||
78 | 161 | def _iterblobs(self): | 164 | def _iterblobs(self): |
79 | 162 | return ((path, sha, mode) for (path, (mode, sha)) | 165 | return ((path, sha, mode) for (path, (mode, sha)) |
80 | 163 | 166 | ||
81 | === modified file 'breezy/git/tests/test_workingtree.py' | |||
82 | --- breezy/git/tests/test_workingtree.py 2020-01-19 15:14:16 +0000 | |||
83 | +++ breezy/git/tests/test_workingtree.py 2020-06-16 23:49:21 +0000 | |||
84 | @@ -22,6 +22,8 @@ | |||
85 | 22 | import os | 22 | import os |
86 | 23 | import stat | 23 | import stat |
87 | 24 | 24 | ||
88 | 25 | from dulwich import __version__ as dulwich_version | ||
89 | 26 | from dulwich.diff_tree import RenameDetector | ||
90 | 25 | from dulwich.index import IndexEntry | 27 | from dulwich.index import IndexEntry |
91 | 26 | from dulwich.objects import ( | 28 | from dulwich.objects import ( |
92 | 27 | S_IFGITLINK, | 29 | S_IFGITLINK, |
93 | @@ -141,9 +143,15 @@ | |||
94 | 141 | 143 | ||
95 | 142 | def test_missing(self): | 144 | def test_missing(self): |
96 | 143 | delta = TreeDelta() | 145 | delta = TreeDelta() |
100 | 144 | delta.removed.append(TreeChange(b'git:a', ('a', 'a'), False, (True, True), (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', None), (True, False))) | 146 | delta.removed.append( |
101 | 145 | changes = [((b'a', b'a'), (stat.S_IFREG | 0o755, 0), | 147 | TreeChange( |
102 | 146 | (b'a' * 40, b'a' * 40))] | 148 | b'git:a', ('a', 'a'), False, (True, True), |
103 | 149 | (b'TREE_ROOT', b'TREE_ROOT'), ('a', 'a'), ('file', None), | ||
104 | 150 | (True, False))) | ||
105 | 151 | changes = [ | ||
106 | 152 | ('remove', | ||
107 | 153 | (b'a', stat.S_IFREG | 0o755, b'a' * 40), | ||
108 | 154 | (b'a', 0, b'a' * 40))] | ||
109 | 147 | self.assertEqual( | 155 | self.assertEqual( |
110 | 148 | delta, | 156 | delta, |
111 | 149 | tree_delta_from_git_changes(changes, (default_mapping, default_mapping))) | 157 | tree_delta_from_git_changes(changes, (default_mapping, default_mapping))) |
112 | @@ -158,7 +166,7 @@ | |||
113 | 158 | 166 | ||
114 | 159 | def expectDelta(self, expected_changes, | 167 | def expectDelta(self, expected_changes, |
115 | 160 | expected_extras=None, want_unversioned=False, | 168 | expected_extras=None, want_unversioned=False, |
117 | 161 | tree_id=None): | 169 | tree_id=None, rename_detector=None): |
118 | 162 | if tree_id is None: | 170 | if tree_id is None: |
119 | 163 | try: | 171 | try: |
120 | 164 | tree_id = self.store[self.wt.branch.repository._git.head()].tree | 172 | tree_id = self.store[self.wt.branch.repository._git.head()].tree |
121 | @@ -166,7 +174,8 @@ | |||
122 | 166 | tree_id = None | 174 | tree_id = None |
123 | 167 | with self.wt.lock_read(): | 175 | with self.wt.lock_read(): |
124 | 168 | changes, extras = changes_between_git_tree_and_working_copy( | 176 | changes, extras = changes_between_git_tree_and_working_copy( |
126 | 169 | self.store, tree_id, self.wt, want_unversioned=want_unversioned) | 177 | self.store, tree_id, self.wt, want_unversioned=want_unversioned, |
127 | 178 | rename_detector=rename_detector) | ||
128 | 170 | self.assertEqual(expected_changes, list(changes)) | 179 | self.assertEqual(expected_changes, list(changes)) |
129 | 171 | if expected_extras is None: | 180 | if expected_extras is None: |
130 | 172 | expected_extras = set() | 181 | expected_extras = set() |
131 | @@ -174,7 +183,7 @@ | |||
132 | 174 | 183 | ||
133 | 175 | def test_empty(self): | 184 | def test_empty(self): |
134 | 176 | self.expectDelta( | 185 | self.expectDelta( |
136 | 177 | [((None, b''), (None, stat.S_IFDIR), (None, Tree().id))]) | 186 | [('add', (None, None, None), (b'', stat.S_IFDIR, Tree().id))]) |
137 | 178 | 187 | ||
138 | 179 | def test_added_file(self): | 188 | def test_added_file(self): |
139 | 180 | self.build_tree(['a']) | 189 | self.build_tree(['a']) |
140 | @@ -183,20 +192,74 @@ | |||
141 | 183 | t = Tree() | 192 | t = Tree() |
142 | 184 | t.add(b"a", stat.S_IFREG | 0o644, a.id) | 193 | t.add(b"a", stat.S_IFREG | 0o644, a.id) |
143 | 185 | self.expectDelta( | 194 | self.expectDelta( |
146 | 186 | [((None, b''), (None, stat.S_IFDIR), (None, t.id)), | 195 | [('add', (None, None, None), (b'', stat.S_IFDIR, t.id)), |
147 | 187 | ((None, b'a'), (None, stat.S_IFREG | 0o644), (None, a.id))]) | 196 | ('add', (None, None, None), (b'a', stat.S_IFREG | 0o644, a.id))]) |
148 | 197 | |||
149 | 198 | def test_renamed_file(self): | ||
150 | 199 | self.build_tree(['a']) | ||
151 | 200 | self.wt.add(['a']) | ||
152 | 201 | self.wt.rename_one('a', 'b') | ||
153 | 202 | a = Blob.from_string(b'contents of a\n') | ||
154 | 203 | self.store.add_object(a) | ||
155 | 204 | oldt = Tree() | ||
156 | 205 | oldt.add(b"a", stat.S_IFREG | 0o644, a.id) | ||
157 | 206 | self.store.add_object(oldt) | ||
158 | 207 | newt = Tree() | ||
159 | 208 | newt.add(b"b", stat.S_IFREG | 0o644, a.id) | ||
160 | 209 | self.store.add_object(newt) | ||
161 | 210 | self.expectDelta( | ||
162 | 211 | [('modify', (b'', stat.S_IFDIR, oldt.id), (b'', stat.S_IFDIR, newt.id)), | ||
163 | 212 | ('delete', (b'a', stat.S_IFREG | 0o644, a.id), (None, None, None)), | ||
164 | 213 | ('add', (None, None, None), (b'b', stat.S_IFREG | 0o644, a.id)), | ||
165 | 214 | ], | ||
166 | 215 | tree_id=oldt.id) | ||
167 | 216 | if dulwich_version >= (0, 19, 15): | ||
168 | 217 | self.expectDelta( | ||
169 | 218 | [('modify', (b'', stat.S_IFDIR, oldt.id), (b'', stat.S_IFDIR, newt.id)), | ||
170 | 219 | ('rename', (b'a', stat.S_IFREG | 0o644, a.id), (b'b', stat.S_IFREG | 0o644, a.id))], | ||
171 | 220 | tree_id=oldt.id, rename_detector=RenameDetector(self.store)) | ||
172 | 221 | |||
173 | 222 | def test_copied_file(self): | ||
174 | 223 | self.build_tree(['a']) | ||
175 | 224 | self.wt.add(['a']) | ||
176 | 225 | self.wt.copy_one('a', 'b') | ||
177 | 226 | a = Blob.from_string(b'contents of a\n') | ||
178 | 227 | self.store.add_object(a) | ||
179 | 228 | oldt = Tree() | ||
180 | 229 | oldt.add(b"a", stat.S_IFREG | 0o644, a.id) | ||
181 | 230 | self.store.add_object(oldt) | ||
182 | 231 | newt = Tree() | ||
183 | 232 | newt.add(b"a", stat.S_IFREG | 0o644, a.id) | ||
184 | 233 | newt.add(b"b", stat.S_IFREG | 0o644, a.id) | ||
185 | 234 | self.store.add_object(newt) | ||
186 | 235 | self.expectDelta( | ||
187 | 236 | [('modify', (b'', stat.S_IFDIR, oldt.id), (b'', stat.S_IFDIR, newt.id)), | ||
188 | 237 | ('add', (None, None, None), (b'b', stat.S_IFREG | 0o644, a.id)), | ||
189 | 238 | ], | ||
190 | 239 | tree_id=oldt.id) | ||
191 | 240 | |||
192 | 241 | if dulwich_version >= (0, 19, 15): | ||
193 | 242 | self.expectDelta( | ||
194 | 243 | [('modify', (b'', stat.S_IFDIR, oldt.id), (b'', stat.S_IFDIR, newt.id)), | ||
195 | 244 | ('copy', (b'a', stat.S_IFREG | 0o644, a.id), (b'b', stat.S_IFREG | 0o644, a.id))], | ||
196 | 245 | tree_id=oldt.id, rename_detector=RenameDetector(self.store, find_copies_harder=True)) | ||
197 | 246 | self.expectDelta( | ||
198 | 247 | [('modify', (b'', stat.S_IFDIR, oldt.id), (b'', stat.S_IFDIR, newt.id)), | ||
199 | 248 | ('add', (None, None, None), (b'b', stat.S_IFREG | 0o644, a.id)), | ||
200 | 249 | ], | ||
201 | 250 | tree_id=oldt.id, rename_detector=RenameDetector(self.store, find_copies_harder=False)) | ||
202 | 188 | 251 | ||
203 | 189 | def test_added_unknown_file(self): | 252 | def test_added_unknown_file(self): |
204 | 190 | self.build_tree(['a']) | 253 | self.build_tree(['a']) |
205 | 191 | t = Tree() | 254 | t = Tree() |
206 | 192 | self.expectDelta( | 255 | self.expectDelta( |
208 | 193 | [((None, b''), (None, stat.S_IFDIR), (None, t.id))]) | 256 | [('add', (None, None, None), (b'', stat.S_IFDIR, t.id))]) |
209 | 194 | a = Blob.from_string(b'contents of a\n') | 257 | a = Blob.from_string(b'contents of a\n') |
210 | 195 | t = Tree() | 258 | t = Tree() |
211 | 196 | t.add(b"a", stat.S_IFREG | 0o644, a.id) | 259 | t.add(b"a", stat.S_IFREG | 0o644, a.id) |
212 | 197 | self.expectDelta( | 260 | self.expectDelta( |
215 | 198 | [((None, b''), (None, stat.S_IFDIR), (None, t.id)), | 261 | [('add', (None, None, None), (b'', stat.S_IFDIR, t.id)), |
216 | 199 | ((None, b'a'), (None, stat.S_IFREG | 0o644), (None, a.id))], | 262 | ('add', (None, None, None), (b'a', stat.S_IFREG | 0o644, a.id))], |
217 | 200 | [b'a'], | 263 | [b'a'], |
218 | 201 | want_unversioned=True) | 264 | want_unversioned=True) |
219 | 202 | 265 | ||
220 | @@ -208,8 +271,8 @@ | |||
221 | 208 | t = Tree() | 271 | t = Tree() |
222 | 209 | t.add(b"a", 0, ZERO_SHA) | 272 | t.add(b"a", 0, ZERO_SHA) |
223 | 210 | self.expectDelta( | 273 | self.expectDelta( |
226 | 211 | [((None, b''), (None, stat.S_IFDIR), (None, t.id)), | 274 | [('add', (None, None, None), (b'', stat.S_IFDIR, t.id)), |
227 | 212 | ((None, b'a'), (None, 0), (None, ZERO_SHA))], | 275 | ('add', (None, None, None), (b'a', 0, ZERO_SHA))], |
228 | 213 | []) | 276 | []) |
229 | 214 | 277 | ||
230 | 215 | def test_missing_versioned_file(self): | 278 | def test_missing_versioned_file(self): |
231 | @@ -223,8 +286,12 @@ | |||
232 | 223 | newt = Tree() | 286 | newt = Tree() |
233 | 224 | newt.add(b"a", 0, ZERO_SHA) | 287 | newt.add(b"a", 0, ZERO_SHA) |
234 | 225 | self.expectDelta( | 288 | self.expectDelta( |
237 | 226 | [((b'', b''), (stat.S_IFDIR, stat.S_IFDIR), (oldt.id, newt.id)), | 289 | [('modify', |
238 | 227 | ((b'a', b'a'), (stat.S_IFREG | 0o644, 0), (a.id, ZERO_SHA))]) | 290 | (b'', stat.S_IFDIR, oldt.id), |
239 | 291 | (b'', stat.S_IFDIR, newt.id)), | ||
240 | 292 | ('modify', | ||
241 | 293 | (b'a', stat.S_IFREG | 0o644, a.id), | ||
242 | 294 | (b'a', 0, ZERO_SHA))]) | ||
243 | 228 | 295 | ||
244 | 229 | def test_versioned_replace_by_dir(self): | 296 | def test_versioned_replace_by_dir(self): |
245 | 230 | self.build_tree(['a']) | 297 | self.build_tree(['a']) |
246 | @@ -239,16 +306,20 @@ | |||
247 | 239 | newa = Tree() | 306 | newa = Tree() |
248 | 240 | newt.add(b"a", stat.S_IFDIR, newa.id) | 307 | newt.add(b"a", stat.S_IFDIR, newa.id) |
249 | 241 | self.expectDelta([ | 308 | self.expectDelta([ |
254 | 242 | ((b'', b''), | 309 | ('modify', |
255 | 243 | (stat.S_IFDIR, stat.S_IFDIR), | 310 | (b'', stat.S_IFDIR, oldt.id), |
256 | 244 | (oldt.id, newt.id)), | 311 | (b'', stat.S_IFDIR, newt.id)), |
257 | 245 | ((b'a', b'a'), (stat.S_IFREG | 0o644, stat.S_IFDIR), (olda.id, newa.id)) | 312 | ('modify', |
258 | 313 | (b'a', stat.S_IFREG | 0o644, olda.id), | ||
259 | 314 | (b'a', stat.S_IFDIR, newa.id)) | ||
260 | 246 | ], want_unversioned=False) | 315 | ], want_unversioned=False) |
261 | 247 | self.expectDelta([ | 316 | self.expectDelta([ |
266 | 248 | ((b'', b''), | 317 | ('modify', |
267 | 249 | (stat.S_IFDIR, stat.S_IFDIR), | 318 | (b'', stat.S_IFDIR, oldt.id), |
268 | 250 | (oldt.id, newt.id)), | 319 | (b'', stat.S_IFDIR, newt.id)), |
269 | 251 | ((b'a', b'a'), (stat.S_IFREG | 0o644, stat.S_IFDIR), (olda.id, newa.id)) | 320 | ('modify', |
270 | 321 | (b'a', stat.S_IFREG | 0o644, olda.id), | ||
271 | 322 | (b'a', stat.S_IFDIR, newa.id)), | ||
272 | 252 | ], want_unversioned=True) | 323 | ], want_unversioned=True) |
273 | 253 | 324 | ||
274 | 254 | def test_extra(self): | 325 | def test_extra(self): |
275 | @@ -257,10 +328,12 @@ | |||
276 | 257 | newt = Tree() | 328 | newt = Tree() |
277 | 258 | newt.add(b"a", stat.S_IFREG | 0o644, newa.id) | 329 | newt.add(b"a", stat.S_IFREG | 0o644, newa.id) |
278 | 259 | self.expectDelta([ | 330 | self.expectDelta([ |
283 | 260 | ((None, b''), | 331 | ('add', |
284 | 261 | (None, stat.S_IFDIR), | 332 | (None, None, None), |
285 | 262 | (None, newt.id)), | 333 | (b'', stat.S_IFDIR, newt.id)), |
286 | 263 | ((None, b'a'), (None, stat.S_IFREG | 0o644), (None, newa.id)) | 334 | ('add', |
287 | 335 | (None, None, None), | ||
288 | 336 | (b'a', stat.S_IFREG | 0o644, newa.id)), | ||
289 | 264 | ], [b'a'], want_unversioned=True) | 337 | ], [b'a'], want_unversioned=True) |
290 | 265 | 338 | ||
291 | 266 | def test_submodule(self): | 339 | def test_submodule(self): |
292 | 267 | 340 | ||
293 | === modified file 'breezy/git/tree.py' | |||
294 | --- breezy/git/tree.py 2020-04-27 02:29:59 +0000 | |||
295 | +++ breezy/git/tree.py 2020-06-16 23:49:21 +0000 | |||
296 | @@ -28,7 +28,7 @@ | |||
297 | 28 | parse_submodules, | 28 | parse_submodules, |
298 | 29 | ConfigFile as GitConfigFile, | 29 | ConfigFile as GitConfigFile, |
299 | 30 | ) | 30 | ) |
301 | 31 | from dulwich.diff_tree import tree_changes | 31 | from dulwich.diff_tree import tree_changes, RenameDetector |
302 | 32 | from dulwich.errors import NotTreeError | 32 | from dulwich.errors import NotTreeError |
303 | 33 | from dulwich.index import ( | 33 | from dulwich.index import ( |
304 | 34 | blob_from_path_and_stat, | 34 | blob_from_path_and_stat, |
305 | @@ -737,7 +737,9 @@ | |||
306 | 737 | target_extras = set() | 737 | target_extras = set() |
307 | 738 | ret = delta.TreeDelta() | 738 | ret = delta.TreeDelta() |
308 | 739 | added = [] | 739 | added = [] |
310 | 740 | for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: | 740 | for (change_type, old, new) in changes: |
311 | 741 | (oldpath, oldmode, oldsha) = old | ||
312 | 742 | (newpath, newmode, newsha) = new | ||
313 | 741 | if newpath == b'' and not include_root: | 743 | if newpath == b'' and not include_root: |
314 | 742 | continue | 744 | continue |
315 | 743 | if oldpath is not None: | 745 | if oldpath is not None: |
316 | @@ -809,13 +811,17 @@ | |||
317 | 809 | fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha), | 811 | fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha), |
318 | 810 | (oldversioned, newversioned), | 812 | (oldversioned, newversioned), |
319 | 811 | (oldparent, newparent), (oldname, newname), | 813 | (oldparent, newparent), (oldname, newname), |
321 | 812 | (oldkind, newkind), (oldexe, newexe)) | 814 | (oldkind, newkind), (oldexe, newexe), |
322 | 815 | copied=(change_type == 'copy')) | ||
323 | 813 | if oldpath is None: | 816 | if oldpath is None: |
324 | 814 | added.append((newpath, newkind)) | 817 | added.append((newpath, newkind)) |
325 | 815 | elif newpath is None or newmode == 0: | 818 | elif newpath is None or newmode == 0: |
326 | 816 | ret.removed.append(change) | 819 | ret.removed.append(change) |
327 | 817 | elif oldpath != newpath: | 820 | elif oldpath != newpath: |
329 | 818 | ret.renamed.append(change) | 821 | if change_type == 'copy': |
330 | 822 | ret.copied.append(change) | ||
331 | 823 | else: | ||
332 | 824 | ret.renamed.append(change) | ||
333 | 819 | elif mode_kind(oldmode) != mode_kind(newmode): | 825 | elif mode_kind(oldmode) != mode_kind(newmode): |
334 | 820 | ret.kind_changed.append(change) | 826 | ret.kind_changed.append(change) |
335 | 821 | elif oldsha != newsha or oldmode != newmode: | 827 | elif oldsha != newsha or oldmode != newmode: |
336 | @@ -863,7 +869,9 @@ | |||
337 | 863 | """ | 869 | """ |
338 | 864 | if target_extras is None: | 870 | if target_extras is None: |
339 | 865 | target_extras = set() | 871 | target_extras = set() |
341 | 866 | for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: | 872 | for (change_type, old, new) in changes: |
342 | 873 | (oldpath, oldmode, oldsha) = old | ||
343 | 874 | (newpath, newmode, newsha) = new | ||
344 | 867 | if oldpath is not None: | 875 | if oldpath is not None: |
345 | 868 | oldpath_decoded = oldpath.decode('utf-8') | 876 | oldpath_decoded = oldpath.decode('utf-8') |
346 | 869 | else: | 877 | else: |
347 | @@ -934,7 +942,8 @@ | |||
348 | 934 | fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha), | 942 | fileid, (oldpath_decoded, newpath_decoded), (oldsha != newsha), |
349 | 935 | (oldversioned, newversioned), | 943 | (oldversioned, newversioned), |
350 | 936 | (oldparent, newparent), (oldname, newname), | 944 | (oldparent, newparent), (oldname, newname), |
352 | 937 | (oldkind, newkind), (oldexe, newexe)) | 945 | (oldkind, newkind), (oldexe, newexe), |
353 | 946 | copied=(change_type == 'copy')) | ||
354 | 938 | 947 | ||
355 | 939 | 948 | ||
356 | 940 | class InterGitTrees(_mod_tree.InterTree): | 949 | class InterGitTrees(_mod_tree.InterTree): |
357 | @@ -997,7 +1006,9 @@ | |||
358 | 997 | paths = set(paths) | 1006 | paths = set(paths) |
359 | 998 | ret = {} | 1007 | ret = {} |
360 | 999 | changes = self._iter_git_changes(specific_files=paths)[0] | 1008 | changes = self._iter_git_changes(specific_files=paths)[0] |
362 | 1000 | for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: | 1009 | for (change_type, old, new) in changes: |
363 | 1010 | oldpath = old[0] | ||
364 | 1011 | newpath = new[0] | ||
365 | 1001 | if oldpath in paths: | 1012 | if oldpath in paths: |
366 | 1002 | ret[oldpath] = newpath | 1013 | ret[oldpath] = newpath |
367 | 1003 | for path in paths: | 1014 | for path in paths: |
368 | @@ -1015,7 +1026,9 @@ | |||
369 | 1015 | paths = set(paths) | 1026 | paths = set(paths) |
370 | 1016 | ret = {} | 1027 | ret = {} |
371 | 1017 | changes = self._iter_git_changes(specific_files=paths)[0] | 1028 | changes = self._iter_git_changes(specific_files=paths)[0] |
373 | 1018 | for (oldpath, newpath), (oldmode, newmode), (oldsha, newsha) in changes: | 1029 | for (change_type, old, new) in changes: |
374 | 1030 | oldpath = old[0] | ||
375 | 1031 | newpath = new[0] | ||
376 | 1019 | if newpath in paths: | 1032 | if newpath in paths: |
377 | 1020 | ret[newpath] = oldpath | 1033 | ret[newpath] = oldpath |
378 | 1021 | for path in paths: | 1034 | for path in paths: |
379 | @@ -1060,9 +1073,10 @@ | |||
380 | 1060 | self.target._repository._git.object_store]) | 1073 | self.target._repository._git.object_store]) |
381 | 1061 | else: | 1074 | else: |
382 | 1062 | store = self.source._repository._git.object_store | 1075 | store = self.source._repository._git.object_store |
386 | 1063 | return store.tree_changes( | 1076 | rename_detector = RenameDetector(store) |
387 | 1064 | self.source.tree, self.target.tree, want_unchanged=want_unchanged, | 1077 | return tree_changes( |
388 | 1065 | include_trees=True, change_type_same=True), set() | 1078 | store, self.source.tree, self.target.tree, want_unchanged=want_unchanged, |
389 | 1079 | include_trees=True, change_type_same=True, rename_detector=rename_detector), set() | ||
390 | 1066 | 1080 | ||
391 | 1067 | 1081 | ||
392 | 1068 | _mod_tree.InterTree.register_optimiser(InterGitRevisionTrees) | 1082 | _mod_tree.InterTree.register_optimiser(InterGitRevisionTrees) |
393 | @@ -1601,6 +1615,12 @@ | |||
394 | 1601 | def __init__(self, source, target): | 1615 | def __init__(self, source, target): |
395 | 1602 | super(InterIndexGitTree, self).__init__(source, target) | 1616 | super(InterIndexGitTree, self).__init__(source, target) |
396 | 1603 | self._index = target.index | 1617 | self._index = target.index |
397 | 1618 | if self.source.store == self.target.store: | ||
398 | 1619 | self.store = self.source.store | ||
399 | 1620 | else: | ||
400 | 1621 | self.store = OverlayObjectStore( | ||
401 | 1622 | [self.source.store, self.target.store]) | ||
402 | 1623 | self.rename_detector = RenameDetector(self.store) | ||
403 | 1604 | 1624 | ||
404 | 1605 | @classmethod | 1625 | @classmethod |
405 | 1606 | def is_compatible(cls, source, target): | 1626 | def is_compatible(cls, source, target): |
406 | @@ -1622,15 +1642,17 @@ | |||
407 | 1622 | return changes_between_git_tree_and_working_copy( | 1642 | return changes_between_git_tree_and_working_copy( |
408 | 1623 | self.source.store, self.source.tree, | 1643 | self.source.store, self.source.tree, |
409 | 1624 | self.target, want_unchanged=want_unchanged, | 1644 | self.target, want_unchanged=want_unchanged, |
411 | 1625 | want_unversioned=want_unversioned) | 1645 | want_unversioned=want_unversioned, |
412 | 1646 | rename_detector=self.rename_detector) | ||
413 | 1626 | 1647 | ||
414 | 1627 | 1648 | ||
415 | 1628 | _mod_tree.InterTree.register_optimiser(InterIndexGitTree) | 1649 | _mod_tree.InterTree.register_optimiser(InterIndexGitTree) |
416 | 1629 | 1650 | ||
417 | 1630 | 1651 | ||
419 | 1631 | def changes_between_git_tree_and_working_copy(store, from_tree_sha, target, | 1652 | def changes_between_git_tree_and_working_copy(source_store, from_tree_sha, target, |
420 | 1632 | want_unchanged=False, | 1653 | want_unchanged=False, |
422 | 1633 | want_unversioned=False): | 1654 | want_unversioned=False, |
423 | 1655 | rename_detector=None): | ||
424 | 1634 | """Determine the changes between a git tree and a working tree with index. | 1656 | """Determine the changes between a git tree and a working tree with index. |
425 | 1635 | 1657 | ||
426 | 1636 | """ | 1658 | """ |
427 | @@ -1657,7 +1679,7 @@ | |||
428 | 1657 | blobs[path] = (index_entry.sha, index_entry.mode) | 1679 | blobs[path] = (index_entry.sha, index_entry.mode) |
429 | 1658 | else: | 1680 | else: |
430 | 1659 | dirified.append((path, Tree().id, stat.S_IFDIR)) | 1681 | dirified.append((path, Tree().id, stat.S_IFDIR)) |
432 | 1660 | store.add_object(Tree()) | 1682 | target.store.add_object(Tree()) |
433 | 1661 | else: | 1683 | else: |
434 | 1662 | mode = live_entry.mode | 1684 | mode = live_entry.mode |
435 | 1663 | if not trust_executable: | 1685 | if not trust_executable: |
436 | @@ -1665,6 +1687,19 @@ | |||
437 | 1665 | mode |= 0o111 | 1687 | mode |= 0o111 |
438 | 1666 | else: | 1688 | else: |
439 | 1667 | mode &= ~0o111 | 1689 | mode &= ~0o111 |
440 | 1690 | if live_entry.sha != index_entry.sha: | ||
441 | 1691 | rp = path.decode('utf-8') | ||
442 | 1692 | if stat.S_ISREG(live_entry.mode): | ||
443 | 1693 | blob = Blob() | ||
444 | 1694 | with target.get_file(rp) as f: | ||
445 | 1695 | blob.data = f.read() | ||
446 | 1696 | elif stat.S_ISLNK(live_entry.mode): | ||
447 | 1697 | blob = Blob() | ||
448 | 1698 | blob.data = target.get_symlink_target(rp).encode(osutils._fs_enc) | ||
449 | 1699 | else: | ||
450 | 1700 | blob = None | ||
451 | 1701 | if blob is not None: | ||
452 | 1702 | target.store.add_object(blob) | ||
453 | 1668 | blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode)) | 1703 | blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode)) |
454 | 1669 | if want_unversioned: | 1704 | if want_unversioned: |
455 | 1670 | for e in target.extras(): | 1705 | for e in target.extras(): |
456 | @@ -1681,12 +1716,14 @@ | |||
457 | 1681 | target.abspath(e).encode(osutils._fs_enc), st) | 1716 | target.abspath(e).encode(osutils._fs_enc), st) |
458 | 1682 | else: | 1717 | else: |
459 | 1683 | continue | 1718 | continue |
461 | 1684 | store.add_object(blob) | 1719 | target.store.add_object(blob) |
462 | 1685 | np = np.encode('utf-8') | 1720 | np = np.encode('utf-8') |
463 | 1686 | blobs[np] = (blob.id, cleanup_mode(st.st_mode)) | 1721 | blobs[np] = (blob.id, cleanup_mode(st.st_mode)) |
464 | 1687 | extras.add(np) | 1722 | extras.add(np) |
465 | 1688 | to_tree_sha = commit_tree( | 1723 | to_tree_sha = commit_tree( |
469 | 1689 | store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()]) | 1724 | target.store, dirified + [(p, s, m) for (p, (s, m)) in blobs.items()]) |
470 | 1690 | return store.tree_changes( | 1725 | store = OverlayObjectStore([source_store, target.store]) |
471 | 1691 | from_tree_sha, to_tree_sha, include_trees=True, | 1726 | return tree_changes( |
472 | 1727 | store, from_tree_sha, to_tree_sha, include_trees=True, | ||
473 | 1728 | rename_detector=rename_detector, | ||
474 | 1692 | want_unchanged=want_unchanged, change_type_same=True), extras | 1729 | want_unchanged=want_unchanged, change_type_same=True), extras |
475 | 1693 | 1730 | ||
476 | === modified file 'breezy/tests/per_repository/test_commit_builder.py' | |||
477 | --- breezy/tests/per_repository/test_commit_builder.py 2019-06-29 13:16:26 +0000 | |||
478 | +++ breezy/tests/per_repository/test_commit_builder.py 2020-06-16 23:49:21 +0000 | |||
479 | @@ -498,11 +498,11 @@ | |||
480 | 498 | builder.abort() | 498 | builder.abort() |
481 | 499 | raise | 499 | raise |
482 | 500 | delta = builder.get_basis_delta() | 500 | delta = builder.get_basis_delta() |
484 | 501 | delta_dict = dict((change[2], change) for change in delta) | 501 | delta_dict = dict((change[1], change) for change in delta) |
485 | 502 | if tree.branch.repository._format.records_per_file_revision: | 502 | if tree.branch.repository._format.records_per_file_revision: |
489 | 503 | version_recorded = (file_id in delta_dict | 503 | version_recorded = (new_name in delta_dict |
490 | 504 | and delta_dict[file_id][3] is not None | 504 | and delta_dict[new_name][3] is not None |
491 | 505 | and delta_dict[file_id][3].revision == rev2) | 505 | and delta_dict[new_name][3].revision == rev2) |
492 | 506 | if records_version: | 506 | if records_version: |
493 | 507 | self.assertTrue(version_recorded) | 507 | self.assertTrue(version_recorded) |
494 | 508 | else: | 508 | else: |
495 | @@ -513,11 +513,24 @@ | |||
496 | 513 | specific_files=[new_name]))[1] | 513 | specific_files=[new_name]))[1] |
497 | 514 | 514 | ||
498 | 515 | if delta_against_basis: | 515 | if delta_against_basis: |
501 | 516 | if tree.supports_rename_tracking() or name == new_name: | 516 | (delta_old_name, delta_new_name, |
502 | 517 | expected_delta = (name, new_name, file_id, new_entry) | 517 | delta_file_id, delta_entry) = delta_dict[new_name] |
503 | 518 | self.assertEqual(delta_new_name, new_name) | ||
504 | 519 | if tree.supports_rename_tracking(): | ||
505 | 520 | self.assertEqual(name, delta_old_name) | ||
506 | 518 | else: | 521 | else: |
509 | 519 | expected_delta = (None, new_name, file_id, new_entry) | 522 | self.assertIn(delta_old_name, (name, None)) |
510 | 520 | self.assertEqual(expected_delta, delta_dict[file_id]) | 523 | if tree.supports_setting_file_ids(): |
511 | 524 | self.assertEqual(delta_file_id, file_id) | ||
512 | 525 | self.assertEqual(delta_entry.file_id, file_id) | ||
513 | 526 | self.assertEqual(delta_entry.kind, new_entry.kind) | ||
514 | 527 | self.assertEqual(delta_entry.name, new_entry.name) | ||
515 | 528 | self.assertEqual(delta_entry.parent_id, new_entry.parent_id) | ||
516 | 529 | if delta_entry.kind == 'file': | ||
517 | 530 | self.assertEqual(delta_entry.text_size, new_entry.text_size) | ||
518 | 531 | self.assertEqual(delta_entry.text_sha1, new_entry.text_sha1) | ||
519 | 532 | elif delta_entry.kind == 'symlink': | ||
520 | 533 | self.assertEqual(delta_entry.symlink_target, new_entry.symlink_target) | ||
521 | 521 | else: | 534 | else: |
522 | 522 | expected_delta = None | 535 | expected_delta = None |
523 | 523 | if tree.branch.repository._format.records_per_file_revision: | 536 | if tree.branch.repository._format.records_per_file_revision: |
524 | 524 | 537 | ||
525 | === modified file 'doc/en/release-notes/brz-3.1.txt' | |||
526 | --- doc/en/release-notes/brz-3.1.txt 2020-06-16 01:38:06 +0000 | |||
527 | +++ doc/en/release-notes/brz-3.1.txt 2020-06-16 23:49:21 +0000 | |||
528 | @@ -32,6 +32,9 @@ | |||
529 | 32 | * Permission denied errors from GitLab during push are now properly | 32 | * Permission denied errors from GitLab during push are now properly |
530 | 33 | recognized. (Jelmer Vernooij) | 33 | recognized. (Jelmer Vernooij) |
531 | 34 | 34 | ||
532 | 35 | * Support rename and copy tracking when accessing Git | ||
533 | 36 | repositories. (Jelmer Vernooij, #1760740) | ||
534 | 37 | |||
535 | 35 | Bug Fixes | 38 | Bug Fixes |
536 | 36 | ********* | 39 | ********* |
537 | 37 | 40 | ||
538 | @@ -41,7 +44,6 @@ | |||
539 | 41 | * Don't require ``ctypes.pythonapi`` to exist, as it's missing on newer | 44 | * Don't require ``ctypes.pythonapi`` to exist, as it's missing on newer |
540 | 42 | versions of Pypy3. (Jelmer Vernooij) | 45 | versions of Pypy3. (Jelmer Vernooij) |
541 | 43 | 46 | ||
542 | 44 | |||
543 | 45 | Documentation | 47 | Documentation |
544 | 46 | ************* | 48 | ************* |
545 | 47 | 49 | ||
546 | @@ -73,9 +75,6 @@ | |||
547 | 73 | * Tests for most bzr-specific functionality has been moved to the | 75 | * Tests for most bzr-specific functionality has been moved to the |
548 | 74 | ``breezy.bzr.tests`` module. (Jelmer Vernooij) | 76 | ``breezy.bzr.tests`` module. (Jelmer Vernooij) |
549 | 75 | 77 | ||
550 | 76 | .. | ||
551 | 77 | vim: tw=74 ft=rst ff=unix | ||
552 | 78 | |||
553 | 79 | brz 3.1.0 | 78 | brz 3.1.0 |
554 | 80 | ######### | 79 | ######### |
555 | 81 | 80 |
Running landing tests failed /ci.breezy- vcs.org/ job/brz/ job/brz- land/666/
https:/