浏览代码

Assimilate "borg comment" into "borg recreate"

Marian Beermann 9 年之前
父节点
当前提交
4b5e6ffa77
共有 6 个文件被更改,包括 120 次插入151 次删除
  1. 7 5
      borg/archive.py
  2. 37 49
      borg/archiver.py
  3. 8 2
      borg/testsuite/archiver.py
  4. 15 20
      docs/usage.rst
  5. 0 23
      docs/usage/comment.rst.inc
  6. 53 52
      docs/usage/recreate.rst.inc

+ 7 - 5
borg/archive.py

@@ -978,20 +978,20 @@ class ArchiveRecreater:
         self.interrupt = False
         self.interrupt = False
         self.errors = False
         self.errors = False
 
 
-    def recreate(self, archive_name):
+    def recreate(self, archive_name, comment=None):
         assert not self.is_temporary_archive(archive_name)
         assert not self.is_temporary_archive(archive_name)
         archive = self.open_archive(archive_name)
         archive = self.open_archive(archive_name)
         target, resume_from = self.create_target_or_resume(archive)
         target, resume_from = self.create_target_or_resume(archive)
         if self.exclude_if_present or self.exclude_caches:
         if self.exclude_if_present or self.exclude_caches:
             self.matcher_add_tagged_dirs(archive)
             self.matcher_add_tagged_dirs(archive)
-        if self.matcher.empty() and not self.recompress and not target.recreate_rechunkify:
+        if self.matcher.empty() and not self.recompress and not target.recreate_rechunkify and comment is None:
             logger.info("Skipping archive %s, nothing to do", archive_name)
             logger.info("Skipping archive %s, nothing to do", archive_name)
             return True
             return True
         try:
         try:
             self.process_items(archive, target, resume_from)
             self.process_items(archive, target, resume_from)
         except self.Interrupted as e:
         except self.Interrupted as e:
             return self.save(archive, target, completed=False, metadata=e.metadata)
             return self.save(archive, target, completed=False, metadata=e.metadata)
-        return self.save(archive, target)
+        return self.save(archive, target, comment)
 
 
     def process_items(self, archive, target, resume_from=None):
     def process_items(self, archive, target, resume_from=None):
         matcher = self.matcher
         matcher = self.matcher
@@ -1108,13 +1108,15 @@ class ArchiveRecreater:
         logger.debug('Copied %d chunks from a partially processed item', len(partial_chunks))
         logger.debug('Copied %d chunks from a partially processed item', len(partial_chunks))
         return partial_chunks
         return partial_chunks
 
 
-    def save(self, archive, target, completed=True, metadata=None):
+    def save(self, archive, target, comment=None, completed=True, metadata=None):
         """Save target archive. If completed, replace source. If not, save temporary with additional 'metadata' dict."""
         """Save target archive. If completed, replace source. If not, save temporary with additional 'metadata' dict."""
         if self.dry_run:
         if self.dry_run:
             return completed
             return completed
         if completed:
         if completed:
             timestamp = archive.ts.replace(tzinfo=None)
             timestamp = archive.ts.replace(tzinfo=None)
-            target.save(timestamp=timestamp, additional_metadata={
+            if comment is None:
+                comment = archive.metadata.get(b'comment', '')
+            target.save(timestamp=timestamp, comment=comment, additional_metadata={
                 'cmdline': archive.metadata[b'cmdline'],
                 'cmdline': archive.metadata[b'cmdline'],
                 'recreate_cmdline': sys.argv,
                 'recreate_cmdline': sys.argv,
             })
             })

+ 37 - 49
borg/archiver.py

@@ -626,16 +626,6 @@ class Archiver:
         cache.commit()
         cache.commit()
         return self.exit_code
         return self.exit_code
 
 
-    @with_repository(exclusive=True, cache=True)
-    @with_archive
-    def do_comment(self, args, repository, manifest, key, cache, archive):
-        """Set the archive comment"""
-        archive.set_meta(b'comment', args.comment)
-        manifest.write()
-        repository.commit()
-        cache.commit()
-        return self.exit_code
-
     @with_repository(exclusive=True, cache=True)
     @with_repository(exclusive=True, cache=True)
     def do_delete(self, args, repository, manifest, key, cache):
     def do_delete(self, args, repository, manifest, key, cache):
         """Delete an existing repository or archive"""
         """Delete an existing repository or archive"""
