From 018176771ed7ae35d506835c220e5fc78e8d66af Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 17:38:45 +0100
Subject: [PATCH 01/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=201?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Added command section markers that will help when moving the sections later.
---
 src/borg/archiver.py | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 1b98d887c..4dba922fa 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2602,6 +2602,7 @@ class Archiver:
         mid_common_parser.set_defaults(paths=[], patterns=[])
         parser.common_options.add_common_group(mid_common_parser, '_midcommand')
 
+        # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
         browsing an archive or restoring individual files. Unless the ``--foreground``
@@ -2671,6 +2672,7 @@ class Archiver:
         if parser.prog == 'borgfs':
             return parser
 
+        # borg serve
         serve_epilog = process_epilog("""
         This command starts a repository server process. This command is usually not used manually.
         """)
@@ -2702,6 +2704,7 @@ class Archiver:
                                     'When a new repository is initialized, sets the storage quota on the new '
                                     'repository as well. Default: no quota.')
 
+        # borg init
         init_epilog = process_epilog("""
         This command initializes an empty repository. A repository is a filesystem
         directory containing the deduplicated data from zero or more archives.
@@ -2827,6 +2830,7 @@ class Archiver:
         subparser.add_argument('--make-parent-dirs', dest='make_parent_dirs', action='store_true',
                                help='create the parent directories of the repository directory, if they are missing.')
 
+        # borg check
         check_epilog = process_epilog("""
         The check command verifies the consistency of a repository and the corresponding archives.
 
@@ -2899,6 +2903,7 @@ class Archiver:
                                    help='do only a partial repo check for max. SECONDS seconds (Default: unlimited)')
         define_archive_filters_group(subparser)
 
+        # borg key
         subparser = subparsers.add_parser('key', parents=[mid_common_parser], add_help=False,
                                           description="Manage a keyfile or repokey of a repository",
                                           epilog="",
@@ -3011,6 +3016,7 @@ class Archiver:
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False))
 
