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.