@@ -851,14 +841,14 @@ class Archiver:
             if recreater.is_temporary_archive(name):
             if recreater.is_temporary_archive(name):
                 self.print_error('Refusing to work on temporary archive of prior recreate: %s', name)
                 self.print_error('Refusing to work on temporary archive of prior recreate: %s', name)
                 return self.exit_code
                 return self.exit_code
-            recreater.recreate(name)
+            recreater.recreate(name, args.comment)
         else:
         else:
             for archive in manifest.list_archive_infos(sort_by='ts'):
             for archive in manifest.list_archive_infos(sort_by='ts'):
                 name = archive.name
                 name = archive.name
                 if recreater.is_temporary_archive(name):
                 if recreater.is_temporary_archive(name):
                     continue
                     continue
                 print('Processing', name)
                 print('Processing', name)
-                if not recreater.recreate(name):
+                if not recreater.recreate(name, args.comment):
                     break
                     break
         manifest.write()
         manifest.write()
         repository.commit()
         repository.commit()
@@ -1434,23 +1424,6 @@ class Archiver:
                                type=archivename_validator(),
                                type=archivename_validator(),
                                help='the new archive name to use')
                                help='the new archive name to use')
 
 
-        comment_epilog = textwrap.dedent("""
-        This command sets the archive comment.
-
-        This results in a different archive ID.
-        """)
-        subparser = subparsers.add_parser('comment', parents=[common_parser], add_help=False,
-                                          description=self.do_comment.__doc__,
-                                          epilog=comment_epilog,
-                                          formatter_class=argparse.RawDescriptionHelpFormatter,
-                                          help='set the archive comment')
-        subparser.set_defaults(func=self.do_comment)
-        subparser.add_argument('location', metavar='ARCHIVE',
-                               type=location_validator(archive=True),
-                               help='archive to modify')
-        subparser.add_argument('comment', metavar='COMMENT',
-                               help='the new archive comment')
-
         delete_epilog = textwrap.dedent("""
         delete_epilog = textwrap.dedent("""
         This command deletes an archive from the repository or the complete repository.
         This command deletes an archive from the repository or the complete repository.
         Disk space is reclaimed accordingly. If you delete the complete repository, the
         Disk space is reclaimed accordingly. If you delete the complete repository, the
@@ -1746,22 +1719,36 @@ class Archiver:
         subparser.add_argument('-s', '--stats', dest='stats',
         subparser.add_argument('-s', '--stats', dest='stats',
                                action='store_true', default=False,
                                action='store_true', default=False,
                                help='print statistics at end')
                                help='print statistics at end')
-        subparser.add_argument('-e', '--exclude', dest='excludes',
-                               type=parse_pattern, action='append',
-                               metavar="PATTERN", help='exclude paths matching PATTERN')
-        subparser.add_argument('--exclude-from', dest='exclude_files',
-                               type=argparse.FileType('r'), action='append',
-                               metavar='EXCLUDEFILE', help='read exclude patterns from EXCLUDEFILE, one per line')
-        subparser.add_argument('--exclude-caches', dest='exclude_caches',
-                               action='store_true', default=False,
-                               help='exclude directories that contain a CACHEDIR.TAG file ('
-                                    'http://www.brynosaurus.com/cachedir/spec.html)')
-        subparser.add_argument('--exclude-if-present', dest='exclude_if_present',
-                               metavar='FILENAME', action='append', type=str,
-                               help='exclude directories that contain the specified file')
-        subparser.add_argument('--keep-tag-files', dest='keep_tag_files',
-                               action='store_true', default=False,
-                               help='keep tag files of excluded caches/directories')
+
+        exclude_group = subparser.add_argument_group('Exclusion options')
+        exclude_group.add_argument('-e', '--exclude', dest='excludes',
+                                   type=parse_pattern, action='append',
+                                   metavar="PATTERN", help='exclude paths matching PATTERN')
+        exclude_group.add_argument('--exclude-from', dest='exclude_files',
+                                   type=argparse.FileType('r'), action='append',
+                                   metavar='EXCLUDEFILE', help='read exclude patterns from EXCLUDEFILE, one per line')
+        exclude_group.add_argument('--exclude-caches', dest='exclude_caches',
+                                   action='store_true', default=False,
+                                   help='exclude directories that contain a CACHEDIR.TAG file ('
+                                        'http://www.brynosaurus.com/cachedir/spec.html)')
+        exclude_group.add_argument('--exclude-if-present', dest='exclude_if_present',
+                                   metavar='FILENAME', action='append', type=str,
+                                   help='exclude directories that contain the specified file')
+        exclude_group.add_argument('--keep-tag-files', dest='keep_tag_files',
+                                   action='store_true', default=False,
+                                   help='keep tag files of excluded caches/directories')
+
+        archive_group = subparser.add_argument_group('Archive options')
+        archive_group.add_argument('--comment', dest='comment', metavar='COMMENT', default=None,
+                                   help='add a comment text to the archive')
+        archive_group.add_argument('--timestamp', dest='timestamp',
+                                   type=timestamp, default=None,
+                                   metavar='yyyy-mm-ddThh:mm:ss',
+                                   help='manually specify the archive creation date/time (UTC). '
+                                        'alternatively, give a reference file/directory.')
+        archive_group.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
+                                   type=int, default=300, metavar='SECONDS',
+                                   help='write checkpoint every SECONDS seconds (Default: 300)')
         archive_group.add_argument('-C', '--compression', dest='compression',
         archive_group.add_argument('-C', '--compression', dest='compression',
                                    type=CompressionSpec, default=None, metavar='COMPRESSION',
                                    type=CompressionSpec, default=None, metavar='COMPRESSION',
                                    help='select compression algorithm (and level):\n'
                                    help='select compression algorithm (and level):\n'
@@ -1771,10 +1758,11 @@ class Archiver:
                                         'zlib,0 .. zlib,9 == zlib (with level 0..9),\n'
                                         'zlib,0 .. zlib,9 == zlib (with level 0..9),\n'
                                         'lzma == lzma (default level 6),\n'
                                         'lzma == lzma (default level 6),\n'
                                         'lzma,0 .. lzma,9 == lzma (with level 0..9).')
                                         'lzma,0 .. lzma,9 == lzma (with level 0..9).')