+        # borg create
         create_epilog = process_epilog("""
         This command creates a backup archive containing all files found while recursively
         traversing all paths specified. Paths are added to the archive as they are given,
@@ -3213,6 +3219,7 @@ class Archiver:
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
                                help='paths to archive')
 
+        # borg extract
         extract_epilog = process_epilog("""
         This command extracts the contents of an archive. By default the entire
         archive is extracted but a subset of files and directories can be selected
@@ -3258,6 +3265,7 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
+        # borg export-tar
         export_tar_epilog = process_epilog("""
         This command creates a tarball from an archive.
 
@@ -3312,6 +3320,7 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
+        # borg diff
         diff_epilog = process_epilog("""
             This command finds differences (file contents, user/group/mode) between archives.
 
@@ -3352,6 +3361,7 @@ class Archiver:
                                help='paths of items inside the archives to compare; patterns are supported')
         define_exclusion_group(subparser)
 
+        # borg rename
         rename_epilog = process_epilog("""
         This command renames an archive in the repository.
 
@@ -3370,6 +3380,7 @@ class Archiver:
                                type=archivename_validator(),
                                help='the new archive name to use')
 
+        # borg delete
         delete_epilog = process_epilog("""
         This command deletes an archive from the repository or the complete repository.
 
@@ -3420,6 +3431,7 @@ class Archiver:
                                help='archives to delete')
         define_archive_filters_group(subparser)
 
+        # borg list
         list_epilog = process_epilog("""
         This command lists the contents of a repository or an archive.
 
@@ -3470,6 +3482,7 @@ class Archiver:
         define_archive_filters_group(subparser)
         define_exclusion_group(subparser)
 
+        # borg umount
         umount_epilog = process_epilog("""
         This command un-mounts a FUSE filesystem that was mounted with ``borg mount``.
 
@@ -3485,6 +3498,7 @@ class Archiver:
         subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
                                help='mountpoint of the filesystem to umount')
 
+        # borg info
         info_epilog = process_epilog("""
         This command displays detailed information about the specified archive or repository.
 
@@ -3515,6 +3529,7 @@ class Archiver:
                                help='format output as JSON')
         define_archive_filters_group(subparser)
 
+        # borg break-lock
         break_lock_epilog = process_epilog("""
         This command breaks the repository and cache locks.
         Please use carefully and only while no borg process (on any machine) is
@@ -3530,6 +3545,7 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='repository for which to break the locks')
 
+        # borg prune
         prune_epilog = process_epilog("""
         The prune command prunes a repository by deleting all archives not matching
         any of the specified retention options.
@@ -3616,6 +3632,7 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='repository to prune')
 
+        # borg upgrade
         upgrade_epilog = process_epilog("""
         Upgrade an existing, local Borg repository.
 
@@ -3719,6 +3736,7 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='path to the repository to be upgraded')
 
+        # borg recreate
         recreate_epilog = process_epilog("""
         Recreate the contents of existing archives.
 
@@ -3827,6 +3845,7 @@ class Archiver:
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
                                help='paths to recreate; patterns are supported')
 
+        # borg with-lock
         with_lock_epilog = process_epilog("""
         This command runs a user-specified command while the repository lock is held.
 
@@ -3856,6 +3875,7 @@ class Archiver:
         subparser.add_argument('args', metavar='ARGS', nargs=argparse.REMAINDER,
                                help='command arguments')
 
+        # borg compact
         compact_epilog = process_epilog("""
         This command frees repository space by compacting segments.
 
@@ -3881,6 +3901,7 @@ class Archiver:
         subparser.add_argument('--cleanup-commits', dest='cleanup_commits', action='store_true',
                                help='cleanup commit-only 17-byte segment files')
 
+        # borg config
         config_epilog = process_epilog("""
         This command gets and sets options in a local repository or cache config file.
         For security reasons, this command only works on local repositories.
@@ -3927,6 +3948,7 @@ class Archiver:
         subparser.add_argument('topic', metavar='TOPIC', type=str, nargs='?',
                                help='additional help on TOPIC')
 
+        # borg debug
         debug_epilog = process_epilog("""
         These commands are not intended for normal use and potentially very
         dangerous if used incorrectly.
@@ -4105,6 +4127,7 @@ class Archiver:
         subparser.add_argument('output', metavar='OUTPUT', type=argparse.FileType('wb'),
                                help='Output file')
 
+        # borg benchmark
         benchmark_epilog = process_epilog("These commands do various benchmarks.")
 
         subparser = subparsers.add_parser('benchmark', parents=[mid_common_parser], add_help=False,

From 2e42543d3de71c321bafebd5a12da3a39677fea1 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 18:34:09 +0100
Subject: [PATCH 02/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=202?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Move subparsers definition out of "# borg mount" to the top so the mount section can be moved downwards.
---
 src/borg/archiver.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 4dba922fa..f4fb127c6 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2602,6 +2602,8 @@ class Archiver:
         mid_common_parser.set_defaults(paths=[], patterns=[])
         parser.common_options.add_common_group(mid_common_parser, '_midcommand')
 
+        subparsers = parser.add_subparsers(title='required arguments', metavar='<command>')
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -2649,7 +2651,6 @@ class Archiver:
             parser.help = 'mount repository'
             subparser = parser
         else:
-            subparsers = parser.add_subparsers(title='required arguments', metavar='<command>')
             subparser = subparsers.add_parser('mount', parents=[common_parser], add_help=False,
                                             description=self.do_mount.__doc__,
                                             epilog=mount_epilog,

From 737d17cc3ff99d46bd6b155c0171c98c029c8c89 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 18:40:28 +0100
Subject: [PATCH 03/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=203?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg benchmark and borg break-lock to the top.
---
 src/borg/archiver.py | 160 +++++++++++++++++++++----------------------
 1 file changed, 80 insertions(+), 80 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index f4fb127c6..6b8ee3a9e 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2604,6 +2604,86 @@ class Archiver:
 
         subparsers = parser.add_subparsers(title='required arguments', metavar='<command>')
 
+        # borg benchmark
+        benchmark_epilog = process_epilog("These commands do various benchmarks.")
+
+        subparser = subparsers.add_parser('benchmark', parents=[mid_common_parser], add_help=False,
+                                          description='benchmark command',
+                                          epilog=benchmark_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='benchmark command')
+
+        benchmark_parsers = subparser.add_subparsers(title='required arguments', metavar='<command>')
+        subparser.set_defaults(fallback_func=functools.partial(self.do_subcommand_help, subparser))
+
+        bench_crud_epilog = process_epilog("""
+        This command benchmarks borg CRUD (create, read, update, delete) operations.
+
+        It creates input data below the given PATH and backups this data into the given REPO.
+        The REPO must already exist (it could be a fresh empty repo or an existing repo, the
+        command will create / read / update / delete some archives named borg-benchmark-crud\\* there.
+
+        Make sure you have free space there, you'll need about 1GB each (+ overhead).
+
+        If your repository is encrypted and borg needs a passphrase to unlock the key, use:
+
+        BORG_PASSPHRASE=mysecret borg benchmark crud REPO PATH
+
+        Measurements are done with different input file sizes and counts.
+        The file contents are very artificial (either all zero or all random),
+        thus the measurement results do not necessarily reflect performance with real data.
+        Also, due to the kind of content used, no compression is used in these benchmarks.
+
+        C- == borg create (1st archive creation, no compression, do not use files cache)
+              C-Z- == all-zero files. full dedup, this is primarily measuring reader/chunker/hasher.
+              C-R- == random files. no dedup, measuring throughput through all processing stages.
+
+        R- == borg extract (extract archive, dry-run, do everything, but do not write files to disk)
+              R-Z- == all zero files. Measuring heavily duplicated files.
+              R-R- == random files. No duplication here, measuring throughput through all processing
+              stages, except writing to disk.
+
+        U- == borg create (2nd archive creation of unchanged input files, measure files cache speed)
+              The throughput value is kind of virtual here, it does not actually read the file.
+              U-Z- == needs to check the 2 all-zero chunks' existence in the repo.
+              U-R- == needs to check existence of a lot of different chunks in the repo.
+
+        D- == borg delete archive (delete last remaining archive, measure deletion + compaction)
+              D-Z- == few chunks to delete / few segments to compact/remove.
+              D-R- == many chunks to delete / many segments to compact/remove.
+
+        Please note that there might be quite some variance in these measurements.
+        Try multiple measurements and having a otherwise idle machine (and network, if you use it).
+        """)
+        subparser = benchmark_parsers.add_parser('crud', parents=[common_parser], add_help=False,
+                                                 description=self.do_benchmark_crud.__doc__,
+                                                 epilog=bench_crud_epilog,
+                                                 formatter_class=argparse.RawDescriptionHelpFormatter,
+                                                 help='benchmarks borg CRUD (create, extract, update, delete).')
+        subparser.set_defaults(func=self.do_benchmark_crud)
+
+        subparser.add_argument('location', metavar='REPO',
+                               type=location_validator(archive=False),
+                               help='repo to use for benchmark (must exist)')
+
+        subparser.add_argument('path', metavar='PATH', help='path were to create benchmark input data')
+
+        # borg break-lock
+        break_lock_epilog = process_epilog("""
+        This command breaks the repository and cache locks.
+        Please use carefully and only while no borg process (on any machine) is
+        trying to access the Cache or the Repository.
+        """)
+        subparser = subparsers.add_parser('break-lock', parents=[common_parser], add_help=False,
+                                          description=self.do_break_lock.__doc__,
+                                          epilog=break_lock_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='break repository and cache locks')
+        subparser.set_defaults(func=self.do_break_lock)
+        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
+                               type=location_validator(archive=False),
+                               help='repository for which to break the locks')
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3530,22 +3610,6 @@ class Archiver:
                                help='format output as JSON')
         define_archive_filters_group(subparser)
 
-        # borg break-lock
-        break_lock_epilog = process_epilog("""
-        This command breaks the repository and cache locks.
-        Please use carefully and only while no borg process (on any machine) is
-        trying to access the Cache or the Repository.
-        """)
-        subparser = subparsers.add_parser('break-lock', parents=[common_parser], add_help=False,
-                                          description=self.do_break_lock.__doc__,
-                                          epilog=break_lock_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='break repository and cache locks')
-        subparser.set_defaults(func=self.do_break_lock)
-        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
-                               type=location_validator(archive=False),
-                               help='repository for which to break the locks')
-
         # borg prune
         prune_epilog = process_epilog("""
         The prune command prunes a repository by deleting all archives not matching
@@ -4128,70 +4192,6 @@ class Archiver:
         subparser.add_argument('output', metavar='OUTPUT', type=argparse.FileType('wb'),
                                help='Output file')
 
-        # borg benchmark
-        benchmark_epilog = process_epilog("These commands do various benchmarks.")
-
-        subparser = subparsers.add_parser('benchmark', parents=[mid_common_parser], add_help=False,
-                                          description='benchmark command',
-                                          epilog=benchmark_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='benchmark command')
-
-        benchmark_parsers = subparser.add_subparsers(title='required arguments', metavar='<command>')
-        subparser.set_defaults(fallback_func=functools.partial(self.do_subcommand_help, subparser))
-
-        bench_crud_epilog = process_epilog("""
-        This command benchmarks borg CRUD (create, read, update, delete) operations.
-
-        It creates input data below the given PATH and backups this data into the given REPO.
-        The REPO must already exist (it could be a fresh empty repo or an existing repo, the
-        command will create / read / update / delete some archives named borg-benchmark-crud\\* there.
-
-        Make sure you have free space there, you'll need about 1GB each (+ overhead).
-
-        If your repository is encrypted and borg needs a passphrase to unlock the key, use:
-
-        BORG_PASSPHRASE=mysecret borg benchmark crud REPO PATH
-
-        Measurements are done with different input file sizes and counts.
-        The file contents are very artificial (either all zero or all random),
-        thus the measurement results do not necessarily reflect performance with real data.
-        Also, due to the kind of content used, no compression is used in these benchmarks.
-
-        C- == borg create (1st archive creation, no compression, do not use files cache)
-              C-Z- == all-zero files. full dedup, this is primarily measuring reader/chunker/hasher.
-              C-R- == random files. no dedup, measuring throughput through all processing stages.
-
-        R- == borg extract (extract archive, dry-run, do everything, but do not write files to disk)
-              R-Z- == all zero files. Measuring heavily duplicated files.
-              R-R- == random files. No duplication here, measuring throughput through all processing
-              stages, except writing to disk.
-
-        U- == borg create (2nd archive creation of unchanged input files, measure files cache speed)
-              The throughput value is kind of virtual here, it does not actually read the file.
-              U-Z- == needs to check the 2 all-zero chunks' existence in the repo.
-              U-R- == needs to check existence of a lot of different chunks in the repo.
-
-        D- == borg delete archive (delete last remaining archive, measure deletion + compaction)
-              D-Z- == few chunks to delete / few segments to compact/remove.
-              D-R- == many chunks to delete / many segments to compact/remove.
-
-        Please note that there might be quite some variance in these measurements.
-        Try multiple measurements and having a otherwise idle machine (and network, if you use it).
-        """)
-        subparser = benchmark_parsers.add_parser('crud', parents=[common_parser], add_help=False,
-                                                 description=self.do_benchmark_crud.__doc__,
-                                                 epilog=bench_crud_epilog,
-                                                 formatter_class=argparse.RawDescriptionHelpFormatter,
-                                                 help='benchmarks borg CRUD (create, extract, update, delete).')
-        subparser.set_defaults(func=self.do_benchmark_crud)
-
-        subparser.add_argument('location', metavar='REPO',
-                               type=location_validator(archive=False),
-                               help='repo to use for benchmark (must exist)')
-
-        subparser.add_argument('path', metavar='PATH', help='path were to create benchmark input data')
-
         return parser
 
     def get_args(self, argv, cmd):

From 55522b11fdfe75ddeb1e331af49f8afcb1dc62de Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 18:44:44 +0100
Subject: [PATCH 04/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=204?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg check after borg break-lock.
---
 src/borg/archiver.py | 146 +++++++++++++++++++++----------------------
 1 file changed, 73 insertions(+), 73 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 6b8ee3a9e..d2cb44781 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2684,6 +2684,79 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='repository for which to break the locks')
 
+        # borg check
+        check_epilog = process_epilog("""
+        The check command verifies the consistency of a repository and the corresponding archives.
+
+        First, the underlying repository data files are checked:
+
+        - For all segments the segment magic (header) is checked
+        - For all objects stored in the segments, all metadata (e.g. crc and size) and
+          all data is read. The read data is checked by size and CRC. Bit rot and other
+          types of accidental damage can be detected this way.
+        - If we are in repair mode and a integrity error is detected for a segment,
+          we try to recover as many objects from the segment as possible.
+        - In repair mode, it makes sure that the index is consistent with the data
+          stored in the segments.
+        - If you use a remote repo server via ssh:, the repo check is executed on the
+          repo server without causing significant network traffic.
+        - The repository check can be skipped using the ``--archives-only`` option.
+
+        Second, the consistency and correctness of the archive metadata is verified:
+
+        - Is the repo manifest present? If not, it is rebuilt from archive metadata
+          chunks (this requires reading and decrypting of all metadata and data).
+        - Check if archive metadata chunk is present. if not, remove archive from
+          manifest.
+        - For all files (items) in the archive, for all chunks referenced by these
+          files, check if chunk is present.
+          If a chunk is not present and we are in repair mode, replace it with a same-size
+          replacement chunk of zeros.
+          If a previously lost chunk reappears (e.g. via a later backup) and we are in
+          repair mode, the all-zero replacement chunk will be replaced by the correct chunk.
+          This requires reading of archive and file metadata, but not data.
+        - If we are in repair mode and we checked all the archives: delete orphaned
+          chunks from the repo.
+        - if you use a remote repo server via ssh:, the archive check is executed on
+          the client machine (because if encryption is enabled, the checks will require
+          decryption and this is always done client-side, because key access will be
+          required).
+        - The archive checks can be time consuming, they can be skipped using the
+          ``--repository-only`` option.
+
+        The ``--verify-data`` option will perform a full integrity verification (as opposed to
+        checking the CRC32 of the segment) of data, which means reading the data from the
+        repository, decrypting and decompressing it. This is a cryptographic verification,
+        which will detect (accidental) corruption. For encrypted repositories it is
+        tamper-resistant as well, unless the attacker has access to the keys.
+
+        It is also very slow.
+        """)
+        subparser = subparsers.add_parser('check', parents=[common_parser], add_help=False,
+                                          description=self.do_check.__doc__,
+                                          epilog=check_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='verify repository')
+        subparser.set_defaults(func=self.do_check)
+        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
+                               type=location_validator(),
+                               help='repository or archive to check consistency of')
+        subparser.add_argument('--repository-only', dest='repo_only', action='store_true',
+                               help='only perform repository checks')
+        subparser.add_argument('--archives-only', dest='archives_only', action='store_true',
+                               help='only perform archives checks')
+        subparser.add_argument('--verify-data', dest='verify_data', action='store_true',
+                               help='perform cryptographic archive data integrity verification '
+                                    '(conflicts with ``--repository-only``)')
+        subparser.add_argument('--repair', dest='repair', action='store_true',
+                               help='attempt to repair any inconsistencies found')
+        subparser.add_argument('--save-space', dest='save_space', action='store_true',
+                               help='work slower, but using less space')
+        subparser.add_argument('--max-duration', metavar='SECONDS', dest='max_duration',
+                                   type=int, default=0,
+                                   help='do only a partial repo check for max. SECONDS seconds (Default: unlimited)')
+        define_archive_filters_group(subparser)
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -2911,79 +2984,6 @@ class Archiver:
         subparser.add_argument('--make-parent-dirs', dest='make_parent_dirs', action='store_true',
                                help='create the parent directories of the repository directory, if they are missing.')
 
-        # borg check
-        check_epilog = process_epilog("""
-        The check command verifies the consistency of a repository and the corresponding archives.
-
-        First, the underlying repository data files are checked:
-
-        - For all segments the segment magic (header) is checked
-        - For all objects stored in the segments, all metadata (e.g. crc and size) and
-          all data is read. The read data is checked by size and CRC. Bit rot and other
-          types of accidental damage can be detected this way.
-        - If we are in repair mode and a integrity error is detected for a segment,
-          we try to recover as many objects from the segment as possible.
-        - In repair mode, it makes sure that the index is consistent with the data
-          stored in the segments.
-        - If you use a remote repo server via ssh:, the repo check is executed on the
-          repo server without causing significant network traffic.
-        - The repository check can be skipped using the ``--archives-only`` option.
-
-        Second, the consistency and correctness of the archive metadata is verified:
-
-        - Is the repo manifest present? If not, it is rebuilt from archive metadata
-          chunks (this requires reading and decrypting of all metadata and data).
-        - Check if archive metadata chunk is present. if not, remove archive from
-          manifest.
-        - For all files (items) in the archive, for all chunks referenced by these
-          files, check if chunk is present.
-          If a chunk is not present and we are in repair mode, replace it with a same-size
-          replacement chunk of zeros.
-          If a previously lost chunk reappears (e.g. via a later backup) and we are in
-          repair mode, the all-zero replacement chunk will be replaced by the correct chunk.
-          This requires reading of archive and file metadata, but not data.
-        - If we are in repair mode and we checked all the archives: delete orphaned
-          chunks from the repo.
-        - if you use a remote repo server via ssh:, the archive check is executed on
-          the client machine (because if encryption is enabled, the checks will require
-          decryption and this is always done client-side, because key access will be
-          required).
-        - The archive checks can be time consuming, they can be skipped using the
-          ``--repository-only`` option.
-
-        The ``--verify-data`` option will perform a full integrity verification (as opposed to
-        checking the CRC32 of the segment) of data, which means reading the data from the
-        repository, decrypting and decompressing it. This is a cryptographic verification,
-        which will detect (accidental) corruption. For encrypted repositories it is
-        tamper-resistant as well, unless the attacker has access to the keys.
-
-        It is also very slow.
-        """)
-        subparser = subparsers.add_parser('check', parents=[common_parser], add_help=False,
-                                          description=self.do_check.__doc__,
-                                          epilog=check_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='verify repository')
-        subparser.set_defaults(func=self.do_check)
-        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
-                               type=location_validator(),
-                               help='repository or archive to check consistency of')
-        subparser.add_argument('--repository-only', dest='repo_only', action='store_true',
-                               help='only perform repository checks')
-        subparser.add_argument('--archives-only', dest='archives_only', action='store_true',
-                               help='only perform archives checks')
-        subparser.add_argument('--verify-data', dest='verify_data', action='store_true',
-                               help='perform cryptographic archive data integrity verification '
-                                    '(conflicts with ``--repository-only``)')
-        subparser.add_argument('--repair', dest='repair', action='store_true',
-                               help='attempt to repair any inconsistencies found')
-        subparser.add_argument('--save-space', dest='save_space', action='store_true',
-                               help='work slower, but using less space')
-        subparser.add_argument('--max-duration', metavar='SECONDS', dest='max_duration',
-                                   type=int, default=0,
-                                   help='do only a partial repo check for max. SECONDS seconds (Default: unlimited)')
-        define_archive_filters_group(subparser)
-
         # borg key
         subparser = subparsers.add_parser('key', parents=[mid_common_parser], add_help=False,
                                           description="Manage a keyfile or repokey of a repository",

From 74635f38d665841c8be8b75edcf96d9235d09c9f Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Sat, 30 Mar 2019 07:04:23 +0100
Subject: [PATCH 05/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=205a?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg compact & config.
---
 src/borg/archiver.py | 146 +++++++++++++++++++++----------------------
 1 file changed, 73 insertions(+), 73 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index d2cb44781..6631205f8 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2757,6 +2757,79 @@ class Archiver:
                                    help='do only a partial repo check for max. SECONDS seconds (Default: unlimited)')
         define_archive_filters_group(subparser)
 
+        # borg compact
+        compact_epilog = process_epilog("""
+        This command frees repository space by compacting segments.
+
+        Use this regularly to avoid running out of space - you do not need to use this
+        after each borg command though.
+
+        borg compact does not need a key, so it is possible to invoke it from the
+        client or also from the server.
+
+        Depending on the amount of segments that need compaction, it may take a while.
+
+        See :ref:`separate_compaction` in Additional Notes for more details.
+        """)
+        subparser = subparsers.add_parser('compact', parents=[common_parser], add_help=False,
+                                          description=self.do_compact.__doc__,
+                                          epilog=compact_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='compact segment files / free space in repo')
+        subparser.set_defaults(func=self.do_compact)
+        subparser.add_argument('location', metavar='REPOSITORY',
+                               type=location_validator(archive=False),
+                               help='repository to compact')
+        subparser.add_argument('--cleanup-commits', dest='cleanup_commits', action='store_true',
+                               help='cleanup commit-only 17-byte segment files')
+
+        # borg config
+        config_epilog = process_epilog("""
+        This command gets and sets options in a local repository or cache config file.
+        For security reasons, this command only works on local repositories.
+
+        To delete a config value entirely, use ``--delete``. To list the values
+        of the configuration file or the default values, use ``--list``.  To get and existing
+        key, pass only the key name. To set a key, pass both the key name and
+        the new value. Keys can be specified in the format "section.name" or
+        simply "name"; the section will default to "repository" and "cache" for
+        the repo and cache configs, respectively.
+
+
+        By default, borg config manipulates the repository config file. Using ``--cache``
+        edits the repository cache's config file instead.
+        """)
+        subparser = subparsers.add_parser('config', parents=[common_parser], add_help=False,
+                                          description=self.do_config.__doc__,
+                                          epilog=config_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='get and set configuration values')
+        subparser.set_defaults(func=self.do_config)
+        subparser.add_argument('-c', '--cache', dest='cache', action='store_true',
+                               help='get and set values from the repo cache')
+
+        group = subparser.add_mutually_exclusive_group()
+        group.add_argument('-d', '--delete', dest='delete', action='store_true',
+                               help='delete the key from the config file')
+        group.add_argument('-l', '--list', dest='list', action='store_true',
+                               help='list the configuration of the repo')
+
+        subparser.add_argument('location', metavar='REPOSITORY',
+                               type=location_validator(archive=False, proto='file'),
+                               help='repository to configure')
+        subparser.add_argument('name', metavar='NAME', nargs='?',
+                               help='name of config key')
+        subparser.add_argument('value', metavar='VALUE', nargs='?',
+                               help='new value for key')
+
+        subparser = subparsers.add_parser('help', parents=[common_parser], add_help=False,
+                                          description='Extra help')
+        subparser.add_argument('--epilog-only', dest='epilog_only', action='store_true')
+        subparser.add_argument('--usage-only', dest='usage_only', action='store_true')
+        subparser.set_defaults(func=functools.partial(self.do_help, parser, subparsers.choices))
+        subparser.add_argument('topic', metavar='TOPIC', type=str, nargs='?',
+                               help='additional help on TOPIC')
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3940,79 +4013,6 @@ class Archiver:
         subparser.add_argument('args', metavar='ARGS', nargs=argparse.REMAINDER,
                                help='command arguments')
 
-        # borg compact
-        compact_epilog = process_epilog("""
-        This command frees repository space by compacting segments.
-
-        Use this regularly to avoid running out of space - you do not need to use this
-        after each borg command though.
-
-        borg compact does not need a key, so it is possible to invoke it from the
-        client or also from the server.
-
-        Depending on the amount of segments that need compaction, it may take a while.
-
-        See :ref:`separate_compaction` in Additional Notes for more details.
-        """)
-        subparser = subparsers.add_parser('compact', parents=[common_parser], add_help=False,
-                                          description=self.do_compact.__doc__,
-                                          epilog=compact_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='compact segment files / free space in repo')
-        subparser.set_defaults(func=self.do_compact)
-        subparser.add_argument('location', metavar='REPOSITORY',
-                               type=location_validator(archive=False),
-                               help='repository to compact')
-        subparser.add_argument('--cleanup-commits', dest='cleanup_commits', action='store_true',
-                               help='cleanup commit-only 17-byte segment files')
-
-        # borg config
-        config_epilog = process_epilog("""
-        This command gets and sets options in a local repository or cache config file.
-        For security reasons, this command only works on local repositories.
-
-        To delete a config value entirely, use ``--delete``. To list the values
-        of the configuration file or the default values, use ``--list``.  To get and existing
-        key, pass only the key name. To set a key, pass both the key name and
-        the new value. Keys can be specified in the format "section.name" or
-        simply "name"; the section will default to "repository" and "cache" for
-        the repo and cache configs, respectively.
-
-
-        By default, borg config manipulates the repository config file. Using ``--cache``
-        edits the repository cache's config file instead.
-        """)
-        subparser = subparsers.add_parser('config', parents=[common_parser], add_help=False,
-                                          description=self.do_config.__doc__,
-                                          epilog=config_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='get and set configuration values')
-        subparser.set_defaults(func=self.do_config)
-        subparser.add_argument('-c', '--cache', dest='cache', action='store_true',
-                               help='get and set values from the repo cache')
-
-        group = subparser.add_mutually_exclusive_group()
-        group.add_argument('-d', '--delete', dest='delete', action='store_true',
-                               help='delete the key from the config file')
-        group.add_argument('-l', '--list', dest='list', action='store_true',
-                               help='list the configuration of the repo')
-
-        subparser.add_argument('location', metavar='REPOSITORY',
-                               type=location_validator(archive=False, proto='file'),
-                               help='repository to configure')
-        subparser.add_argument('name', metavar='NAME', nargs='?',
-                               help='name of config key')
-        subparser.add_argument('value', metavar='VALUE', nargs='?',
-                               help='new value for key')
-
-        subparser = subparsers.add_parser('help', parents=[common_parser], add_help=False,
-                                          description='Extra help')
-        subparser.add_argument('--epilog-only', dest='epilog_only', action='store_true')
-        subparser.add_argument('--usage-only', dest='usage_only', action='store_true')
-        subparser.set_defaults(func=functools.partial(self.do_help, parser, subparsers.choices))
-        subparser.add_argument('topic', metavar='TOPIC', type=str, nargs='?',
-                               help='additional help on TOPIC')
-
         # borg debug
         debug_epilog = process_epilog("""
         These commands are not intended for normal use and potentially very

From e6b43ee5e9e0bf6f9e6694526a57104b97acb8e7 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Sat, 30 Mar 2019 07:11:28 +0100
Subject: [PATCH 06/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=205b?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg create.
---
 src/borg/archiver.py | 406 +++++++++++++++++++++----------------------
 1 file changed, 203 insertions(+), 203 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 6631205f8..a8e5acc2a 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2830,6 +2830,209 @@ class Archiver:
         subparser.add_argument('topic', metavar='TOPIC', type=str, nargs='?',
                                help='additional help on TOPIC')
 
+        # borg create
+        create_epilog = process_epilog("""
+        This command creates a backup archive containing all files found while recursively
+        traversing all paths specified. Paths are added to the archive as they are given,
+        that means if relative paths are desired, the command has to be run from the correct
+        directory.
+
+        When giving '-' as path, borg will read data from standard input and create a
+        file 'stdin' in the created archive from that data.
+
+        The archive will consume almost no disk space for files or parts of files that
+        have already been stored in other archives.
+
+        The archive name needs to be unique. It must not end in '.checkpoint' or
+        '.checkpoint.N' (with N being a number), because these names are used for
+        checkpoints and treated in special ways.
+
+        In the archive name, you may use the following placeholders:
+        {now}, {utcnow}, {fqdn}, {hostname}, {user} and some others.
+
+        Backup speed is increased by not reprocessing files that are already part of
+        existing archives and weren't modified. The detection of unmodified files is
+        done by comparing multiple file metadata values with previous values kept in
+        the files cache.
+
+        This comparison can operate in different modes as given by ``--files-cache``:
+
+        - ctime,size,inode (default)
+        - mtime,size,inode (default behaviour of borg versions older than 1.1.0rc4)
+        - ctime,size (ignore the inode number)
+        - mtime,size (ignore the inode number)
+        - rechunk,ctime (all files are considered modified - rechunk, cache ctime)
+        - rechunk,mtime (all files are considered modified - rechunk, cache mtime)
+        - disabled (disable the files cache, all files considered modified - rechunk)
+
+        inode number: better safety, but often unstable on network filesystems
+
+        Normally, detecting file modifications will take inode information into
+        consideration to improve the reliability of file change detection.
+        This is problematic for files located on sshfs and similar network file
+        systems which do not provide stable inode numbers, such files will always
+        be considered modified. You can use modes without `inode` in this case to
+        improve performance, but reliability of change detection might be reduced.
+
+        ctime vs. mtime: safety vs. speed
+
+        - ctime is a rather safe way to detect changes to a file (metadata and contents)
+          as it can not be set from userspace. But, a metadata-only change will already
+          update the ctime, so there might be some unnecessary chunking/hashing even
+          without content changes. Some filesystems do not support ctime (change time).
+        - mtime usually works and only updates if file contents were changed. But mtime
+          can be arbitrarily set from userspace, e.g. to set mtime back to the same value
+          it had before a content change happened. This can be used maliciously as well as
+          well-meant, but in both cases mtime based cache modes can be problematic.
+
+        The mount points of filesystems or filesystem snapshots should be the same for every
+        creation of a new archive to ensure fast operation. This is because the file cache that
+        is used to determine changed files quickly uses absolute filenames.
+        If this is not possible, consider creating a bind mount to a stable location.
+
+        The ``--progress`` option shows (from left to right) Original, Compressed and Deduplicated
+        (O, C and D, respectively), then the Number of files (N) processed so far, followed by
+        the currently processed path.
+
+        When using ``--stats``, you will get some statistics about how much data was
+        added - the "This Archive" deduplicated size there is most interesting as that is
+        how much your repository will grow. Please note that the "All archives" stats refer to
+        the state after creation. Also, the ``--stats`` and ``--dry-run`` options are mutually
+        exclusive because the data is not actually compressed and deduplicated during a dry run.
+
+        See the output of the "borg help patterns" command for more help on exclude patterns.
+        See the output of the "borg help placeholders" command for more help on placeholders.
+
+        .. man NOTES
+
+        The ``--exclude`` patterns are not like tar. In tar ``--exclude`` .bundler/gems will
+        exclude foo/.bundler/gems. In borg it will not, you need to use ``--exclude``
+        '\\*/.bundler/gems' to get the same effect. See ``borg help patterns`` for
+        more information.
+
+        In addition to using ``--exclude`` patterns, it is possible to use
+        ``--exclude-if-present`` to specify the name of a filesystem object (e.g. a file
+        or folder name) which, when contained within another folder, will prevent the
+        containing folder from being backed up.  By default, the containing folder and
+        all of its contents will be omitted from the backup.  If, however, you wish to
+        only include the objects specified by ``--exclude-if-present`` in your backup,
+        and not include any other contents of the containing folder, this can be enabled
+        through using the ``--keep-exclude-tags`` option.
+
+        Item flags
+        ++++++++++
+
+        ``--list`` outputs a list of all files, directories and other
+        file system items it considered (no matter whether they had content changes
+        or not). For each item, it prefixes a single-letter flag that indicates type
+        and/or status of the item.
+
+        If you are interested only in a subset of that output, you can give e.g.
+        ``--filter=AME`` and it will only show regular files with A, M or E status (see
+        below).
+
+        A uppercase character represents the status of a regular file relative to the
+        "files" cache (not relative to the repo -- this is an issue if the files cache
+        is not used). Metadata is stored in any case and for 'A' and 'M' also new data
+        chunks are stored. For 'U' all data chunks refer to already existing chunks.
+
+        - 'A' = regular file, added (see also :ref:`a_status_oddity` in the FAQ)
+        - 'M' = regular file, modified
+        - 'U' = regular file, unchanged
+        - 'C' = regular file, it changed while we backed it up
+        - 'E' = regular file, an error happened while accessing/reading *this* file
+
+        A lowercase character means a file type other than a regular file,
+        borg usually just stores their metadata:
+
+        - 'd' = directory
+        - 'b' = block device
+        - 'c' = char device
+        - 'h' = regular file, hardlink (to already seen inodes)
+        - 's' = symlink
+        - 'f' = fifo
+
+        Other flags used include:
+
+        - 'i' = backup data was read from standard input (stdin)
+        - '-' = dry run, item was *not* backed up
+        - 'x' = excluded, item was *not* backed up
+        - '?' = missing status code (if you see this, please file a bug report!)
+        """)
+
+        subparser = subparsers.add_parser('create', parents=[common_parser], add_help=False,
+                                          description=self.do_create.__doc__,
+                                          epilog=create_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='create backup')
+        subparser.set_defaults(func=self.do_create)
+
+        dryrun_group = subparser.add_mutually_exclusive_group()
+        dryrun_group.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
+                               help='do not create a backup archive')
+        dryrun_group.add_argument('-s', '--stats', dest='stats', action='store_true',
+                               help='print statistics for the created archive')
+
+        subparser.add_argument('--list', dest='output_list', action='store_true',
+                               help='output verbose list of items (files, dirs, ...)')
+        subparser.add_argument('--filter', metavar='STATUSCHARS', dest='output_filter',
+                               help='only display items with the given status characters (see description)')
+        subparser.add_argument('--json', action='store_true',
+                               help='output stats as JSON. Implies ``--stats``.')
+        subparser.add_argument('--no-cache-sync', dest='no_cache_sync', action='store_true',
+                               help='experimental: do not synchronize the cache. Implies not using the files cache.')
+        subparser.add_argument('--stdin-name', metavar='NAME', dest='stdin_name', default='stdin',
+                               help='use NAME in archive for stdin data (default: "stdin")')
+
+        exclude_group = define_exclusion_group(subparser, tag_files=True)
+        exclude_group.add_argument('--exclude-nodump', dest='exclude_nodump', action='store_true',
+                                   help='exclude files flagged NODUMP')
+
+        fs_group = subparser.add_argument_group('Filesystem options')
+        fs_group.add_argument('-x', '--one-file-system', dest='one_file_system', action='store_true',
+                              help='stay in the same file system and do not store mount points of other file systems')
+        fs_group.add_argument('--numeric-owner', dest='numeric_owner', action='store_true',
+                              help='only store numeric user and group identifiers')
+        fs_group.add_argument('--noatime', dest='noatime', action='store_true',
+                              help='do not store atime into archive')
+        fs_group.add_argument('--noctime', dest='noctime', action='store_true',
+                              help='do not store ctime into archive')
+        fs_group.add_argument('--nobirthtime', dest='nobirthtime', action='store_true',
+                              help='do not store birthtime (creation date) into archive')
+        fs_group.add_argument('--nobsdflags', dest='nobsdflags', action='store_true',
+                              help='do not read and store bsdflags (e.g. NODUMP, IMMUTABLE) into archive')
+        fs_group.add_argument('--files-cache', metavar='MODE', dest='files_cache_mode',
+                              type=FilesCacheMode, default=DEFAULT_FILES_CACHE_MODE_UI,
+                              help='operate files cache in MODE. default: %s' % DEFAULT_FILES_CACHE_MODE_UI)
+        fs_group.add_argument('--read-special', dest='read_special', action='store_true',
+                              help='open and read block and char device files as well as FIFOs as if they were '
+                                   'regular files. Also follows symlinks pointing to these kinds of files.')
+
+        archive_group = subparser.add_argument_group('Archive options')
+        archive_group.add_argument('--comment', dest='comment', metavar='COMMENT', default='',
+                                   help='add a comment text to the archive')
+        archive_group.add_argument('--timestamp', metavar='TIMESTAMP', dest='timestamp',
+                                   type=timestamp, default=None,
+                                   help='manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). '
+                                        'Alternatively, give a reference file/directory.')
+        archive_group.add_argument('-c', '--checkpoint-interval', metavar='SECONDS', dest='checkpoint_interval',
+                                   type=int, default=1800,
+                                   help='write checkpoint every SECONDS seconds (Default: 1800)')
+        archive_group.add_argument('--chunker-params', metavar='PARAMS', dest='chunker_params',
+                                   type=ChunkerParams, default=CHUNKER_PARAMS,
+                                   help='specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, '
+                                        'HASH_MASK_BITS, HASH_WINDOW_SIZE). default: %s,%d,%d,%d,%d' % CHUNKER_PARAMS)
+        archive_group.add_argument('-C', '--compression', metavar='COMPRESSION', dest='compression',
+                                   type=CompressionSpec, default=CompressionSpec('lz4'),
+                                   help='select compression algorithm, see the output of the '
+                                        '"borg help compression" command for details.')
+
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='name of archive to create (must be also a valid directory name)')
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+                               help='paths to archive')
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3170,209 +3373,6 @@ class Archiver:
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False))
 
-        # borg create
-        create_epilog = process_epilog("""
-        This command creates a backup archive containing all files found while recursively
-        traversing all paths specified. Paths are added to the archive as they are given,
-        that means if relative paths are desired, the command has to be run from the correct
-        directory.
-
-        When giving '-' as path, borg will read data from standard input and create a
-        file 'stdin' in the created archive from that data.
-
-        The archive will consume almost no disk space for files or parts of files that
-        have already been stored in other archives.
-
-        The archive name needs to be unique. It must not end in '.checkpoint' or
-        '.checkpoint.N' (with N being a number), because these names are used for
-        checkpoints and treated in special ways.
-
-        In the archive name, you may use the following placeholders:
-        {now}, {utcnow}, {fqdn}, {hostname}, {user} and some others.
-
-        Backup speed is increased by not reprocessing files that are already part of
-        existing archives and weren't modified. The detection of unmodified files is
-        done by comparing multiple file metadata values with previous values kept in
-        the files cache.
-
-        This comparison can operate in different modes as given by ``--files-cache``:
-
-        - ctime,size,inode (default)
-        - mtime,size,inode (default behaviour of borg versions older than 1.1.0rc4)
-        - ctime,size (ignore the inode number)
-        - mtime,size (ignore the inode number)
-        - rechunk,ctime (all files are considered modified - rechunk, cache ctime)
-        - rechunk,mtime (all files are considered modified - rechunk, cache mtime)
-        - disabled (disable the files cache, all files considered modified - rechunk)
-
-        inode number: better safety, but often unstable on network filesystems
-
-        Normally, detecting file modifications will take inode information into
-        consideration to improve the reliability of file change detection.
-        This is problematic for files located on sshfs and similar network file
-        systems which do not provide stable inode numbers, such files will always
-        be considered modified. You can use modes without `inode` in this case to
-        improve performance, but reliability of change detection might be reduced.
-
-        ctime vs. mtime: safety vs. speed
-
-        - ctime is a rather safe way to detect changes to a file (metadata and contents)
-          as it can not be set from userspace. But, a metadata-only change will already
-          update the ctime, so there might be some unnecessary chunking/hashing even
-          without content changes. Some filesystems do not support ctime (change time).
-        - mtime usually works and only updates if file contents were changed. But mtime
-          can be arbitrarily set from userspace, e.g. to set mtime back to the same value
-          it had before a content change happened. This can be used maliciously as well as
-          well-meant, but in both cases mtime based cache modes can be problematic.
-
-        The mount points of filesystems or filesystem snapshots should be the same for every
-        creation of a new archive to ensure fast operation. This is because the file cache that
-        is used to determine changed files quickly uses absolute filenames.
-        If this is not possible, consider creating a bind mount to a stable location.
-
-        The ``--progress`` option shows (from left to right) Original, Compressed and Deduplicated
-        (O, C and D, respectively), then the Number of files (N) processed so far, followed by
-        the currently processed path.
-
-        When using ``--stats``, you will get some statistics about how much data was
-        added - the "This Archive" deduplicated size there is most interesting as that is
-        how much your repository will grow. Please note that the "All archives" stats refer to
-        the state after creation. Also, the ``--stats`` and ``--dry-run`` options are mutually
-        exclusive because the data is not actually compressed and deduplicated during a dry run.
-
-        See the output of the "borg help patterns" command for more help on exclude patterns.
-        See the output of the "borg help placeholders" command for more help on placeholders.
-
-        .. man NOTES
-
-        The ``--exclude`` patterns are not like tar. In tar ``--exclude`` .bundler/gems will
-        exclude foo/.bundler/gems. In borg it will not, you need to use ``--exclude``
-        '\\*/.bundler/gems' to get the same effect. See ``borg help patterns`` for
-        more information.
-
-        In addition to using ``--exclude`` patterns, it is possible to use
-        ``--exclude-if-present`` to specify the name of a filesystem object (e.g. a file
-        or folder name) which, when contained within another folder, will prevent the
-        containing folder from being backed up.  By default, the containing folder and
-        all of its contents will be omitted from the backup.  If, however, you wish to
-        only include the objects specified by ``--exclude-if-present`` in your backup,
-        and not include any other contents of the containing folder, this can be enabled
-        through using the ``--keep-exclude-tags`` option.
-
-        Item flags
-        ++++++++++
-
-        ``--list`` outputs a list of all files, directories and other
-        file system items it considered (no matter whether they had content changes
-        or not). For each item, it prefixes a single-letter flag that indicates type
-        and/or status of the item.
-
-        If you are interested only in a subset of that output, you can give e.g.
-        ``--filter=AME`` and it will only show regular files with A, M or E status (see
-        below).
-
-        A uppercase character represents the status of a regular file relative to the
-        "files" cache (not relative to the repo -- this is an issue if the files cache
-        is not used). Metadata is stored in any case and for 'A' and 'M' also new data
-        chunks are stored. For 'U' all data chunks refer to already existing chunks.
-
-        - 'A' = regular file, added (see also :ref:`a_status_oddity` in the FAQ)
-        - 'M' = regular file, modified
-        - 'U' = regular file, unchanged
-        - 'C' = regular file, it changed while we backed it up
-        - 'E' = regular file, an error happened while accessing/reading *this* file
-
-        A lowercase character means a file type other than a regular file,
-        borg usually just stores their metadata:
-
-        - 'd' = directory
-        - 'b' = block device
-        - 'c' = char device
-        - 'h' = regular file, hardlink (to already seen inodes)
-        - 's' = symlink
-        - 'f' = fifo
-
-        Other flags used include:
-
-        - 'i' = backup data was read from standard input (stdin)
-        - '-' = dry run, item was *not* backed up
-        - 'x' = excluded, item was *not* backed up
-        - '?' = missing status code (if you see this, please file a bug report!)
-        """)
-
-        subparser = subparsers.add_parser('create', parents=[common_parser], add_help=False,
-                                          description=self.do_create.__doc__,
-                                          epilog=create_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='create backup')
-        subparser.set_defaults(func=self.do_create)
-
-        dryrun_group = subparser.add_mutually_exclusive_group()
-        dryrun_group.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
-                               help='do not create a backup archive')
-        dryrun_group.add_argument('-s', '--stats', dest='stats', action='store_true',
-                               help='print statistics for the created archive')
-
-        subparser.add_argument('--list', dest='output_list', action='store_true',
-                               help='output verbose list of items (files, dirs, ...)')
-        subparser.add_argument('--filter', metavar='STATUSCHARS', dest='output_filter',
-                               help='only display items with the given status characters (see description)')
-        subparser.add_argument('--json', action='store_true',
-                               help='output stats as JSON. Implies ``--stats``.')
-        subparser.add_argument('--no-cache-sync', dest='no_cache_sync', action='store_true',
-                               help='experimental: do not synchronize the cache. Implies not using the files cache.')
-        subparser.add_argument('--stdin-name', metavar='NAME', dest='stdin_name', default='stdin',
-                               help='use NAME in archive for stdin data (default: "stdin")')
-
-        exclude_group = define_exclusion_group(subparser, tag_files=True)
-        exclude_group.add_argument('--exclude-nodump', dest='exclude_nodump', action='store_true',
-                                   help='exclude files flagged NODUMP')
-
-        fs_group = subparser.add_argument_group('Filesystem options')
-        fs_group.add_argument('-x', '--one-file-system', dest='one_file_system', action='store_true',
-                              help='stay in the same file system and do not store mount points of other file systems')
-        fs_group.add_argument('--numeric-owner', dest='numeric_owner', action='store_true',
-                              help='only store numeric user and group identifiers')
-        fs_group.add_argument('--noatime', dest='noatime', action='store_true',
-                              help='do not store atime into archive')
-        fs_group.add_argument('--noctime', dest='noctime', action='store_true',
-                              help='do not store ctime into archive')
-        fs_group.add_argument('--nobirthtime', dest='nobirthtime', action='store_true',
-                              help='do not store birthtime (creation date) into archive')
-        fs_group.add_argument('--nobsdflags', dest='nobsdflags', action='store_true',
-                              help='do not read and store bsdflags (e.g. NODUMP, IMMUTABLE) into archive')
-        fs_group.add_argument('--files-cache', metavar='MODE', dest='files_cache_mode',
-                              type=FilesCacheMode, default=DEFAULT_FILES_CACHE_MODE_UI,
-                              help='operate files cache in MODE. default: %s' % DEFAULT_FILES_CACHE_MODE_UI)
-        fs_group.add_argument('--read-special', dest='read_special', action='store_true',
-                              help='open and read block and char device files as well as FIFOs as if they were '
-                                   'regular files. Also follows symlinks pointing to these kinds of files.')
-
-        archive_group = subparser.add_argument_group('Archive options')
-        archive_group.add_argument('--comment', dest='comment', metavar='COMMENT', default='',
-                                   help='add a comment text to the archive')
-        archive_group.add_argument('--timestamp', metavar='TIMESTAMP', dest='timestamp',
-                                   type=timestamp, default=None,
-                                   help='manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). '
-                                        'Alternatively, give a reference file/directory.')
-        archive_group.add_argument('-c', '--checkpoint-interval', metavar='SECONDS', dest='checkpoint_interval',
-                                   type=int, default=1800,
-                                   help='write checkpoint every SECONDS seconds (Default: 1800)')
-        archive_group.add_argument('--chunker-params', metavar='PARAMS', dest='chunker_params',
-                                   type=ChunkerParams, default=CHUNKER_PARAMS,
-                                   help='specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, '
-                                        'HASH_MASK_BITS, HASH_WINDOW_SIZE). default: %s,%d,%d,%d,%d' % CHUNKER_PARAMS)
-        archive_group.add_argument('-C', '--compression', metavar='COMPRESSION', dest='compression',
-                                   type=CompressionSpec, default=CompressionSpec('lz4'),
-                                   help='select compression algorithm, see the output of the '
-                                        '"borg help compression" command for details.')
-
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='name of archive to create (must be also a valid directory name)')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='paths to archive')
-
         # borg extract
         extract_epilog = process_epilog("""
         This command extracts the contents of an archive. By default the entire

From 28172553db4fcfbd6c23292b86fc4f61e64bb466 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 18:59:24 +0100
Subject: [PATCH 07/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=206?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg debug & delete.
---
 src/borg/archiver.py | 460 +++++++++++++++++++++----------------------
 1 file changed, 230 insertions(+), 230 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index a8e5acc2a..0b242d959 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3033,6 +3033,236 @@ class Archiver:
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
                                help='paths to archive')
 
+        # borg debug
+        debug_epilog = process_epilog("""
+        These commands are not intended for normal use and potentially very
+        dangerous if used incorrectly.
+
+        They exist to improve debugging capabilities without direct system access, e.g.
+        in case you ever run into some severe malfunction. Use them only if you know
+        what you are doing or if a trusted developer tells you what to do.""")
+
+        subparser = subparsers.add_parser('debug', parents=[mid_common_parser], add_help=False,
+                                          description='debugging command (not intended for normal use)',
+                                          epilog=debug_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='debugging command (not intended for normal use)')
+
+        debug_parsers = subparser.add_subparsers(title='required arguments', metavar='<command>')
+        subparser.set_defaults(fallback_func=functools.partial(self.do_subcommand_help, subparser))
+
+        debug_info_epilog = process_epilog("""
+        This command displays some system information that might be useful for bug
+        reports and debugging problems. If a traceback happens, this information is
+        already appended at the end of the traceback.
+        """)
+        subparser = debug_parsers.add_parser('info', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_info.__doc__,
+                                          epilog=debug_info_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='show system infos for debugging / bug reports (debug)')
+        subparser.set_defaults(func=self.do_debug_info)
+
+        debug_dump_archive_items_epilog = process_epilog("""
+        This command dumps raw (but decrypted and decompressed) archive items (only metadata) to files.
+        """)
+        subparser = debug_parsers.add_parser('dump-archive-items', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_dump_archive_items.__doc__,
+                                          epilog=debug_dump_archive_items_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='dump archive items (metadata) (debug)')
+        subparser.set_defaults(func=self.do_debug_dump_archive_items)
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to dump')
+
+        debug_dump_archive_epilog = process_epilog("""
+        This command dumps all metadata of an archive in a decoded form to a file.
+        """)
+        subparser = debug_parsers.add_parser('dump-archive', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_dump_archive.__doc__,
+                                          epilog=debug_dump_archive_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='dump decoded archive metadata (debug)')
+        subparser.set_defaults(func=self.do_debug_dump_archive)
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to dump')
+        subparser.add_argument('path', metavar='PATH', type=str,
+                               help='file to dump data into')
+
+        debug_dump_manifest_epilog = process_epilog("""
+        This command dumps manifest metadata of a repository in a decoded form to a file.
+        """)
+        subparser = debug_parsers.add_parser('dump-manifest', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_dump_manifest.__doc__,
+                                          epilog=debug_dump_manifest_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='dump decoded repository metadata (debug)')
+        subparser.set_defaults(func=self.do_debug_dump_manifest)
+        subparser.add_argument('location', metavar='REPOSITORY',
+                               type=location_validator(archive=False),
+                               help='repository to dump')
+        subparser.add_argument('path', metavar='PATH', type=str,
+                               help='file to dump data into')
+
+        debug_dump_repo_objs_epilog = process_epilog("""
+        This command dumps raw (but decrypted and decompressed) repo objects to files.
+        """)
+        subparser = debug_parsers.add_parser('dump-repo-objs', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_dump_repo_objs.__doc__,
+                                          epilog=debug_dump_repo_objs_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='dump repo objects (debug)')
+        subparser.set_defaults(func=self.do_debug_dump_repo_objs)
+        subparser.add_argument('location', metavar='REPOSITORY',
+                               type=location_validator(archive=False),
+                               help='repo to dump')
+        subparser.add_argument('--ghost', dest='ghost', action='store_true',
+                               help='dump all segment file contents, including deleted/uncommitted objects and commits.')
+
+        debug_search_repo_objs_epilog = process_epilog("""
+        This command searches raw (but decrypted and decompressed) repo objects for a specific bytes sequence.
+        """)
+        subparser = debug_parsers.add_parser('search-repo-objs', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_search_repo_objs.__doc__,
+                                          epilog=debug_search_repo_objs_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='search repo objects (debug)')
+        subparser.set_defaults(func=self.do_debug_search_repo_objs)
+        subparser.add_argument('location', metavar='REPOSITORY',
+                               type=location_validator(archive=False),
+                               help='repo to search')
+        subparser.add_argument('wanted', metavar='WANTED', type=str,
+                               help='term to search the repo for, either 0x1234abcd hex term or a string')
+
+        debug_get_obj_epilog = process_epilog("""
+        This command gets an object from the repository.
+        """)
+        subparser = debug_parsers.add_parser('get-obj', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_get_obj.__doc__,
+                                          epilog=debug_get_obj_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='get object from repository (debug)')
+        subparser.set_defaults(func=self.do_debug_get_obj)
+        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
+                               type=location_validator(archive=False),
+                               help='repository to use')
+        subparser.add_argument('id', metavar='ID', type=str,
+                               help='hex object ID to get from the repo')
+        subparser.add_argument('path', metavar='PATH', type=str,
+                               help='file to write object data into')
+
+        debug_put_obj_epilog = process_epilog("""
+        This command puts objects into the repository.
+        """)
+        subparser = debug_parsers.add_parser('put-obj', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_put_obj.__doc__,
+                                          epilog=debug_put_obj_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='put object to repository (debug)')
+        subparser.set_defaults(func=self.do_debug_put_obj)
+        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
+                               type=location_validator(archive=False),
+                               help='repository to use')
+        subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
+                               help='file(s) to read and create object(s) from')
+
+        debug_delete_obj_epilog = process_epilog("""
+        This command deletes objects from the repository.
+        """)
+        subparser = debug_parsers.add_parser('delete-obj', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_delete_obj.__doc__,
+                                          epilog=debug_delete_obj_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='delete object from repository (debug)')
+        subparser.set_defaults(func=self.do_debug_delete_obj)
+        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
+                               type=location_validator(archive=False),
+                               help='repository to use')
+        subparser.add_argument('ids', metavar='IDs', nargs='+', type=str,
+                               help='hex object ID(s) to delete from the repo')
+
+        debug_refcount_obj_epilog = process_epilog("""
+        This command displays the reference count for objects from the repository.
+        """)
+        subparser = debug_parsers.add_parser('refcount-obj', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_refcount_obj.__doc__,
+                                          epilog=debug_refcount_obj_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='show refcount for object from repository (debug)')
+        subparser.set_defaults(func=self.do_debug_refcount_obj)
+        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
+                               type=location_validator(archive=False),
+                               help='repository to use')
+        subparser.add_argument('ids', metavar='IDs', nargs='+', type=str,
+                               help='hex object ID(s) to show refcounts for')
+
+        debug_convert_profile_epilog = process_epilog("""
+        Convert a Borg profile to a Python cProfile compatible profile.
+        """)
+        subparser = debug_parsers.add_parser('convert-profile', parents=[common_parser], add_help=False,
+                                          description=self.do_debug_convert_profile.__doc__,
+                                          epilog=debug_convert_profile_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='convert Borg profile to Python profile (debug)')
+        subparser.set_defaults(func=self.do_debug_convert_profile)
+        subparser.add_argument('input', metavar='INPUT', type=argparse.FileType('rb'),
+                               help='Borg profile')
+        subparser.add_argument('output', metavar='OUTPUT', type=argparse.FileType('wb'),
+                               help='Output file')
+
+        # borg delete
+        delete_epilog = process_epilog("""
+        This command deletes an archive from the repository or the complete repository.
+
+        Important: When deleting archives, repository disk space is **not** freed until
+        you run ``borg compact``.
+
+        If you delete the complete repository, the local cache for it (if any) is
+        also deleted. Alternatively, you can delete just the local cache with the
+        ``--cache-only`` option.
+
+        When using ``--stats``, you will get some statistics about how much data was
+        deleted - the "Deleted data" deduplicated size there is most interesting as
+        that is how much your repository will shrink.
+        Please note that the "All archives" stats refer to the state after deletion.
+
+        You can delete multiple archives by specifying their common prefix, if they
+        have one, using the ``--prefix PREFIX`` option. You can also specify a shell
+        pattern to match multiple archives using the ``--glob-archives GLOB`` option
+        (for more info on these patterns, see ``borg help patterns``). Note that these
+        two options are mutually exclusive.
+
+        To avoid accidentally deleting archives, especially when using glob patterns,
+        it might be helpful to use the ``--dry-run`` to test out the command without
+        actually making any changes to the repository.
+        """)
+        subparser = subparsers.add_parser('delete', parents=[common_parser], add_help=False,
+                                          description=self.do_delete.__doc__,
+                                          epilog=delete_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='delete archive')
+        subparser.set_defaults(func=self.do_delete)
+        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
+                               help='do not change repository')
+        subparser.add_argument('-s', '--stats', dest='stats', action='store_true',
+                               help='print statistics for the deleted archive')
+        subparser.add_argument('--cache-only', dest='cache_only', action='store_true',
+                               help='delete only the local cache for the given repository')
+        subparser.add_argument('--force', dest='forced',
+                               action='count', default=0,
+                               help='force deletion of corrupted archives, '
+                                    'use ``--force --force`` in case ``--force`` does not work.')
+        subparser.add_argument('--save-space', dest='save_space', action='store_true',
+                               help='work slower, but using less space')
+        subparser.add_argument('location', metavar='TARGET', nargs='?', default='',
+                               type=location_validator(),
+                               help='archive or repository to delete')
+        subparser.add_argument('archives', metavar='ARCHIVE', nargs='*',
+                               help='archives to delete')
+        define_archive_filters_group(subparser)
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3534,57 +3764,6 @@ class Archiver:
                                type=archivename_validator(),
                                help='the new archive name to use')
 
