Переглянути джерело

Add "--strip-components all" on the "extract" action to remove leading path components (#647).

Dan Helfman 2 роки тому
батько
коміт
8cec7c74d8
4 змінених файлів з 62 додано та 3 видалено
  1. 2 0
      NEWS
  2. 7 0
      borgmatic/borg/extract.py
  3. 2 3
      borgmatic/commands/arguments.py
  4. 51 0
      tests/unit/borg/test_extract.py

+ 2 - 0
NEWS

@@ -6,6 +6,8 @@
  * #304: Run any command-line actions in the order specified instead of using a fixed ordering.
  * #628: Add a Healthchecks "log" state to send borgmatic logs to Healthchecks without signalling
    success or failure.
+ * #647: Add "--strip-components all" feature on the "extract" action to remove leading path
+   components of files you extract. Must be used with the "--path" flag.
 
 1.7.8
  * #620: With the "create" action and the "--list" ("--files") flag, only show excluded files at

+ 7 - 0
borgmatic/borg/extract.py

@@ -87,6 +87,13 @@ def extract_archive(
     else:
         numeric_ids_flags = ('--numeric-owner',) if location_config.get('numeric_ids') else ()
 
+    if strip_components == 'all':
+        if not paths:
+            raise ValueError('The --strip-components flag with "all" requires at least one --path')
+
+        # Calculate the maximum number of leading path components of the given paths.
+        strip_components = max(0, *(len(path.split(os.path.sep)) - 1 for path in paths))
+
     full_command = (
         (local_path, 'extract')
         + (('--remote-path', remote_path) if remote_path else ())

+ 2 - 3
borgmatic/commands/arguments.py

@@ -476,10 +476,9 @@ def make_parsers():
     )
     extract_group.add_argument(
         '--strip-components',
-        type=int,
+        type=lambda number: number if number == 'all' else int(number),
         metavar='NUMBER',
-        dest='strip_components',
-        help='Number of leading path components to remove from each extracted path. Skip paths with fewer elements',
+        help='Number of leading path components to remove from each extracted path or "all" to strip all leading path components. Skip paths with fewer elements',
     )
     extract_group.add_argument(
         '--progress',

+ 51 - 0
tests/unit/borg/test_extract.py

@@ -312,6 +312,57 @@ def test_extract_archive_calls_borg_with_strip_components():
     )
 
 
+def test_extract_archive_calls_borg_with_strip_components_calculated_from_all():
+    flexmock(module.os.path).should_receive('abspath').and_return('repo')
+    insert_execute_command_mock(
+        (
+            'borg',
+            'extract',
+            '--strip-components',
+            '2',
+            'repo::archive',
+            'foo/bar/baz.txt',
+            'foo/bar.txt',
+        )
+    )
+    flexmock(module.feature).should_receive('available').and_return(True)
+    flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
+        ('repo::archive',)
+    )
+
+    module.extract_archive(
+        dry_run=False,
+        repository='repo',
+        archive='archive',
+        paths=['foo/bar/baz.txt', 'foo/bar.txt'],
+        location_config={},
+        storage_config={},
+        local_borg_version='1.2.3',
+        strip_components='all',
+    )
+
+
+def test_extract_archive_with_strip_components_all_and_no_paths_raises():
+    flexmock(module.os.path).should_receive('abspath').and_return('repo')
+    flexmock(module.feature).should_receive('available').and_return(True)
+    flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
+        ('repo::archive',)
+    )
+    flexmock(module).should_receive('execute_command').never()
+
+    with pytest.raises(ValueError):
+        module.extract_archive(
+            dry_run=False,
+            repository='repo',
+            archive='archive',
+            paths=None,
+            location_config={},
+            storage_config={},
+            local_borg_version='1.2.3',
+            strip_components='all',
+        )
+
+
 def test_extract_archive_calls_borg_with_progress_parameter():
     flexmock(module.os.path).should_receive('abspath').and_return('repo')
     flexmock(module.environment).should_receive('make_environment')