-        subparser.add_argument('--chunker-params', dest='chunker_params',
-                               type=ChunkerParams, default=None,
-                               metavar='CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE',
-                               help='specify the chunker parameters (or "default").')
+        archive_group.add_argument('--chunker-params', dest='chunker_params',
+                                   type=ChunkerParams, default=None,
+                                   metavar='CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE',
+                                   help='specify the chunker parameters (or "default").')
+
         subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
         subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
                                type=location_validator(),
                                type=location_validator(),
                                help='repository/archive to recreate')
                                help='repository/archive to recreate')

+ 8 - 2
borg/testsuite/archiver.py

@@ -800,13 +800,19 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         self.cmd('init', self.repository_location)
         self.cmd('init', self.repository_location)
         self.cmd('create', self.repository_location + '::test1', 'input')
         self.cmd('create', self.repository_location + '::test1', 'input')
         self.cmd('create', '--comment', 'this is the comment', self.repository_location + '::test2', 'input')
         self.cmd('create', '--comment', 'this is the comment', self.repository_location + '::test2', 'input')
+        self.cmd('create', '--comment', '"deleted" comment', self.repository_location + '::test3', 'input')
+        self.cmd('create', '--comment', 'preserved comment', self.repository_location + '::test4', 'input')
         assert 'Comment: \n' in self.cmd('info', self.repository_location + '::test1')
         assert 'Comment: \n' in self.cmd('info', self.repository_location + '::test1')
         assert 'Comment: this is the comment' in self.cmd('info', self.repository_location + '::test2')
         assert 'Comment: this is the comment' in self.cmd('info', self.repository_location + '::test2')
 
 