-        # borg delete
-        delete_epilog = process_epilog("""
-        This command deletes an archive from the repository or the complete repository.
-
-        Important: When deleting archives, repository disk space is **not** freed until
-        you run ``borg compact``.
-
-        If you delete the complete repository, the local cache for it (if any) is
-        also deleted. Alternatively, you can delete just the local cache with the
-        ``--cache-only`` option.
-
-        When using ``--stats``, you will get some statistics about how much data was
-        deleted - the "Deleted data" deduplicated size there is most interesting as
-        that is how much your repository will shrink.
-        Please note that the "All archives" stats refer to the state after deletion.
-
-        You can delete multiple archives by specifying their common prefix, if they
-        have one, using the ``--prefix PREFIX`` option. You can also specify a shell
-        pattern to match multiple archives using the ``--glob-archives GLOB`` option
-        (for more info on these patterns, see ``borg help patterns``). Note that these
-        two options are mutually exclusive.
-
-        To avoid accidentally deleting archives, especially when using glob patterns,
-        it might be helpful to use the ``--dry-run`` to test out the command without
-        actually making any changes to the repository.
-        """)
-        subparser = subparsers.add_parser('delete', parents=[common_parser], add_help=False,
-                                          description=self.do_delete.__doc__,
-                                          epilog=delete_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='delete archive')
-        subparser.set_defaults(func=self.do_delete)
-        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
-                               help='do not change repository')
-        subparser.add_argument('-s', '--stats', dest='stats', action='store_true',
-                               help='print statistics for the deleted archive')
-        subparser.add_argument('--cache-only', dest='cache_only', action='store_true',
-                               help='delete only the local cache for the given repository')
-        subparser.add_argument('--force', dest='forced',
-                               action='count', default=0,
-                               help='force deletion of corrupted archives, '
-                                    'use ``--force --force`` in case ``--force`` does not work.')
-        subparser.add_argument('--save-space', dest='save_space', action='store_true',
-                               help='work slower, but using less space')
-        subparser.add_argument('location', metavar='TARGET', nargs='?', default='',
-                               type=location_validator(),
-                               help='archive or repository to delete')
-        subparser.add_argument('archives', metavar='ARCHIVE', nargs='*',
-                               help='archives to delete')
-        define_archive_filters_group(subparser)
-
         # borg list
         list_epilog = process_epilog("""
         This command lists the contents of a repository or an archive.
@@ -4013,185 +4192,6 @@ class Archiver:
         subparser.add_argument('args', metavar='ARGS', nargs=argparse.REMAINDER,
                                help='command arguments')
 
-        # borg debug
-        debug_epilog = process_epilog("""
-        These commands are not intended for normal use and potentially very
-        dangerous if used incorrectly.
-
-        They exist to improve debugging capabilities without direct system access, e.g.
-        in case you ever run into some severe malfunction. Use them only if you know
-        what you are doing or if a trusted developer tells you what to do.""")
-
-        subparser = subparsers.add_parser('debug', parents=[mid_common_parser], add_help=False,
-                                          description='debugging command (not intended for normal use)',
-                                          epilog=debug_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='debugging command (not intended for normal use)')
-
-        debug_parsers = subparser.add_subparsers(title='required arguments', metavar='<command>')
-        subparser.set_defaults(fallback_func=functools.partial(self.do_subcommand_help, subparser))
-
-        debug_info_epilog = process_epilog("""
-        This command displays some system information that might be useful for bug
-        reports and debugging problems. If a traceback happens, this information is
-        already appended at the end of the traceback.
-        """)
-        subparser = debug_parsers.add_parser('info', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_info.__doc__,
-                                          epilog=debug_info_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='show system infos for debugging / bug reports (debug)')
-        subparser.set_defaults(func=self.do_debug_info)
-
-        debug_dump_archive_items_epilog = process_epilog("""
-        This command dumps raw (but decrypted and decompressed) archive items (only metadata) to files.
-        """)
-        subparser = debug_parsers.add_parser('dump-archive-items', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_dump_archive_items.__doc__,
-                                          epilog=debug_dump_archive_items_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='dump archive items (metadata) (debug)')
-        subparser.set_defaults(func=self.do_debug_dump_archive_items)
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to dump')
-
-        debug_dump_archive_epilog = process_epilog("""
-        This command dumps all metadata of an archive in a decoded form to a file.
-        """)
-        subparser = debug_parsers.add_parser('dump-archive', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_dump_archive.__doc__,
-                                          epilog=debug_dump_archive_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='dump decoded archive metadata (debug)')
-        subparser.set_defaults(func=self.do_debug_dump_archive)
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to dump')
-        subparser.add_argument('path', metavar='PATH', type=str,
-                               help='file to dump data into')
-
-        debug_dump_manifest_epilog = process_epilog("""
-        This command dumps manifest metadata of a repository in a decoded form to a file.
-        """)
-        subparser = debug_parsers.add_parser('dump-manifest', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_dump_manifest.__doc__,
-                                          epilog=debug_dump_manifest_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='dump decoded repository metadata (debug)')
-        subparser.set_defaults(func=self.do_debug_dump_manifest)
-        subparser.add_argument('location', metavar='REPOSITORY',
-                               type=location_validator(archive=False),
-                               help='repository to dump')
-        subparser.add_argument('path', metavar='PATH', type=str,
-                               help='file to dump data into')
-
-        debug_dump_repo_objs_epilog = process_epilog("""
-        This command dumps raw (but decrypted and decompressed) repo objects to files.
-        """)
-        subparser = debug_parsers.add_parser('dump-repo-objs', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_dump_repo_objs.__doc__,
-                                          epilog=debug_dump_repo_objs_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='dump repo objects (debug)')
-        subparser.set_defaults(func=self.do_debug_dump_repo_objs)
-        subparser.add_argument('location', metavar='REPOSITORY',
-                               type=location_validator(archive=False),
-                               help='repo to dump')
-        subparser.add_argument('--ghost', dest='ghost', action='store_true',
-                               help='dump all segment file contents, including deleted/uncommitted objects and commits.')
-
-        debug_search_repo_objs_epilog = process_epilog("""
-        This command searches raw (but decrypted and decompressed) repo objects for a specific bytes sequence.
-        """)
-        subparser = debug_parsers.add_parser('search-repo-objs', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_search_repo_objs.__doc__,
-                                          epilog=debug_search_repo_objs_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='search repo objects (debug)')
-        subparser.set_defaults(func=self.do_debug_search_repo_objs)
-        subparser.add_argument('location', metavar='REPOSITORY',
-                               type=location_validator(archive=False),
-                               help='repo to search')
-        subparser.add_argument('wanted', metavar='WANTED', type=str,
-                               help='term to search the repo for, either 0x1234abcd hex term or a string')
-
-        debug_get_obj_epilog = process_epilog("""
-        This command gets an object from the repository.
-        """)
-        subparser = debug_parsers.add_parser('get-obj', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_get_obj.__doc__,
-                                          epilog=debug_get_obj_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='get object from repository (debug)')
-        subparser.set_defaults(func=self.do_debug_get_obj)
-        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
-                               type=location_validator(archive=False),
-                               help='repository to use')
-        subparser.add_argument('id', metavar='ID', type=str,
-                               help='hex object ID to get from the repo')
-        subparser.add_argument('path', metavar='PATH', type=str,
-                               help='file to write object data into')
-
-        debug_put_obj_epilog = process_epilog("""
-        This command puts objects into the repository.
-        """)
-        subparser = debug_parsers.add_parser('put-obj', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_put_obj.__doc__,
-                                          epilog=debug_put_obj_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='put object to repository (debug)')
-        subparser.set_defaults(func=self.do_debug_put_obj)
-        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
-                               type=location_validator(archive=False),
-                               help='repository to use')
-        subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
-                               help='file(s) to read and create object(s) from')
-
-        debug_delete_obj_epilog = process_epilog("""
-        This command deletes objects from the repository.
-        """)
-        subparser = debug_parsers.add_parser('delete-obj', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_delete_obj.__doc__,
-                                          epilog=debug_delete_obj_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='delete object from repository (debug)')
-        subparser.set_defaults(func=self.do_debug_delete_obj)
-        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
-                               type=location_validator(archive=False),
-                               help='repository to use')
-        subparser.add_argument('ids', metavar='IDs', nargs='+', type=str,
-                               help='hex object ID(s) to delete from the repo')
-
-        debug_refcount_obj_epilog = process_epilog("""
-        This command displays the reference count for objects from the repository.
-        """)
-        subparser = debug_parsers.add_parser('refcount-obj', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_refcount_obj.__doc__,
-                                          epilog=debug_refcount_obj_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='show refcount for object from repository (debug)')
-        subparser.set_defaults(func=self.do_debug_refcount_obj)
-        subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
-                               type=location_validator(archive=False),
-                               help='repository to use')
-        subparser.add_argument('ids', metavar='IDs', nargs='+', type=str,
-                               help='hex object ID(s) to show refcounts for')
-
-        debug_convert_profile_epilog = process_epilog("""
-        Convert a Borg profile to a Python cProfile compatible profile.
-        """)
-        subparser = debug_parsers.add_parser('convert-profile', parents=[common_parser], add_help=False,
-                                          description=self.do_debug_convert_profile.__doc__,
-                                          epilog=debug_convert_profile_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='convert Borg profile to Python profile (debug)')
-        subparser.set_defaults(func=self.do_debug_convert_profile)
-        subparser.add_argument('input', metavar='INPUT', type=argparse.FileType('rb'),
-                               help='Borg profile')
-        subparser.add_argument('output', metavar='OUTPUT', type=argparse.FileType('wb'),
-                               help='Output file')
-
         return parser
 
     def get_args(self, argv, cmd):

From 76380e9ccb5a0316ea7da4494dee811bfadb84e3 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:07:07 +0100
Subject: [PATCH 08/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=207?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg diff.
---
 src/borg/archiver.py | 82 ++++++++++++++++++++++----------------------
 1 file changed, 41 insertions(+), 41 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 0b242d959..b61e43796 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3263,6 +3263,47 @@ class Archiver:
                                help='archives to delete')
         define_archive_filters_group(subparser)
 
+        # borg diff
+        diff_epilog = process_epilog("""
+            This command finds differences (file contents, user/group/mode) between archives.
+
+            A repository location and an archive name must be specified for REPO::ARCHIVE1.
+            ARCHIVE2 is just another archive name in same repository (no repository location
+            allowed).
+
+            For archives created with Borg 1.1 or newer diff automatically detects whether
+            the archives are created with the same chunker params. If so, only chunk IDs
+            are compared, which is very fast.
+
+            For archives prior to Borg 1.1 chunk contents are compared by default.
+            If you did not create the archives with different chunker params,
+            pass ``--same-chunker-params``.
+            Note that the chunker params changed from Borg 0.xx to 1.0.
+
+            See the output of the "borg help patterns" command for more help on exclude patterns.
+            """)
+        subparser = subparsers.add_parser('diff', parents=[common_parser], add_help=False,
+                                          description=self.do_diff.__doc__,
+                                          epilog=diff_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='find differences in archive contents')
+        subparser.set_defaults(func=self.do_diff)
+        subparser.add_argument('--numeric-owner', dest='numeric_owner', action='store_true',
+                               help='only consider numeric user and group identifiers')
+        subparser.add_argument('--same-chunker-params', dest='same_chunker_params', action='store_true',
+                               help='Override check of chunker parameters.')
+        subparser.add_argument('--sort', dest='sort', action='store_true',
+                               help='Sort the output lines by file path.')
+        subparser.add_argument('location', metavar='REPO::ARCHIVE1',
+                               type=location_validator(archive=True),
+                               help='repository location and ARCHIVE1 name')
+        subparser.add_argument('archive2', metavar='ARCHIVE2',
+                               type=archivename_validator(),
+                               help='ARCHIVE2 name (no repository location allowed)')
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+                               help='paths of items inside the archives to compare; patterns are supported')
+        define_exclusion_group(subparser)
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3704,47 +3745,6 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
-        # borg diff
-        diff_epilog = process_epilog("""
-            This command finds differences (file contents, user/group/mode) between archives.
-
-            A repository location and an archive name must be specified for REPO::ARCHIVE1.
-            ARCHIVE2 is just another archive name in same repository (no repository location
-            allowed).
-
-            For archives created with Borg 1.1 or newer diff automatically detects whether
-            the archives are created with the same chunker params. If so, only chunk IDs
-            are compared, which is very fast.
-
-            For archives prior to Borg 1.1 chunk contents are compared by default.
-            If you did not create the archives with different chunker params,
-            pass ``--same-chunker-params``.
-            Note that the chunker params changed from Borg 0.xx to 1.0.
-
-            See the output of the "borg help patterns" command for more help on exclude patterns.
-            """)
-        subparser = subparsers.add_parser('diff', parents=[common_parser], add_help=False,
-                                          description=self.do_diff.__doc__,
-                                          epilog=diff_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='find differences in archive contents')
-        subparser.set_defaults(func=self.do_diff)
-        subparser.add_argument('--numeric-owner', dest='numeric_owner', action='store_true',
-                               help='only consider numeric user and group identifiers')
-        subparser.add_argument('--same-chunker-params', dest='same_chunker_params', action='store_true',
-                               help='Override check of chunker parameters.')
-        subparser.add_argument('--sort', dest='sort', action='store_true',
-                               help='Sort the output lines by file path.')
-        subparser.add_argument('location', metavar='REPO::ARCHIVE1',
-                               type=location_validator(archive=True),
-                               help='repository location and ARCHIVE1 name')
-        subparser.add_argument('archive2', metavar='ARCHIVE2',
-                               type=archivename_validator(),
-                               help='ARCHIVE2 name (no repository location allowed)')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='paths of items inside the archives to compare; patterns are supported')
-        define_exclusion_group(subparser)
-
         # borg rename
         rename_epilog = process_epilog("""
         This command renames an archive in the repository.

From 947b47a11aec36c71d7ad0317cdff234ce28f6f8 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:08:37 +0100
Subject: [PATCH 09/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=208?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg export-tar.
---
 src/borg/archiver.py | 110 +++++++++++++++++++++----------------------
 1 file changed, 55 insertions(+), 55 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index b61e43796..184663ab2 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3304,6 +3304,61 @@ class Archiver:
                                help='paths of items inside the archives to compare; patterns are supported')
         define_exclusion_group(subparser)
 
+        # borg export-tar
+        export_tar_epilog = process_epilog("""
+        This command creates a tarball from an archive.
+
+        When giving '-' as the output FILE, Borg will write a tar stream to standard output.
+
+        By default (``--tar-filter=auto``) Borg will detect whether the FILE should be compressed
+        based on its file extension and pipe the tarball through an appropriate filter
+        before writing it to FILE:
+
+        - .tar.gz: gzip
+        - .tar.bz2: bzip2
+        - .tar.xz: xz
+
+        Alternatively a ``--tar-filter`` program may be explicitly specified. It should
+        read the uncompressed tar stream from stdin and write a compressed/filtered
+        tar stream to stdout.
+
+        The generated tarball uses the GNU tar format.
+
+        export-tar is a lossy conversion:
+        BSD flags, ACLs, extended attributes (xattrs), atime and ctime are not exported.
+        Timestamp resolution is limited to whole seconds, not the nanosecond resolution
+        otherwise supported by Borg.
+
+        A ``--sparse`` option (as found in borg extract) is not supported.
+
+        By default the entire archive is extracted but a subset of files and directories
+        can be selected by passing a list of ``PATHs`` as arguments.
+        The file selection can further be restricted by using the ``--exclude`` option.
+
+        See the output of the "borg help patterns" command for more help on exclude patterns.
+
+        ``--progress`` can be slower than no progress display, since it makes one additional
+        pass over the archive metadata.
+        """)
+        subparser = subparsers.add_parser('export-tar', parents=[common_parser], add_help=False,
+                                          description=self.do_export_tar.__doc__,
+                                          epilog=export_tar_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='create tarball from archive')
+        subparser.set_defaults(func=self.do_export_tar)
+        subparser.add_argument('--tar-filter', dest='tar_filter', default='auto',
+                               help='filter program to pipe data through')
+        subparser.add_argument('--list', dest='output_list', action='store_true',
+                               help='output verbose list of items (files, dirs, ...)')
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to export')
+        subparser.add_argument('tarfile', metavar='FILE',
+                               help='output tar file. "-" to write to stdout instead.')
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+                               help='paths to extract; patterns are supported')
+        define_exclusion_group(subparser, strip_components=True)
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3690,61 +3745,6 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
-        # borg export-tar
-        export_tar_epilog = process_epilog("""
-        This command creates a tarball from an archive.
-
-        When giving '-' as the output FILE, Borg will write a tar stream to standard output.
-
-        By default (``--tar-filter=auto``) Borg will detect whether the FILE should be compressed
-        based on its file extension and pipe the tarball through an appropriate filter
-        before writing it to FILE:
-
-        - .tar.gz: gzip
-        - .tar.bz2: bzip2
-        - .tar.xz: xz
-
-        Alternatively a ``--tar-filter`` program may be explicitly specified. It should
-        read the uncompressed tar stream from stdin and write a compressed/filtered
-        tar stream to stdout.
-
-        The generated tarball uses the GNU tar format.
-
-        export-tar is a lossy conversion:
-        BSD flags, ACLs, extended attributes (xattrs), atime and ctime are not exported.
-        Timestamp resolution is limited to whole seconds, not the nanosecond resolution
-        otherwise supported by Borg.
-
-        A ``--sparse`` option (as found in borg extract) is not supported.
-
-        By default the entire archive is extracted but a subset of files and directories
-        can be selected by passing a list of ``PATHs`` as arguments.
-        The file selection can further be restricted by using the ``--exclude`` option.
-
-        See the output of the "borg help patterns" command for more help on exclude patterns.
-
-        ``--progress`` can be slower than no progress display, since it makes one additional
-        pass over the archive metadata.
-        """)
-        subparser = subparsers.add_parser('export-tar', parents=[common_parser], add_help=False,
-                                          description=self.do_export_tar.__doc__,
-                                          epilog=export_tar_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='create tarball from archive')
-        subparser.set_defaults(func=self.do_export_tar)
-        subparser.add_argument('--tar-filter', dest='tar_filter', default='auto',
-                               help='filter program to pipe data through')
-        subparser.add_argument('--list', dest='output_list', action='store_true',
-                               help='output verbose list of items (files, dirs, ...)')
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to export')
-        subparser.add_argument('tarfile', metavar='FILE',
-                               help='output tar file. "-" to write to stdout instead.')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='paths to extract; patterns are supported')
-        define_exclusion_group(subparser, strip_components=True)
-
         # borg rename
         rename_epilog = process_epilog("""
         This command renames an archive in the repository.

From aecd2f7fcb6265b1f42115455579cad28e200ae2 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:10:33 +0100
Subject: [PATCH 10/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=209?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg extract.
---
 src/borg/archiver.py | 92 ++++++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 46 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 184663ab2..ac34510c0 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3359,6 +3359,52 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
+        # borg extract
+        extract_epilog = process_epilog("""
+        This command extracts the contents of an archive. By default the entire
+        archive is extracted but a subset of files and directories can be selected
+        by passing a list of ``PATHs`` as arguments. The file selection can further
+        be restricted by using the ``--exclude`` option.
+
+        See the output of the "borg help patterns" command for more help on exclude patterns.
+
+        By using ``--dry-run``, you can do all extraction steps except actually writing the
+        output data: reading metadata and data chunks from the repo, checking the hash/hmac,
+        decrypting, decompressing.
+
+        ``--progress`` can be slower than no progress display, since it makes one additional
+        pass over the archive metadata.
+
+        .. note::
+
+            Currently, extract always writes into the current working directory ("."),
+            so make sure you ``cd`` to the right place before calling ``borg extract``.
+        """)
+        subparser = subparsers.add_parser('extract', parents=[common_parser], add_help=False,
+                                          description=self.do_extract.__doc__,
+                                          epilog=extract_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='extract archive contents')
+        subparser.set_defaults(func=self.do_extract)
+        subparser.add_argument('--list', dest='output_list', action='store_true',
+                               help='output verbose list of items (files, dirs, ...)')
+        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
+                               help='do not actually change any files')
+        subparser.add_argument('--numeric-owner', dest='numeric_owner', action='store_true',
+                               help='only obey numeric user and group identifiers')
+        subparser.add_argument('--nobsdflags', dest='nobsdflags', action='store_true',
+                               help='do not extract/set bsdflags (e.g. NODUMP, IMMUTABLE)')
+        subparser.add_argument('--stdout', dest='stdout', action='store_true',
+                               help='write all extracted data to stdout')
+        subparser.add_argument('--sparse', dest='sparse', action='store_true',
+                               help='create holes in output sparse file from all-zero chunks')
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to extract')
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+                               help='paths to extract; patterns are supported')
+        define_exclusion_group(subparser, strip_components=True)
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3699,52 +3745,6 @@ class Archiver:
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False))
 
-        # borg extract
-        extract_epilog = process_epilog("""
-        This command extracts the contents of an archive. By default the entire
-        archive is extracted but a subset of files and directories can be selected
-        by passing a list of ``PATHs`` as arguments. The file selection can further
-        be restricted by using the ``--exclude`` option.
-
-        See the output of the "borg help patterns" command for more help on exclude patterns.
-
-        By using ``--dry-run``, you can do all extraction steps except actually writing the
-        output data: reading metadata and data chunks from the repo, checking the hash/hmac,
-        decrypting, decompressing.
-
-        ``--progress`` can be slower than no progress display, since it makes one additional
-        pass over the archive metadata.
-
-        .. note::
-
-            Currently, extract always writes into the current working directory ("."),
-            so make sure you ``cd`` to the right place before calling ``borg extract``.
-        """)
-        subparser = subparsers.add_parser('extract', parents=[common_parser], add_help=False,
-                                          description=self.do_extract.__doc__,
-                                          epilog=extract_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='extract archive contents')
-        subparser.set_defaults(func=self.do_extract)
-        subparser.add_argument('--list', dest='output_list', action='store_true',
-                               help='output verbose list of items (files, dirs, ...)')
-        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
-                               help='do not actually change any files')
-        subparser.add_argument('--numeric-owner', dest='numeric_owner', action='store_true',
-                               help='only obey numeric user and group identifiers')
-        subparser.add_argument('--nobsdflags', dest='nobsdflags', action='store_true',
-                               help='do not extract/set bsdflags (e.g. NODUMP, IMMUTABLE)')
-        subparser.add_argument('--stdout', dest='stdout', action='store_true',
-                               help='write all extracted data to stdout')
-        subparser.add_argument('--sparse', dest='sparse', action='store_true',
-                               help='create holes in output sparse file from all-zero chunks')
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to extract')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='paths to extract; patterns are supported')
-        define_exclusion_group(subparser, strip_components=True)
-
         # borg rename
         rename_epilog = process_epilog("""
         This command renames an archive in the repository.

From 9bee80ba124cbf5256480ac566a1f4a9aa29aa75 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:12:10 +0100
Subject: [PATCH 11/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=2010?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg info.
---
 src/borg/archiver.py | 62 ++++++++++++++++++++++----------------------
 1 file changed, 31 insertions(+), 31 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index ac34510c0..05f6654e6 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3405,6 +3405,37 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
+        # borg info
+        info_epilog = process_epilog("""
+        This command displays detailed information about the specified archive or repository.
+
+        Please note that the deduplicated sizes of the individual archives do not add
+        up to the deduplicated size of the repository ("all archives"), because the two
+        are meaning different things:
+
+        This archive / deduplicated size = amount of data stored ONLY for this archive
+        = unique chunks of this archive.
+        All archives / deduplicated size = amount of data stored in the repo
+        = all chunks in the repository.
+
+        Borg archives can only contain a limited amount of file metadata.
+        The size of an archive relative to this limit depends on a number of factors,
+        mainly the number of files, the lengths of paths and other metadata stored for files.
+        This is shown as *utilization of maximum supported archive size*.
+        """)
+        subparser = subparsers.add_parser('info', parents=[common_parser], add_help=False,
+                                          description=self.do_info.__doc__,
+                                          epilog=info_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='show repository or archive information')
+        subparser.set_defaults(func=self.do_info)
+        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
+                               type=location_validator(),
+                               help='archive or repository to display information about')
+        subparser.add_argument('--json', action='store_true',
+                               help='format output as JSON')
+        define_archive_filters_group(subparser)
+
         # borg mount
         mount_epilog = process_epilog("""
         This command mounts an archive as a FUSE filesystem. This can be useful for
@@ -3831,37 +3862,6 @@ class Archiver:
         subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
                                help='mountpoint of the filesystem to umount')
 
-        # borg info
-        info_epilog = process_epilog("""
-        This command displays detailed information about the specified archive or repository.
-
-        Please note that the deduplicated sizes of the individual archives do not add
-        up to the deduplicated size of the repository ("all archives"), because the two
-        are meaning different things:
-
-        This archive / deduplicated size = amount of data stored ONLY for this archive
-        = unique chunks of this archive.
-        All archives / deduplicated size = amount of data stored in the repo
-        = all chunks in the repository.
-
-        Borg archives can only contain a limited amount of file metadata.
-        The size of an archive relative to this limit depends on a number of factors,
-        mainly the number of files, the lengths of paths and other metadata stored for files.
-        This is shown as *utilization of maximum supported archive size*.
-        """)
-        subparser = subparsers.add_parser('info', parents=[common_parser], add_help=False,
-                                          description=self.do_info.__doc__,
-                                          epilog=info_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='show repository or archive information')
-        subparser.set_defaults(func=self.do_info)
-        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
-                               type=location_validator(),
-                               help='archive or repository to display information about')
-        subparser.add_argument('--json', action='store_true',
-                               help='format output as JSON')
-        define_archive_filters_group(subparser)
-
         # borg prune
         prune_epilog = process_epilog("""
         The prune command prunes a repository by deleting all archives not matching

From 2eebe13d017771bcdd80d814542c007d6844a9af Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:13:52 +0100
Subject: [PATCH 12/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=2011?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg rename between mount and serve.
---
 src/borg/archiver.py | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 05f6654e6..564050f13 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3505,6 +3505,25 @@ class Archiver:
         if parser.prog == 'borgfs':
             return parser
 
+        # borg rename
+        rename_epilog = process_epilog("""
+        This command renames an archive in the repository.
+
+        This results in a different archive ID.
+        """)
+        subparser = subparsers.add_parser('rename', parents=[common_parser], add_help=False,
+                                          description=self.do_rename.__doc__,
+                                          epilog=rename_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='rename archive')
+        subparser.set_defaults(func=self.do_rename)
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to rename')
+        subparser.add_argument('name', metavar='NEWNAME',
+                               type=archivename_validator(),
+                               help='the new archive name to use')
+
         # borg serve
         serve_epilog = process_epilog("""
         This command starts a repository server process. This command is usually not used manually.
@@ -3776,25 +3795,6 @@ class Archiver:
         subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
                                type=location_validator(archive=False))
 
-        # borg rename
-        rename_epilog = process_epilog("""
-        This command renames an archive in the repository.
-
-        This results in a different archive ID.
-        """)
-        subparser = subparsers.add_parser('rename', parents=[common_parser], add_help=False,
-                                          description=self.do_rename.__doc__,
-                                          epilog=rename_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='rename archive')
-        subparser.set_defaults(func=self.do_rename)
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to rename')
-        subparser.add_argument('name', metavar='NEWNAME',
-                               type=archivename_validator(),
-                               help='the new archive name to use')
-
         # borg list
         list_epilog = process_epilog("""
         This command lists the contents of a repository or an archive.

From b7af4b5f915faa1e8f2a7346626d920dd8ef0c9a Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:15:47 +0100
Subject: [PATCH 13/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=2012?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg init, key, list after info.
---
 src/borg/archiver.py | 240 +++++++++++++++++++++----------------------
 1 file changed, 120 insertions(+), 120 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 564050f13..00b7aadf2 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3436,126 +3436,6 @@ class Archiver:
                                help='format output as JSON')
         define_archive_filters_group(subparser)
 
-        # borg mount
-        mount_epilog = process_epilog("""
-        This command mounts an archive as a FUSE filesystem. This can be useful for
-        browsing an archive or restoring individual files. Unless the ``--foreground``
-        option is given the command will run in the background until the filesystem
-        is ``umounted``.
-
-        The command ``borgfs`` provides a wrapper for ``borg mount``. This can also be
-        used in fstab entries:
-        ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0``
-
-        To allow a regular user to use fstab entries, add the ``user`` option:
-        ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0``
-
-        For FUSE configuration and mount options, see the mount.fuse(8) manual page.
-
-        Additional mount options supported by borg:
-
-        - versions: when used with a repository mount, this gives a merged, versioned
-          view of the files in the archives. EXPERIMENTAL, layout may change in future.
-        - allow_damaged_files: by default damaged files (where missing chunks were
-          replaced with runs of zeros by borg check ``--repair``) are not readable and
-          return EIO (I/O error). Set this option to read such files.
-        - ignore_permissions: for security reasons the "default_permissions" mount
-          option is internally enforced by borg. "ignore_permissions" can be given to
-          not enforce "default_permissions".
-
-        The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users
-        to tweak the performance. It sets the number of cached data chunks; additional
-        memory usage can be up to ~8 MiB times this number. The default is the number
-        of CPU cores.
-
-        When the daemonized process receives a signal or crashes, it does not unmount.
-        Unmounting in these cases could cause an active rsync or similar process
-        to unintentionally delete data.
-
-        When running in the foreground ^C/SIGINT unmounts cleanly, but other
-        signals or crashes do not.
-        """)
-
-        if parser.prog == 'borgfs':
-            parser.description = self.do_mount.__doc__
-            parser.epilog = mount_epilog
-            parser.formatter_class = argparse.RawDescriptionHelpFormatter
-            parser.help = 'mount repository'
-            subparser = parser
-        else:
-            subparser = subparsers.add_parser('mount', parents=[common_parser], add_help=False,
-                                            description=self.do_mount.__doc__,
-                                            epilog=mount_epilog,
-                                            formatter_class=argparse.RawDescriptionHelpFormatter,
-                                            help='mount repository')
-        subparser.set_defaults(func=self.do_mount)
-        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', type=location_validator(),
-                            help='repository/archive to mount')
-        subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
-                            help='where to mount filesystem')
-        subparser.add_argument('-f', '--foreground', dest='foreground',
-                            action='store_true',
-                            help='stay in foreground, do not daemonize')
-        subparser.add_argument('-o', dest='options', type=str,
-                            help='Extra mount options')
-        define_archive_filters_group(subparser)
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='paths to extract; patterns are supported')
-        define_exclusion_group(subparser, strip_components=True)
-        if parser.prog == 'borgfs':
-            return parser
-
-        # borg rename
-        rename_epilog = process_epilog("""
-        This command renames an archive in the repository.
-
-        This results in a different archive ID.
-        """)
-        subparser = subparsers.add_parser('rename', parents=[common_parser], add_help=False,
-                                          description=self.do_rename.__doc__,
-                                          epilog=rename_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='rename archive')
-        subparser.set_defaults(func=self.do_rename)
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to rename')
-        subparser.add_argument('name', metavar='NEWNAME',
-                               type=archivename_validator(),
-                               help='the new archive name to use')
-
-        # borg serve
-        serve_epilog = process_epilog("""
-        This command starts a repository server process. This command is usually not used manually.
-        """)
-        subparser = subparsers.add_parser('serve', parents=[common_parser], add_help=False,
-                                          description=self.do_serve.__doc__, epilog=serve_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='start repository server process')
-        subparser.set_defaults(func=self.do_serve)
-        subparser.add_argument('--restrict-to-path', metavar='PATH', dest='restrict_to_paths', action='append',
-                               help='restrict repository access to PATH. '
-                                    'Can be specified multiple times to allow the client access to several directories. '
-                                    'Access to all sub-directories is granted implicitly; PATH doesn\'t need to directly point to a repository.')
-        subparser.add_argument('--restrict-to-repository', metavar='PATH', dest='restrict_to_repositories', action='append',
-                                help='restrict repository access. Only the repository located at PATH '
-                                     '(no sub-directories are considered) is accessible. '
-                                     'Can be specified multiple times to allow the client access to several repositories. '
-                                     'Unlike ``--restrict-to-path`` sub-directories are not accessible; '
-                                     'PATH needs to directly point at a repository location. '
-                                     'PATH may be an empty directory or the last element of PATH may not exist, in which case '
-                                     'the client may initialize a repository there.')
-        subparser.add_argument('--append-only', dest='append_only', action='store_true',
-                               help='only allow appending to repository segment files. Note that this only '
-                                    'affects the low level structure of the repository, and running `delete` '
-                                    'or `prune` will still be allowed. See :ref:`append_only_mode` in Additional '
-                                    'Notes for more details.')
-        subparser.add_argument('--storage-quota', metavar='QUOTA', dest='storage_quota',
-                               type=parse_storage_quota, default=None,
-                               help='Override storage quota of the repository (e.g. 5G, 1.5T). '
-                                    'When a new repository is initialized, sets the storage quota on the new '
-                                    'repository as well. Default: no quota.')
-
         # borg init
         init_epilog = process_epilog("""
         This command initializes an empty repository. A repository is a filesystem
@@ -3846,6 +3726,126 @@ class Archiver:
         define_archive_filters_group(subparser)
         define_exclusion_group(subparser)
 
+        # borg mount
+        mount_epilog = process_epilog("""
+        This command mounts an archive as a FUSE filesystem. This can be useful for
+        browsing an archive or restoring individual files. Unless the ``--foreground``
+        option is given the command will run in the background until the filesystem
+        is ``umounted``.
+
+        The command ``borgfs`` provides a wrapper for ``borg mount``. This can also be
+        used in fstab entries:
+        ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto 0 0``
+
+        To allow a regular user to use fstab entries, add the ``user`` option:
+        ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0``
+
+        For FUSE configuration and mount options, see the mount.fuse(8) manual page.
+
+        Additional mount options supported by borg:
+
+        - versions: when used with a repository mount, this gives a merged, versioned
+          view of the files in the archives. EXPERIMENTAL, layout may change in future.
+        - allow_damaged_files: by default damaged files (where missing chunks were
+          replaced with runs of zeros by borg check ``--repair``) are not readable and
+          return EIO (I/O error). Set this option to read such files.
+        - ignore_permissions: for security reasons the "default_permissions" mount
+          option is internally enforced by borg. "ignore_permissions" can be given to
+          not enforce "default_permissions".
+
+        The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users
+        to tweak the performance. It sets the number of cached data chunks; additional
+        memory usage can be up to ~8 MiB times this number. The default is the number
+        of CPU cores.
+
+        When the daemonized process receives a signal or crashes, it does not unmount.
+        Unmounting in these cases could cause an active rsync or similar process
+        to unintentionally delete data.
+
+        When running in the foreground ^C/SIGINT unmounts cleanly, but other
+        signals or crashes do not.
+        """)
+
+        if parser.prog == 'borgfs':
+            parser.description = self.do_mount.__doc__
+            parser.epilog = mount_epilog
+            parser.formatter_class = argparse.RawDescriptionHelpFormatter
+            parser.help = 'mount repository'
+            subparser = parser
+        else:
+            subparser = subparsers.add_parser('mount', parents=[common_parser], add_help=False,
+                                            description=self.do_mount.__doc__,
+                                            epilog=mount_epilog,
+                                            formatter_class=argparse.RawDescriptionHelpFormatter,
+                                            help='mount repository')
+        subparser.set_defaults(func=self.do_mount)
+        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', type=location_validator(),
+                            help='repository/archive to mount')
+        subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
+                            help='where to mount filesystem')
+        subparser.add_argument('-f', '--foreground', dest='foreground',
+                            action='store_true',
+                            help='stay in foreground, do not daemonize')
+        subparser.add_argument('-o', dest='options', type=str,
+                            help='Extra mount options')
+        define_archive_filters_group(subparser)
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+                               help='paths to extract; patterns are supported')
+        define_exclusion_group(subparser, strip_components=True)
+        if parser.prog == 'borgfs':
+            return parser
+
+        # borg rename
+        rename_epilog = process_epilog("""
+        This command renames an archive in the repository.
+
+        This results in a different archive ID.
+        """)
+        subparser = subparsers.add_parser('rename', parents=[common_parser], add_help=False,
+                                          description=self.do_rename.__doc__,
+                                          epilog=rename_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='rename archive')
+        subparser.set_defaults(func=self.do_rename)
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to rename')
+        subparser.add_argument('name', metavar='NEWNAME',
+                               type=archivename_validator(),
+                               help='the new archive name to use')
+
+        # borg serve
+        serve_epilog = process_epilog("""
+        This command starts a repository server process. This command is usually not used manually.
+        """)
+        subparser = subparsers.add_parser('serve', parents=[common_parser], add_help=False,
+                                          description=self.do_serve.__doc__, epilog=serve_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='start repository server process')
+        subparser.set_defaults(func=self.do_serve)
+        subparser.add_argument('--restrict-to-path', metavar='PATH', dest='restrict_to_paths', action='append',
+                               help='restrict repository access to PATH. '
+                                    'Can be specified multiple times to allow the client access to several directories. '
+                                    'Access to all sub-directories is granted implicitly; PATH doesn\'t need to directly point to a repository.')
+        subparser.add_argument('--restrict-to-repository', metavar='PATH', dest='restrict_to_repositories', action='append',
+                                help='restrict repository access. Only the repository located at PATH '
+                                     '(no sub-directories are considered) is accessible. '
+                                     'Can be specified multiple times to allow the client access to several repositories. '
+                                     'Unlike ``--restrict-to-path`` sub-directories are not accessible; '
+                                     'PATH needs to directly point at a repository location. '
+                                     'PATH may be an empty directory or the last element of PATH may not exist, in which case '
+                                     'the client may initialize a repository there.')
+        subparser.add_argument('--append-only', dest='append_only', action='store_true',
+                               help='only allow appending to repository segment files. Note that this only '
+                                    'affects the low level structure of the repository, and running `delete` '
+                                    'or `prune` will still be allowed. See :ref:`append_only_mode` in Additional '
+                                    'Notes for more details.')
+        subparser.add_argument('--storage-quota', metavar='QUOTA', dest='storage_quota',
+                               type=parse_storage_quota, default=None,
+                               help='Override storage quota of the repository (e.g. 5G, 1.5T). '
+                                    'When a new repository is initialized, sets the storage quota on the new '
+                                    'repository as well. Default: no quota.')
+
         # borg umount
         umount_epilog = process_epilog("""
         This command un-mounts a FUSE filesystem that was mounted with ``borg mount``.

From 00793cba0142fc45db1ab18545628f5109d734d7 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:22:00 +0100
Subject: [PATCH 14/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=2013?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg prune.
---
 src/borg/archiver.py | 134 +++++++++++++++++++++----------------------
 1 file changed, 67 insertions(+), 67 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 00b7aadf2..8142082a3 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3795,73 +3795,6 @@ class Archiver:
         if parser.prog == 'borgfs':
             return parser
 
-        # borg rename
-        rename_epilog = process_epilog("""
-        This command renames an archive in the repository.
-
-        This results in a different archive ID.
-        """)
-        subparser = subparsers.add_parser('rename', parents=[common_parser], add_help=False,
-                                          description=self.do_rename.__doc__,
-                                          epilog=rename_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='rename archive')
-        subparser.set_defaults(func=self.do_rename)
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to rename')
-        subparser.add_argument('name', metavar='NEWNAME',
-                               type=archivename_validator(),
-                               help='the new archive name to use')
-
-        # borg serve
-        serve_epilog = process_epilog("""
-        This command starts a repository server process. This command is usually not used manually.
-        """)
-        subparser = subparsers.add_parser('serve', parents=[common_parser], add_help=False,
-                                          description=self.do_serve.__doc__, epilog=serve_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='start repository server process')
-        subparser.set_defaults(func=self.do_serve)
-        subparser.add_argument('--restrict-to-path', metavar='PATH', dest='restrict_to_paths', action='append',
-                               help='restrict repository access to PATH. '
-                                    'Can be specified multiple times to allow the client access to several directories. '
-                                    'Access to all sub-directories is granted implicitly; PATH doesn\'t need to directly point to a repository.')
-        subparser.add_argument('--restrict-to-repository', metavar='PATH', dest='restrict_to_repositories', action='append',
-                                help='restrict repository access. Only the repository located at PATH '
-                                     '(no sub-directories are considered) is accessible. '
-                                     'Can be specified multiple times to allow the client access to several repositories. '
-                                     'Unlike ``--restrict-to-path`` sub-directories are not accessible; '
-                                     'PATH needs to directly point at a repository location. '
-                                     'PATH may be an empty directory or the last element of PATH may not exist, in which case '
-                                     'the client may initialize a repository there.')
-        subparser.add_argument('--append-only', dest='append_only', action='store_true',
-                               help='only allow appending to repository segment files. Note that this only '
-                                    'affects the low level structure of the repository, and running `delete` '
-                                    'or `prune` will still be allowed. See :ref:`append_only_mode` in Additional '
-                                    'Notes for more details.')
-        subparser.add_argument('--storage-quota', metavar='QUOTA', dest='storage_quota',
-                               type=parse_storage_quota, default=None,
-                               help='Override storage quota of the repository (e.g. 5G, 1.5T). '
-                                    'When a new repository is initialized, sets the storage quota on the new '
-                                    'repository as well. Default: no quota.')
-
-        # borg umount
-        umount_epilog = process_epilog("""
-        This command un-mounts a FUSE filesystem that was mounted with ``borg mount``.
-
-        This is a convenience wrapper that just calls the platform-specific shell
-        command - usually this is either umount or fusermount -u.
-        """)
-        subparser = subparsers.add_parser('umount', parents=[common_parser], add_help=False,
-                                          description=self.do_umount.__doc__,
-                                          epilog=umount_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='umount repository')
-        subparser.set_defaults(func=self.do_umount)
-        subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
-                               help='mountpoint of the filesystem to umount')
-
         # borg prune
         prune_epilog = process_epilog("""
         The prune command prunes a repository by deleting all archives not matching
@@ -3949,6 +3882,73 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='repository to prune')
 
+        # borg rename
+        rename_epilog = process_epilog("""
+        This command renames an archive in the repository.
+
+        This results in a different archive ID.
+        """)
+        subparser = subparsers.add_parser('rename', parents=[common_parser], add_help=False,
+                                          description=self.do_rename.__doc__,
+                                          epilog=rename_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='rename archive')
+        subparser.set_defaults(func=self.do_rename)
+        subparser.add_argument('location', metavar='ARCHIVE',
+                               type=location_validator(archive=True),
+                               help='archive to rename')
+        subparser.add_argument('name', metavar='NEWNAME',
+                               type=archivename_validator(),
+                               help='the new archive name to use')
+
+        # borg serve
+        serve_epilog = process_epilog("""
+        This command starts a repository server process. This command is usually not used manually.
+        """)
+        subparser = subparsers.add_parser('serve', parents=[common_parser], add_help=False,
+                                          description=self.do_serve.__doc__, epilog=serve_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='start repository server process')
+        subparser.set_defaults(func=self.do_serve)
+        subparser.add_argument('--restrict-to-path', metavar='PATH', dest='restrict_to_paths', action='append',
+                               help='restrict repository access to PATH. '
+                                    'Can be specified multiple times to allow the client access to several directories. '
+                                    'Access to all sub-directories is granted implicitly; PATH doesn\'t need to directly point to a repository.')
+        subparser.add_argument('--restrict-to-repository', metavar='PATH', dest='restrict_to_repositories', action='append',
+                                help='restrict repository access. Only the repository located at PATH '
+                                     '(no sub-directories are considered) is accessible. '
+                                     'Can be specified multiple times to allow the client access to several repositories. '
+                                     'Unlike ``--restrict-to-path`` sub-directories are not accessible; '
+                                     'PATH needs to directly point at a repository location. '
+                                     'PATH may be an empty directory or the last element of PATH may not exist, in which case '
+                                     'the client may initialize a repository there.')
+        subparser.add_argument('--append-only', dest='append_only', action='store_true',
+                               help='only allow appending to repository segment files. Note that this only '
+                                    'affects the low level structure of the repository, and running `delete` '
+                                    'or `prune` will still be allowed. See :ref:`append_only_mode` in Additional '
+                                    'Notes for more details.')
+        subparser.add_argument('--storage-quota', metavar='QUOTA', dest='storage_quota',
+                               type=parse_storage_quota, default=None,
+                               help='Override storage quota of the repository (e.g. 5G, 1.5T). '
+                                    'When a new repository is initialized, sets the storage quota on the new '
+                                    'repository as well. Default: no quota.')
+
+        # borg umount
+        umount_epilog = process_epilog("""
+        This command un-mounts a FUSE filesystem that was mounted with ``borg mount``.
+
+        This is a convenience wrapper that just calls the platform-specific shell
+        command - usually this is either umount or fusermount -u.
+        """)
+        subparser = subparsers.add_parser('umount', parents=[common_parser], add_help=False,
+                                          description=self.do_umount.__doc__,
+                                          epilog=umount_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help='umount repository')
+        subparser.set_defaults(func=self.do_umount)
+        subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
+                               help='mountpoint of the filesystem to umount')
+
         # borg upgrade
         upgrade_epilog = process_epilog("""
         Upgrade an existing, local Borg repository.

From 1e2fee8c1aa7012061aa4413acfb82422cc3ebc6 Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:23:35 +0100
Subject: [PATCH 15/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=2014?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Moved borg recreate.
---
 src/borg/archiver.py | 218 +++++++++++++++++++++----------------------
 1 file changed, 109 insertions(+), 109 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 8142082a3..715794d05 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -3882,6 +3882,115 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='repository to prune')
 
+        # borg recreate
+        recreate_epilog = process_epilog("""
+        Recreate the contents of existing archives.
+
+        This is an *experimental* feature. Do *not* use this on your only backup.
+
+        Important: Repository disk space is **not** freed until you run ``borg compact``.
+
+        ``--exclude``, ``--exclude-from``, ``--exclude-if-present``, ``--keep-exclude-tags``, and PATH
+        have the exact same semantics as in "borg create". If PATHs are specified the
+        resulting archive will only contain files from these PATHs.
+
+        Note that all paths in an archive are relative, therefore absolute patterns/paths
+        will *not* match (``--exclude``, ``--exclude-from``, PATHs).
+
+        ``--recompress`` allows one to change the compression of existing data in archives.
+        Due to how Borg stores compressed size information this might display
+        incorrect information for archives that were not recreated at the same time.
+        There is no risk of data loss by this.
+
+        ``--chunker-params`` will re-chunk all files in the archive, this can be
+        used to have upgraded Borg 0.xx or Attic archives deduplicate with
+        Borg 1.x archives.
+
+        **USE WITH CAUTION.**
+        Depending on the PATHs and patterns given, recreate can be used to permanently
+        delete files from archives.
+        When in doubt, use ``--dry-run --verbose --list`` to see how patterns/PATHS are
+        interpreted.
+
+        The archive being recreated is only removed after the operation completes. The
+        archive that is built during the operation exists at the same time at
+        "<ARCHIVE>.recreate". The new archive will have a different archive ID.
+
+        With ``--target`` the original archive is not replaced, instead a new archive is created.
+
+        When rechunking (or recompressing), space usage can be substantial - expect
+        at least the entire deduplicated size of the archives using the previous
+        chunker (or compression) params.
+
+        If you recently ran borg check --repair and it had to fix lost chunks with all-zero
+        replacement chunks, please first run another backup for the same data and re-run
+        borg check --repair afterwards to heal any archives that had lost chunks which are
+        still generated from the input data.
+
+        Important: running borg recreate to re-chunk will remove the chunks_healthy
+        metadata of all items with replacement chunks, so healing will not be possible
+        any more after re-chunking (it is also unlikely it would ever work: due to the
+        change of chunking parameters, the missing chunk likely will never be seen again
+        even if you still have the data that produced it).
+        """)
+        subparser = subparsers.add_parser('recreate', parents=[common_parser], add_help=False,
+                                          description=self.do_recreate.__doc__,
+                                          epilog=recreate_epilog,
+                                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                                          help=self.do_recreate.__doc__)
+        subparser.set_defaults(func=self.do_recreate)
+        subparser.add_argument('--list', dest='output_list', action='store_true',
+                               help='output verbose list of items (files, dirs, ...)')
+        subparser.add_argument('--filter', metavar='STATUSCHARS', dest='output_filter',
+                               help='only display items with the given status characters (listed in borg create --help)')
+        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
+                               help='do not change anything')
+        subparser.add_argument('-s', '--stats', dest='stats', action='store_true',
+                               help='print statistics at end')
+
+        define_exclusion_group(subparser, tag_files=True)
+
+        archive_group = subparser.add_argument_group('Archive options')
+        archive_group.add_argument('--target', dest='target', metavar='TARGET', default=None,
+                                   type=archivename_validator(),
+                                   help='create a new archive with the name ARCHIVE, do not replace existing archive '
+                                        '(only applies for a single archive)')
+        archive_group.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
+                                   type=int, default=1800, metavar='SECONDS',
+                                   help='write checkpoint every SECONDS seconds (Default: 1800)')
+        archive_group.add_argument('--comment', dest='comment', metavar='COMMENT', default=None,
+                                   help='add a comment text to the archive')
+        archive_group.add_argument('--timestamp', metavar='TIMESTAMP', dest='timestamp',
+                                   type=timestamp, default=None,
+                                   help='manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). '
+                                        'alternatively, give a reference file/directory.')
+        archive_group.add_argument('-C', '--compression', metavar='COMPRESSION', dest='compression',
+                                   type=CompressionSpec, default=CompressionSpec('lz4'),
+                                   help='select compression algorithm, see the output of the '
+                                        '"borg help compression" command for details.')
+        archive_group.add_argument('--recompress', metavar='MODE', dest='recompress', nargs='?',
+                                   default='never', const='if-different', choices=('never', 'if-different', 'always'),
+                                   help='recompress data chunks according to ``--compression``. '
+                                        'MODE `if-different`: '
+                                        'recompress if current compression is with a different compression algorithm '
+                                        '(the level is not considered). '
+                                        'MODE `always`: '
+                                        'recompress even if current compression is with the same compression algorithm '
+                                        '(use this to change the compression level). '
+                                        'MODE `never` (default): '
+                                        'do not recompress.')
+        archive_group.add_argument('--chunker-params', metavar='PARAMS', dest='chunker_params',
+                                   type=ChunkerParams, default=CHUNKER_PARAMS,
+                                   help='specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, '
+                                        'HASH_MASK_BITS, HASH_WINDOW_SIZE) or `default` to use the current defaults. '
+                                        'default: %s,%d,%d,%d,%d' % CHUNKER_PARAMS)
+
+        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
+                               type=location_validator(),
+                               help='repository/archive to recreate')
+        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
+                               help='paths to recreate; patterns are supported')
+
         # borg rename
         rename_epilog = process_epilog("""
         This command renames an archive in the repository.
@@ -4053,115 +4162,6 @@ class Archiver:
                                type=location_validator(archive=False),
                                help='path to the repository to be upgraded')
 
-        # borg recreate
-        recreate_epilog = process_epilog("""
-        Recreate the contents of existing archives.
-
-        This is an *experimental* feature. Do *not* use this on your only backup.
-
-        Important: Repository disk space is **not** freed until you run ``borg compact``.
-
-        ``--exclude``, ``--exclude-from``, ``--exclude-if-present``, ``--keep-exclude-tags``, and PATH
-        have the exact same semantics as in "borg create". If PATHs are specified the
-        resulting archive will only contain files from these PATHs.
-
-        Note that all paths in an archive are relative, therefore absolute patterns/paths
-        will *not* match (``--exclude``, ``--exclude-from``, PATHs).
-
-        ``--recompress`` allows one to change the compression of existing data in archives.
-        Due to how Borg stores compressed size information this might display
-        incorrect information for archives that were not recreated at the same time.
-        There is no risk of data loss by this.
-
-        ``--chunker-params`` will re-chunk all files in the archive, this can be
-        used to have upgraded Borg 0.xx or Attic archives deduplicate with
-        Borg 1.x archives.
-
-        **USE WITH CAUTION.**
-        Depending on the PATHs and patterns given, recreate can be used to permanently
-        delete files from archives.
-        When in doubt, use ``--dry-run --verbose --list`` to see how patterns/PATHS are
-        interpreted.
-
-        The archive being recreated is only removed after the operation completes. The
-        archive that is built during the operation exists at the same time at
-        "<ARCHIVE>.recreate". The new archive will have a different archive ID.
-
-        With ``--target`` the original archive is not replaced, instead a new archive is created.
-
-        When rechunking (or recompressing), space usage can be substantial - expect
-        at least the entire deduplicated size of the archives using the previous
-        chunker (or compression) params.
-
-        If you recently ran borg check --repair and it had to fix lost chunks with all-zero
-        replacement chunks, please first run another backup for the same data and re-run
-        borg check --repair afterwards to heal any archives that had lost chunks which are
-        still generated from the input data.
-
-        Important: running borg recreate to re-chunk will remove the chunks_healthy
-        metadata of all items with replacement chunks, so healing will not be possible
-        any more after re-chunking (it is also unlikely it would ever work: due to the
-        change of chunking parameters, the missing chunk likely will never be seen again
-        even if you still have the data that produced it).
-        """)
-        subparser = subparsers.add_parser('recreate', parents=[common_parser], add_help=False,
-                                          description=self.do_recreate.__doc__,
-                                          epilog=recreate_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help=self.do_recreate.__doc__)
-        subparser.set_defaults(func=self.do_recreate)
-        subparser.add_argument('--list', dest='output_list', action='store_true',
-                               help='output verbose list of items (files, dirs, ...)')
-        subparser.add_argument('--filter', metavar='STATUSCHARS', dest='output_filter',
-                               help='only display items with the given status characters (listed in borg create --help)')
-        subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
-                               help='do not change anything')
-        subparser.add_argument('-s', '--stats', dest='stats', action='store_true',
-                               help='print statistics at end')
-
-        define_exclusion_group(subparser, tag_files=True)
-
-        archive_group = subparser.add_argument_group('Archive options')
-        archive_group.add_argument('--target', dest='target', metavar='TARGET', default=None,
-                                   type=archivename_validator(),
-                                   help='create a new archive with the name ARCHIVE, do not replace existing archive '
-                                        '(only applies for a single archive)')
-        archive_group.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
-                                   type=int, default=1800, metavar='SECONDS',
-                                   help='write checkpoint every SECONDS seconds (Default: 1800)')
-        archive_group.add_argument('--comment', dest='comment', metavar='COMMENT', default=None,
-                                   help='add a comment text to the archive')
-        archive_group.add_argument('--timestamp', metavar='TIMESTAMP', dest='timestamp',
-                                   type=timestamp, default=None,
-                                   help='manually specify the archive creation date/time (UTC, yyyy-mm-ddThh:mm:ss format). '
-                                        'alternatively, give a reference file/directory.')
-        archive_group.add_argument('-C', '--compression', metavar='COMPRESSION', dest='compression',
-                                   type=CompressionSpec, default=CompressionSpec('lz4'),
-                                   help='select compression algorithm, see the output of the '
-                                        '"borg help compression" command for details.')
-        archive_group.add_argument('--recompress', metavar='MODE', dest='recompress', nargs='?',
-                                   default='never', const='if-different', choices=('never', 'if-different', 'always'),
-                                   help='recompress data chunks according to ``--compression``. '
-                                        'MODE `if-different`: '
-                                        'recompress if current compression is with a different compression algorithm '
-                                        '(the level is not considered). '
-                                        'MODE `always`: '
-                                        'recompress even if current compression is with the same compression algorithm '
-                                        '(use this to change the compression level). '
-                                        'MODE `never` (default): '
-                                        'do not recompress.')
-        archive_group.add_argument('--chunker-params', metavar='PARAMS', dest='chunker_params',
-                                   type=ChunkerParams, default=CHUNKER_PARAMS,
-                                   help='specify the chunker parameters (ALGO, CHUNK_MIN_EXP, CHUNK_MAX_EXP, '
-                                        'HASH_MASK_BITS, HASH_WINDOW_SIZE) or `default` to use the current defaults. '
-                                        'default: %s,%d,%d,%d,%d' % CHUNKER_PARAMS)
-
-        subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
-                               type=location_validator(),
-                               help='repository/archive to recreate')
-        subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='paths to recreate; patterns are supported')
-
         # borg with-lock
         with_lock_epilog = process_epilog("""
         This command runs a user-specified command while the repository lock is held.

From 1d74783a02d8f54b814e63aa6eb5bffd18abf7ad Mon Sep 17 00:00:00 2001
From: Thalian <github@fantasya-pbem.de>
Date: Fri, 29 Mar 2019 19:31:07 +0100
Subject: [PATCH 16/16] =?UTF-8?q?[TASK]=20#4471=20=E2=80=93=20borg=20help?=
 =?UTF-8?q?=20should=20print=20<command>=20list=20in=20sorted=20order:=20S?=
 =?UTF-8?q?tep=2015?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Finally moved borg help and added a section comment.
---
 src/borg/archiver.py | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 715794d05..245e16041 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2822,14 +2822,6 @@ class Archiver:
         subparser.add_argument('value', metavar='VALUE', nargs='?',
                                help='new value for key')
 
-        subparser = subparsers.add_parser('help', parents=[common_parser], add_help=False,
-                                          description='Extra help')
-        subparser.add_argument('--epilog-only', dest='epilog_only', action='store_true')
-        subparser.add_argument('--usage-only', dest='usage_only', action='store_true')
-        subparser.set_defaults(func=functools.partial(self.do_help, parser, subparsers.choices))
-        subparser.add_argument('topic', metavar='TOPIC', type=str, nargs='?',
-                               help='additional help on TOPIC')
-
         # borg create
         create_epilog = process_epilog("""
         This command creates a backup archive containing all files found while recursively
@@ -3405,6 +3397,15 @@ class Archiver:
                                help='paths to extract; patterns are supported')
         define_exclusion_group(subparser, strip_components=True)
 
+        # borg help
+        subparser = subparsers.add_parser('help', parents=[common_parser], add_help=False,
+                                          description='Extra help')
+        subparser.add_argument('--epilog-only', dest='epilog_only', action='store_true')
+        subparser.add_argument('--usage-only', dest='usage_only', action='store_true')
+        subparser.set_defaults(func=functools.partial(self.do_help, parser, subparsers.choices))
+        subparser.add_argument('topic', metavar='TOPIC', type=str, nargs='?',
+                               help='additional help on TOPIC')
+
         # borg info
         info_epilog = process_epilog("""
         This command displays detailed information about the specified archive or repository.