Merge lp:~jelmer/brz/export-nested into lp:brz
- export-nested
- Merge into trunk
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/export-nested |
Merge into: | lp:brz |
Diff against target: |
573 lines (+166/-73) 13 files modified
breezy/archive/__init__.py (+3/-2) breezy/archive/tar.py (+27/-21) breezy/archive/zip.py (+3/-2) breezy/builtins.py (+5/-2) breezy/bzr/inventorytree.py (+5/-3) breezy/bzr/remote.py (+8/-2) breezy/bzr/workingtree.py (+5/-1) breezy/export.py (+31/-25) breezy/git/remote.py (+6/-2) breezy/git/tree.py (+9/-4) breezy/tests/blackbox/test_export.py (+17/-0) breezy/tests/per_tree/test_export.py (+38/-2) breezy/tree.py (+9/-7) |
To merge this branch: | bzr merge lp:~jelmer/brz/export-nested |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+413552@code.launchpad.net |
Commit message
Add support for --recurse-nested argument to 'brz export'.
Description of the change
Add support for --recurse-nested argument to 'brz export'.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
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/archive/__init__.py' |
2 | --- breezy/archive/__init__.py 2020-02-18 01:57:45 +0000 |
3 | +++ breezy/archive/__init__.py 2022-01-03 23:15:42 +0000 |
4 | @@ -77,13 +77,14 @@ |
5 | |
6 | |
7 | def create_archive(format, tree, name, root=None, subdir=None, |
8 | - force_mtime=None): |
9 | + force_mtime=None, recurse_nested=False): |
10 | try: |
11 | archive_fn = format_registry.get(format) |
12 | except KeyError: |
13 | raise errors.NoSuchExportFormat(format) |
14 | return archive_fn(tree, name, root=root, subdir=subdir, |
15 | - force_mtime=force_mtime) |
16 | + force_mtime=force_mtime, |
17 | + recurse_nested=recurse_nested) |
18 | |
19 | |
20 | format_registry = ArchiveFormatRegistry() |
21 | |
22 | === modified file 'breezy/archive/tar.py' |
23 | --- breezy/archive/tar.py 2020-02-18 01:57:45 +0000 |
24 | +++ breezy/archive/tar.py 2022-01-03 23:15:42 +0000 |
25 | @@ -79,21 +79,20 @@ |
26 | return (item, fileobj) |
27 | |
28 | |
29 | -def tarball_generator(tree, root, subdir=None, force_mtime=None, format=''): |
30 | +def tarball_generator(tree, root, subdir=None, force_mtime=None, format='', recurse_nested=False): |
31 | """Export tree contents to a tarball. |
32 | |
33 | - :returns: A generator that will produce file content chunks. |
34 | - |
35 | - :param tree: Tree to export |
36 | - |
37 | - :param subdir: Sub directory to export |
38 | - |
39 | - :param force_mtime: Option mtime to force, instead of using tree |
40 | + Args: |
41 | + tree: Tree to export |
42 | + subdir: Sub directory to export |
43 | + force_mtime: Option mtime to force, instead of using tree |
44 | timestamps. |
45 | + Returns: A generator that will produce file content chunks. |
46 | """ |
47 | buf = BytesIO() |
48 | with closing(tarfile.open(None, "w:%s" % format, buf)) as ball, tree.lock_read(): |
49 | - for final_path, tree_path, entry in _export_iter_entries(tree, subdir): |
50 | + for final_path, tree_path, entry in _export_iter_entries( |
51 | + tree, subdir, recurse_nested=recurse_nested): |
52 | (item, fileobj) = prepare_tarball_item( |
53 | tree, root, final_path, tree_path, entry, force_mtime) |
54 | ball.addfile(item, fileobj) |
55 | @@ -104,7 +103,7 @@ |
56 | yield buf.getvalue() |
57 | |
58 | |
59 | -def tgz_generator(tree, dest, root, subdir, force_mtime=None): |
60 | +def tgz_generator(tree, dest, root, subdir, force_mtime=None, recurse_nested=False): |
61 | """Export this tree to a new tar file. |
62 | |
63 | `dest` will be created holding the contents of this tree; if it |
64 | @@ -133,7 +132,9 @@ |
65 | buf = BytesIO() |
66 | zipstream = gzip.GzipFile(basename, 'w', fileobj=buf, |
67 | mtime=root_mtime) |
68 | - for chunk in tarball_generator(tree, root, subdir, force_mtime): |
69 | + for chunk in tarball_generator( |
70 | + tree, root, subdir, force_mtime, |
71 | + recurse_nested=recurse_nested): |
72 | zipstream.write(chunk) |
73 | # Yield the data that was written so far, rinse, repeat. |
74 | yield buf.getvalue() |
75 | @@ -144,33 +145,37 @@ |
76 | yield buf.getvalue() |
77 | |
78 | |
79 | -def tbz_generator(tree, dest, root, subdir, force_mtime=None): |
80 | +def tbz_generator(tree, dest, root, subdir, force_mtime=None, recurse_nested=False): |
81 | """Export this tree to a new tar file. |
82 | |
83 | `dest` will be created holding the contents of this tree; if it |
84 | already exists, it will be clobbered, like with "tar -c". |
85 | """ |
86 | return tarball_generator( |
87 | - tree, root, subdir, force_mtime, format='bz2') |
88 | + tree, root, subdir, force_mtime, format='bz2', |
89 | + recurse_nested=recurse_nested) |
90 | |
91 | |
92 | def plain_tar_generator(tree, dest, root, subdir, |
93 | - force_mtime=None): |
94 | + force_mtime=None, recurse_nested=False): |
95 | """Export this tree to a new tar file. |
96 | |
97 | `dest` will be created holding the contents of this tree; if it |
98 | already exists, it will be clobbered, like with "tar -c". |
99 | """ |
100 | return tarball_generator( |
101 | - tree, root, subdir, force_mtime, format='') |
102 | - |
103 | - |
104 | -def tar_xz_generator(tree, dest, root, subdir, force_mtime=None): |
105 | - return tar_lzma_generator(tree, dest, root, subdir, force_mtime, "xz") |
106 | + tree, root, subdir, force_mtime, format='', |
107 | + recurse_nested=recurse_nested) |
108 | + |
109 | + |
110 | +def tar_xz_generator(tree, dest, root, subdir, force_mtime=None, recurse_nested=False): |
111 | + return tar_lzma_generator( |
112 | + tree, dest, root, subdir, force_mtime, "xz", |
113 | + recurse_nested=recurse_nested) |
114 | |
115 | |
116 | def tar_lzma_generator(tree, dest, root, subdir, force_mtime=None, |
117 | - compression_format="alone"): |
118 | + compression_format="alone", recurse_nested=False): |
119 | """Export this tree to a new .tar.lzma file. |
120 | |
121 | `dest` will be created holding the contents of this tree; if it |
122 | @@ -189,7 +194,8 @@ |
123 | }[compression_format]) |
124 | |
125 | for chunk in tarball_generator( |
126 | - tree, root, subdir, force_mtime=force_mtime): |
127 | + tree, root, subdir, force_mtime=force_mtime, |
128 | + recurse_nested=recurse_nested): |
129 | yield compressor.compress(chunk) |
130 | |
131 | yield compressor.flush() |
132 | |
133 | === modified file 'breezy/archive/zip.py' |
134 | --- breezy/archive/zip.py 2020-02-18 01:57:45 +0000 |
135 | +++ breezy/archive/zip.py 2022-01-03 23:15:42 +0000 |
136 | @@ -43,7 +43,7 @@ |
137 | |
138 | |
139 | def zip_archive_generator(tree, dest, root, subdir=None, |
140 | - force_mtime=None): |
141 | + force_mtime=None, recurse_nested=False): |
142 | """ Export this tree to a new zip file. |
143 | |
144 | `dest` will be created holding the contents of this tree; if it |
145 | @@ -53,7 +53,8 @@ |
146 | with tempfile.SpooledTemporaryFile() as buf: |
147 | with closing(zipfile.ZipFile(buf, "w", compression)) as zipf, \ |
148 | tree.lock_read(): |
149 | - for dp, tp, ie in _export_iter_entries(tree, subdir): |
150 | + for dp, tp, ie in _export_iter_entries( |
151 | + tree, subdir, recurse_nested=recurse_nested): |
152 | mutter(" export {%s} kind %s to %s", tp, ie.kind, dest) |
153 | |
154 | # zipfile.ZipFile switches all paths to forward |
155 | |
156 | === modified file 'breezy/builtins.py' |
157 | --- breezy/builtins.py 2021-12-20 23:40:21 +0000 |
158 | +++ breezy/builtins.py 2022-01-03 23:15:42 +0000 |
159 | @@ -3368,11 +3368,13 @@ |
160 | Option('uncommitted', |
161 | help='Export the working tree contents rather than that of the ' |
162 | 'last revision.'), |
163 | + Option('recurse-nested', |
164 | + help='Include contents of nested trees.'), |
165 | ] |
166 | |
167 | def run(self, dest, branch_or_subdir=None, revision=None, format=None, |
168 | root=None, filters=False, per_file_timestamps=False, uncommitted=False, |
169 | - directory=u'.'): |
170 | + directory=u'.', recurse_nested=False): |
171 | from .export import export, guess_format, get_root_name |
172 | |
173 | if branch_or_subdir is None: |
174 | @@ -3411,7 +3413,8 @@ |
175 | |
176 | try: |
177 | export(export_tree, dest, format, root, subdir, |
178 | - per_file_timestamps=per_file_timestamps) |
179 | + per_file_timestamps=per_file_timestamps, |
180 | + recurse_nested=recurse_nested) |
181 | except errors.NoSuchExportFormat as e: |
182 | raise errors.CommandError( |
183 | gettext('Unsupported export format: %s') % e.format) |
184 | |
185 | === modified file 'breezy/bzr/inventorytree.py' |
186 | --- breezy/bzr/inventorytree.py 2021-12-25 12:35:22 +0000 |
187 | +++ breezy/bzr/inventorytree.py 2022-01-03 23:15:42 +0000 |
188 | @@ -302,9 +302,11 @@ |
189 | def iter_entries(inv): |
190 | for p, e in inv.iter_entries_by_dir(specific_file_ids=inventory_file_ids): |
191 | if e.kind == 'tree-reference' and recurse_nested: |
192 | - subinv = self._get_nested_tree(p, e.file_id, e.reference_revision).root_inventory |
193 | - for subp, e in iter_entries(subinv): |
194 | - yield (osutils.pathjoin(p, subp) if subp else p), e |
195 | + subtree = self._get_nested_tree(p, e.file_id, e.reference_revision) |
196 | + with subtree.lock_read(): |
197 | + subinv = subtree.root_inventory |
198 | + for subp, e in iter_entries(subinv): |
199 | + yield (osutils.pathjoin(p, subp) if subp else p), e |
200 | else: |
201 | yield p, e |
202 | return iter_entries(self.root_inventory) |
203 | |
204 | === modified file 'breezy/bzr/remote.py' |
205 | --- breezy/bzr/remote.py 2021-08-18 22:38:08 +0000 |
206 | +++ breezy/bzr/remote.py 2022-01-03 23:15:42 +0000 |
207 | @@ -962,13 +962,19 @@ |
208 | def __init__(self, repository, inv, revision_id): |
209 | super(RemoteInventoryTree, self).__init__(repository, inv, revision_id) |
210 | |
211 | - def archive(self, format, name, root=None, subdir=None, force_mtime=None): |
212 | + def archive(self, format, name, root=None, subdir=None, force_mtime=None, recurse_nested=False): |
213 | + if recurse_nested: |
214 | + # For now, just fall back to non-HPSS mode if nested trees are involved. |
215 | + return super(RemoteInventoryTree, self).archive( |
216 | + format, name, root, subdir, force_mtime=force_mtime, |
217 | + recurse_nested=recurse_nested) |
218 | ret = self._repository._revision_archive( |
219 | self.get_revision_id(), format, name, root, subdir, |
220 | force_mtime=force_mtime) |
221 | if ret is None: |
222 | return super(RemoteInventoryTree, self).archive( |
223 | - format, name, root, subdir, force_mtime=force_mtime) |
224 | + format, name, root, subdir, force_mtime=force_mtime, |
225 | + recurse_nested=recurse_nested) |
226 | return ret |
227 | |
228 | def annotate_iter(self, path, |
229 | |
230 | === modified file 'breezy/bzr/workingtree.py' |
231 | --- breezy/bzr/workingtree.py 2021-11-13 13:01:39 +0000 |
232 | +++ breezy/bzr/workingtree.py 2022-01-03 23:15:42 +0000 |
233 | @@ -73,6 +73,7 @@ |
234 | errors, |
235 | osutils, |
236 | ) |
237 | +from ..controldir import ControlDir |
238 | from ..lock import LogicalLockResult |
239 | from .inventorytree import InventoryRevisionTree, MutableInventoryTree |
240 | from ..trace import mutter, note |
241 | @@ -509,6 +510,9 @@ |
242 | def get_nested_tree(self, path): |
243 | return WorkingTree.open(self.abspath(path)) |
244 | |
245 | + def _get_nested_tree(self, path, file_id, reference_revision): |
246 | + return self.get_nested_tree(path) |
247 | + |
248 | def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False): |
249 | """See MutableTree.set_parent_trees.""" |
250 | parent_ids = [rev for (rev, tree) in parents_list] |
251 | @@ -1756,7 +1760,7 @@ |
252 | # The only trick here is that if we supports_tree_reference then we |
253 | # need to detect if a directory becomes a tree-reference. |
254 | iterator = super(WorkingTree, self).iter_entries_by_dir( |
255 | - specific_files=specific_files) |
256 | + specific_files=specific_files, recurse_nested=recurse_nested) |
257 | if not self.supports_tree_reference(): |
258 | return iterator |
259 | else: |
260 | |
261 | === modified file 'breezy/export.py' |
262 | --- breezy/export.py 2022-01-03 13:33:13 +0000 |
263 | +++ breezy/export.py 2022-01-03 23:15:42 +0000 |
264 | @@ -31,27 +31,29 @@ |
265 | |
266 | |
267 | def export(tree, dest, format=None, root=None, subdir=None, |
268 | - per_file_timestamps=False, fileobj=None): |
269 | + per_file_timestamps=False, fileobj=None, |
270 | + recurse_nested=False): |
271 | """Export the given Tree to the specific destination. |
272 | |
273 | - :param tree: A Tree (such as RevisionTree) to export |
274 | - :param dest: The destination where the files,etc should be put |
275 | - :param format: The format (dir, zip, etc), if None, it will check the |
276 | - extension on dest, looking for a match |
277 | - :param root: The root location inside the format. |
278 | - It is common practise to have zipfiles and tarballs |
279 | - extract into a subdirectory, rather than into the |
280 | - current working directory. |
281 | - If root is None, the default root will be |
282 | - selected as the destination without its |
283 | - extension. |
284 | - :param subdir: A starting directory within the tree. None means to export |
285 | - the entire tree, and anything else should specify the relative path to |
286 | - a directory to start exporting from. |
287 | - :param per_file_timestamps: Whether to use the timestamp stored in the |
288 | - tree rather than now(). This will do a revision lookup |
289 | - for every file so will be significantly slower. |
290 | - :param fileobj: Optional file object to use |
291 | + Args: |
292 | + tree: A Tree (such as RevisionTree) to export |
293 | + dest: The destination where the files,etc should be put |
294 | + format: The format (dir, zip, etc), if None, it will check the |
295 | + extension on dest, looking for a match |
296 | + root: The root location inside the format. |
297 | + It is common practise to have zipfiles and tarballs |
298 | + extract into a subdirectory, rather than into the |
299 | + current working directory. |
300 | + If root is None, the default root will be |
301 | + selected as the destination without its |
302 | + extension. |
303 | + subdir: A starting directory within the tree. None means to export the |
304 | + entire tree, and anything else should specify the relative path |
305 | + to a directory to start exporting from. |
306 | + per_file_timestamps: Whether to use the timestamp stored in the tree |
307 | + rather than now(). This will do a revision lookup for every file so will |
308 | + be significantly slower. |
309 | + fileobj: Optional file object to use |
310 | """ |
311 | if format is None and dest is not None: |
312 | format = guess_format(dest) |
313 | @@ -79,13 +81,15 @@ |
314 | # then we should stream a tar file and unpack that on the fly. |
315 | with tree.lock_read(): |
316 | for unused in dir_exporter_generator(tree, dest, root, subdir, |
317 | - force_mtime): |
318 | + force_mtime, |
319 | + recurse_nested=recurse_nested): |
320 | pass |
321 | return |
322 | |
323 | with tree.lock_read(): |
324 | chunks = tree.archive(format, dest, root=root, |
325 | - subdir=subdir, force_mtime=force_mtime) |
326 | + subdir=subdir, force_mtime=force_mtime, |
327 | + recurse_nested=recurse_nested) |
328 | if dest == '-': |
329 | for chunk in chunks: |
330 | getattr(sys.stdout, 'buffer', sys.stdout).write(chunk) |
331 | @@ -126,7 +130,7 @@ |
332 | return dest |
333 | |
334 | |
335 | -def _export_iter_entries(tree, subdir, skip_special=True): |
336 | +def _export_iter_entries(tree, subdir, skip_special=True, recurse_nested=False): |
337 | """Iter the entries for tree suitable for exporting. |
338 | |
339 | :param tree: A tree object. |
340 | @@ -139,7 +143,7 @@ |
341 | subdir = None |
342 | if subdir is not None: |
343 | subdir = subdir.rstrip('/') |
344 | - entries = tree.iter_entries_by_dir() |
345 | + entries = tree.iter_entries_by_dir(recurse_nested=recurse_nested) |
346 | for path, entry in entries: |
347 | if path == '': |
348 | continue |
349 | @@ -166,7 +170,8 @@ |
350 | |
351 | |
352 | def dir_exporter_generator(tree, dest, root, subdir=None, |
353 | - force_mtime=None, fileobj=None): |
354 | + force_mtime=None, fileobj=None, |
355 | + recurse_nested=False): |
356 | """Return a generator that exports this tree to a new directory. |
357 | |
358 | `dest` should either not exist or should be empty. If it does not exist it |
359 | @@ -192,7 +197,8 @@ |
360 | # Note in the case of revision trees, this does trigger a double inventory |
361 | # lookup, hopefully it isn't too expensive. |
362 | to_fetch = [] |
363 | - for dp, tp, ie in _export_iter_entries(tree, subdir): |
364 | + for dp, tp, ie in _export_iter_entries( |
365 | + tree, subdir, recurse_nested=recurse_nested): |
366 | fullpath = osutils.pathjoin(dest, dp) |
367 | if ie.kind == "file": |
368 | to_fetch.append((tp, (dp, tp, None))) |
369 | |
370 | === modified file 'breezy/git/remote.py' |
371 | --- breezy/git/remote.py 2021-11-13 13:27:11 +0000 |
372 | +++ breezy/git/remote.py 2022-01-03 23:15:42 +0000 |
373 | @@ -421,7 +421,9 @@ |
374 | return RemoteGitRepository |
375 | |
376 | def archive(self, format, committish, write_data, progress=None, |
377 | - write_error=None, subdirs=None, prefix=None): |
378 | + write_error=None, subdirs=None, prefix=None, recurse_nested=False): |
379 | + if recurse_nested: |
380 | + raise NotImplementedError('recurse_nested is not yet supported') |
381 | if progress is None: |
382 | pb = ui.ui_factory.nested_progress_bar() |
383 | progress = DefaultProgressReporter(pb).progress |
384 | @@ -882,7 +884,7 @@ |
385 | |
386 | class GitRemoteRevisionTree(RevisionTree): |
387 | |
388 | - def archive(self, format, name, root=None, subdir=None, force_mtime=None): |
389 | + def archive(self, format, name, root=None, subdir=None, force_mtime=None, recurse_nested=False): |
390 | """Create an archive of this tree. |
391 | |
392 | :param format: Format name (e.g. 'tar') |
393 | @@ -891,6 +893,8 @@ |
394 | :param subdir: Subdirectory to export (or None) |
395 | :return: Iterator over archive chunks |
396 | """ |
397 | + if recurse_nested: |
398 | + raise NotImplementedError('recurse_nested is not yet supported') |
399 | commit = self._repository.lookup_bzr_revision_id( |
400 | self.get_revision_id())[0] |
401 | import tempfile |
402 | |
403 | === modified file 'breezy/git/tree.py' |
404 | --- breezy/git/tree.py 2021-12-25 12:35:22 +0000 |
405 | +++ breezy/git/tree.py 2022-01-03 23:15:42 +0000 |
406 | @@ -351,6 +351,9 @@ |
407 | else: |
408 | nested_repo_transport = self._repository.controldir.control_transport.clone( |
409 | posixpath.join('modules', decode_git_path(info[1]))) |
410 | + if not nested_repo_transport.has('.'): |
411 | + nested_repo_transport = self._repository.controldir.user_transport.clone( |
412 | + posixpath.join(decode_git_path(info[1]), '.git')) |
413 | nested_controldir = _mod_controldir.ControlDir.open_from_transport( |
414 | nested_repo_transport) |
415 | return nested_controldir.find_repository() |
416 | @@ -574,14 +577,16 @@ |
417 | child_path_decoded = decode_git_path(child_path) |
418 | if recurse_nested and S_ISGITLINK(mode): |
419 | mode = stat.S_IFDIR |
420 | - store = self._get_submodule_store(child_path) |
421 | - hexsha = store[hexsha].tree |
422 | + substore = self._get_submodule_store(child_path) |
423 | + hexsha = substore[hexsha].tree |
424 | + else: |
425 | + substore = store |
426 | if stat.S_ISDIR(mode): |
427 | if (specific_files is None or |
428 | any([p for p in specific_files if p.startswith( |
429 | child_path)])): |
430 | extradirs.append( |
431 | - (store, child_path, hexsha, |
432 | + (substore, child_path, hexsha, |
433 | self.path2id(child_path_decoded))) |
434 | if specific_files is None or child_path in specific_files: |
435 | if stat.S_ISDIR(mode): |
436 | @@ -589,7 +594,7 @@ |
437 | self._get_dir_ie(child_path, parent_id)) |
438 | else: |
439 | yield (child_path_decoded, |
440 | - self._get_file_ie(store, child_path, name, mode, |
441 | + self._get_file_ie(substore, child_path, name, mode, |
442 | hexsha, parent_id)) |
443 | todo.extendleft(reversed(extradirs)) |
444 | |
445 | |
446 | === modified file 'breezy/tests/blackbox/test_export.py' |
447 | --- breezy/tests/blackbox/test_export.py 2020-06-19 21:26:53 +0000 |
448 | +++ breezy/tests/blackbox/test_export.py 2022-01-03 23:15:42 +0000 |
449 | @@ -241,6 +241,23 @@ |
450 | d_info = zfile.getinfo(names[3]) |
451 | self.assertEqual(dir_attr, d_info.external_attr) |
452 | |
453 | + def test_dir_export_nested(self): |
454 | + tree = self.make_branch_and_tree('dir') |
455 | + self.build_tree(['dir/a']) |
456 | + tree.add('a') |
457 | + |
458 | + subtree = self.make_branch_and_tree('dir/subdir') |
459 | + tree.add_reference(subtree) |
460 | + |
461 | + self.build_tree(['dir/subdir/b']) |
462 | + subtree.add('b') |
463 | + |
464 | + self.run_bzr('export --uncommitted direxport1 dir') |
465 | + self.assertFalse(os.path.exists('direxport1/subdir/b')) |
466 | + |
467 | + self.run_bzr('export --recurse-nested --uncommitted direxport2 dir') |
468 | + self.assertTrue(os.path.exists('direxport2/subdir/b')) |
469 | + |
470 | def test_dir_export(self): |
471 | tree = self.make_branch_and_tree('dir') |
472 | self.build_tree(['dir/a']) |
473 | |
474 | === modified file 'breezy/tests/per_tree/test_export.py' |
475 | --- breezy/tests/per_tree/test_export.py 2021-12-08 18:46:11 +0000 |
476 | +++ breezy/tests/per_tree/test_export.py 2022-01-03 23:15:42 +0000 |
477 | @@ -18,12 +18,14 @@ |
478 | import tarfile |
479 | import zipfile |
480 | |
481 | +from breezy.errors import UnsupportedOperation |
482 | from breezy.export import export |
483 | from breezy import osutils |
484 | from breezy import tests |
485 | from breezy.tests.per_tree import TestCaseWithTree |
486 | from breezy.tests import ( |
487 | features, |
488 | + TestNotApplicable, |
489 | ) |
490 | |
491 | |
492 | @@ -59,6 +61,35 @@ |
493 | names = self.get_export_names() |
494 | self.assertIn('output/link', names) |
495 | |
496 | + def prepare_nested_export(self, recurse_nested): |
497 | + tree = self.make_branch_and_tree('dir') |
498 | + self.build_tree(['dir/a']) |
499 | + tree.add('a') |
500 | + tree.commit('1') |
501 | + |
502 | + subtree = self.make_branch_and_tree('dir/subdir') |
503 | + |
504 | + self.build_tree(['dir/subdir/b']) |
505 | + subtree.add('b') |
506 | + subtree.commit('1a') |
507 | + |
508 | + try: |
509 | + tree.add_reference(subtree) |
510 | + except UnsupportedOperation: |
511 | + raise TestNotApplicable('format does not supported nested trees') |
512 | + tree.commit('2') |
513 | + export(tree, 'output', self.exporter, recurse_nested=recurse_nested) |
514 | + |
515 | + def test_export_nested_recurse(self): |
516 | + self.prepare_nested_export(True) |
517 | + names = self.get_export_names() |
518 | + self.assertIn('output/subdir/b', names) |
519 | + |
520 | + def test_export_nested_nonrecurse(self): |
521 | + self.prepare_nested_export(False) |
522 | + names = self.get_export_names() |
523 | + self.assertNotIn('output/subdir/b', names) |
524 | + |
525 | |
526 | class TestTar(ExportTest, TestCaseWithTree): |
527 | |
528 | @@ -94,5 +125,10 @@ |
529 | exporter = 'dir' |
530 | |
531 | def get_export_names(self): |
532 | - return [osutils.pathjoin('output', name) |
533 | - for name in os.listdir('output')] |
534 | + ret = [] |
535 | + for (dirpath, dirnames, filenames) in os.walk('output'): |
536 | + for dirname in dirnames: |
537 | + ret.append(osutils.pathjoin(dirpath, dirname)) |
538 | + for filename in filenames: |
539 | + ret.append(osutils.pathjoin(dirpath, filename)) |
540 | + return ret |
541 | |
542 | === modified file 'breezy/tree.py' |
543 | --- breezy/tree.py 2021-12-25 12:35:22 +0000 |
544 | +++ breezy/tree.py 2022-01-03 23:15:42 +0000 |
545 | @@ -713,19 +713,21 @@ |
546 | return searcher |
547 | |
548 | def archive(self, format, name, root='', subdir=None, |
549 | - force_mtime=None): |
550 | + force_mtime=None, recurse_nested=False): |
551 | """Create an archive of this tree. |
552 | |
553 | - :param format: Format name (e.g. 'tar') |
554 | - :param name: target file name |
555 | - :param root: Root directory name (or None) |
556 | - :param subdir: Subdirectory to export (or None) |
557 | - :return: Iterator over archive chunks |
558 | + Args: |
559 | + format: Format name (e.g. 'tar') |
560 | + name: target file name |
561 | + root: Root directory name (or None) |
562 | + subdir: Subdirectory to export (or None) |
563 | + Returns: Iterator over archive chunks |
564 | """ |
565 | from .archive import create_archive |
566 | with self.lock_read(): |
567 | return create_archive(format, self, name, root, |
568 | - subdir, force_mtime=force_mtime) |
569 | + subdir, force_mtime=force_mtime, |
570 | + recurse_nested=recurse_nested) |
571 | |
572 | @classmethod |
573 | def versionable_kind(cls, kind): |
Running landing tests failed /ci.breezy- vcs.org/ job/brz/ job/brz- land/882/
https:/