-        self.cmd('comment', self.repository_location + '::test1', 'added comment')
-        self.cmd('comment', self.repository_location + '::test2', 'modified comment')
+        self.cmd('recreate', self.repository_location + '::test1', '--comment', 'added comment')
+        self.cmd('recreate', self.repository_location + '::test2', '--comment', 'modified comment')
+        self.cmd('recreate', self.repository_location + '::test3', '--comment', '')
+        self.cmd('recreate', self.repository_location + '::test4', '12345')
         assert 'Comment: added comment' in self.cmd('info', self.repository_location + '::test1')
         assert 'Comment: added comment' in self.cmd('info', self.repository_location + '::test1')
         assert 'Comment: modified comment' in self.cmd('info', self.repository_location + '::test2')
         assert 'Comment: modified comment' in self.cmd('info', self.repository_location + '::test2')
+        assert 'Comment: \n' in self.cmd('info', self.repository_location + '::test3')
+        assert 'Comment: preserved comment' in self.cmd('info', self.repository_location + '::test4')
 
 
     def test_delete(self):
     def test_delete(self):
         self.create_regular_file('file1', size=1024 * 80)
         self.create_regular_file('file1', size=1024 * 80)

+ 15 - 20
docs/usage.rst

@@ -356,26 +356,6 @@ Examples
     newname                              Mon, 2016-02-15 19:50:19
     newname                              Mon, 2016-02-15 19:50:19
 
 
 
 
-.. include:: usage/comment.rst.inc
-
-Examples
-~~~~~~~~
-::
-
-    $ borg create --comment "This is a comment" /mnt/backup::archivename ~
-    $ borg info /mnt/backup::archivename
-    Name: archivename
-    Fingerprint: ...
-    Comment: This is a comment
-    ...
-    $ borg comment /mnt/backup::archivename "This is a better comment"
-    $ borg info /mnt/backup::archivename
-    Name: archivename
-    Fingerprint: ...
-    Comment: This is a better comment
-    ...
-
-
 .. include:: usage/list.rst.inc
 .. include:: usage/list.rst.inc
 
 
 Examples
 Examples
@@ -648,6 +628,21 @@ Examples
     $ borg recreate /mnt/backup -e /home/icke/Pictures/drunk_photos
     $ borg recreate /mnt/backup -e /home/icke/Pictures/drunk_photos
 
 
 
 
+    # Change archive comment
+    $ borg create --comment "This is a comment" /mnt/backup::archivename ~
+    $ borg info /mnt/backup::archivename
+    Name: archivename
+    Fingerprint: ...
+    Comment: This is a comment
+    ...
+    $ borg recreate --comment "This is a better comment" /mnt/backup::archivename
+    $ borg info /mnt/backup::archivename
+    Name: archivename
+    Fingerprint: ...
+    Comment: This is a better comment
+    ...
+
+
 Miscellaneous Help
 Miscellaneous Help
 ------------------
 ------------------
 
 

+ 0 - 23
docs/usage/comment.rst.inc

@@ -1,23 +0,0 @@
-.. _borg_comment:
-
-borg comment
-------------
-::
-
-    borg comment <options> ARCHIVE COMMENT
-
-positional arguments
-    ARCHIVE
-        archive to modify
-    COMMENT
-        the new archive comment
-
-`Common options`_
-    |
-
-Description
-~~~~~~~~~~~
-
-This command sets the archive comment.
-
-This results in a different archive ID.

+ 53 - 52
docs/usage/recreate.rst.inc

@@ -4,58 +4,59 @@ borg recreate
 -------------
 -------------
 ::
 ::
 
 
-    usage: borg recreate [-h] [-v] [--debug] [--lock-wait N] [--show-version]
-                         [--show-rc] [--no-files-cache] [--umask M]
-                         [--remote-path PATH] [--list] [--filter STATUSCHARS] [-p]
-                         [-n] [-s] [-e PATTERN] [--exclude-from EXCLUDEFILE]
-                         [--exclude-caches] [--exclude-if-present FILENAME]
-                         [--keep-tag-files] [-C COMPRESSION]
-                         [--chunker-params CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE]
-                         [REPOSITORY_OR_ARCHIVE] [PATH [PATH ...]]
-    
-    Re-create archives
-    
-    positional arguments:
-      REPOSITORY_OR_ARCHIVE
-                            repository/archive to recreate
-      PATH                  paths to recreate; patterns are supported
-    
-    optional arguments:
-      -h, --help            show this help message and exit
-      -v, --verbose, --info
-                            enable informative (verbose) output, work on log level
-                            INFO
-      --debug               enable debug output, work on log level DEBUG
-      --lock-wait N         wait for the lock, but max. N seconds (default: 1).
-      --show-version        show/log the borg version
-      --show-rc             show/log the return code (rc)
-      --no-files-cache      do not load/update the file metadata cache used to
-                            detect unchanged files
-      --umask M             set umask to M (local and remote, default: 0077)
-      --remote-path PATH    set remote path to executable (default: "borg")
-      --list                output verbose list of items (files, dirs, ...)
-      --filter STATUSCHARS  only display items with the given status characters
-      -p, --progress        show progress display while rewriting archives
-      -n, --dry-run         do not change anything
-      -s, --stats           print statistics at end
-      -e PATTERN, --exclude PATTERN
-                            exclude paths matching PATTERN
-      --exclude-from EXCLUDEFILE
-                            read exclude patterns from EXCLUDEFILE, one per line
-      --exclude-caches      exclude directories that contain a CACHEDIR.TAG file
-                            (http://www.brynosaurus.com/cachedir/spec.html)
-      --exclude-if-present FILENAME
-                            exclude directories that contain the specified file
-      --keep-tag-files      keep tag files of excluded caches/directories
-      -C COMPRESSION, --compression COMPRESSION
-                            select compression algorithm (and level): none == no
-                            compression (default), lz4 == lz4, zlib == zlib
-                            (default level 6), zlib,0 .. zlib,9 == zlib (with
-                            level 0..9), lzma == lzma (default level 6), lzma,0 ..
-                            lzma,9 == lzma (with level 0..9).
-      --chunker-params CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE
-                            specify the chunker parameters (or "default").
-    
+    borg recreate <options> REPOSITORY_OR_ARCHIVE PATH
+
+positional arguments
+    REPOSITORY_OR_ARCHIVE
+        repository/archive to recreate
+    PATH
+        paths to recreate; patterns are supported
+
+optional arguments
+    ``--list``
+        | output verbose list of items (files, dirs, ...)
+    ``--filter STATUSCHARS``
+        | only display items with the given status characters
+    ``-p``, ``--progress``
+        | show progress display while rewriting archives
+    ``-n``, ``--dry-run``
+        | do not change anything
+    ``-s``, ``--stats``
+        | print statistics at end
+
+`Common options`_
+    |
+
+Exclusion options
+    ``-e PATTERN``, ``--exclude PATTERN``
+        | exclude paths matching PATTERN
+    ``--exclude-from EXCLUDEFILE``
+        | read exclude patterns from EXCLUDEFILE, one per line
+    ``--exclude-caches``
+        | exclude directories that contain a CACHEDIR.TAG file (http://www.brynosaurus.com/cachedir/spec.html)
+    ``--exclude-if-present FILENAME``
+        | exclude directories that contain the specified file
+    ``--keep-tag-files``
+        | keep tag files of excluded caches/directories
+
+Archive options
+    ``--comment COMMENT``
+        | add a comment text to the archive
+    ``--timestamp yyyy-mm-ddThh:mm:ss``
+        | manually specify the archive creation date/time (UTC). alternatively, give a reference file/directory.
+    ``-c SECONDS``, ``--checkpoint-interval SECONDS``
+        | write checkpoint every SECONDS seconds (Default: 300)
+    ``-C COMPRESSION``, ``--compression COMPRESSION``
+        | select compression algorithm (and level):
+        | none == no compression (default),
+        | lz4 == lz4,
+        | zlib == zlib (default level 6),
+        | zlib,0 .. zlib,9 == zlib (with level 0..9),
+        | lzma == lzma (default level 6),
+        | lzma,0 .. lzma,9 == lzma (with level 0..9).
+    ``--chunker-params CHUNK_MIN_EXP,CHUNK_MAX_EXP,HASH_MASK_BITS,HASH_WINDOW_SIZE``
+        | specify the chunker parameters (or "default").
+
 Description
 Description
 ~~~~~~~~~~~
 ~~~~~~~~~~~