2
0
Эх сурвалжийг харах

Merge pull request #847 from Ape/pr/archive_comments

Add archive comments
TW 9 жил өмнө
parent
commit
938302f2f2

+ 15 - 9
borg/archive.py

@@ -180,7 +180,7 @@ class Archive:
     def load(self, id):
         self.id = id
         self.metadata = self._load_meta(self.id)
-        decode_dict(self.metadata, (b'name', b'hostname', b'username', b'time', b'time_end'))
+        decode_dict(self.metadata, (b'name', b'comment', b'hostname', b'username', b'time', b'time_end'))
         self.metadata[b'cmdline'] = [arg.decode('utf-8', 'surrogateescape') for arg in self.metadata[b'cmdline']]
         self.name = self.metadata[b'name']
 
@@ -240,7 +240,7 @@ Number of files: {0.stats.nfiles}'''.format(
         del self.manifest.archives[self.checkpoint_name]
         self.cache.chunk_decref(self.id, self.stats)
 
-    def save(self, name=None, timestamp=None):
+    def save(self, name=None, comment=None, timestamp=None):
         name = name or self.name
         if name in self.manifest.archives:
             raise self.AlreadyExists(name)
@@ -256,6 +256,7 @@ Number of files: {0.stats.nfiles}'''.format(
         metadata = StableDict({
             'version': 1,
             'name': name,
+            'comment': comment,
             'items': self.items_buffer.chunks,
             'cmdline': sys.argv,
             'hostname': socket.gethostname(),
@@ -447,17 +448,22 @@ Number of files: {0.stats.nfiles}'''.format(
             except OSError:
                 pass
 
-    def rename(self, name):
-        if name in self.manifest.archives:
-            raise self.AlreadyExists(name)
+    def set_meta(self, key, value):
         metadata = StableDict(self._load_meta(self.id))
-        metadata[b'name'] = name
+        metadata[key] = value
         data = msgpack.packb(metadata, unicode_errors='surrogateescape')
         new_id = self.key.id_hash(data)
         self.cache.add_chunk(new_id, data, self.stats)
-        self.manifest.archives[name] = {'id': new_id, 'time': metadata[b'time']}
+        self.manifest.archives[self.name] = {'id': new_id, 'time': metadata[b'time']}
         self.cache.chunk_decref(self.id, self.stats)
-        del self.manifest.archives[self.name]
+
+    def rename(self, name):
+        if name in self.manifest.archives:
+            raise self.AlreadyExists(name)
+        oldname = self.name
+        self.name = name
+        self.set_meta(b'name', name)
+        del self.manifest.archives[oldname]
 
     def delete(self, stats, progress=False):
         unpacker = msgpack.Unpacker(use_list=False)
@@ -879,7 +885,7 @@ class ArchiveChecker:
                 archive = StableDict(msgpack.unpackb(data))
                 if archive[b'version'] != 1:
                     raise Exception('Unknown archive metadata version')
-                decode_dict(archive, (b'name', b'hostname', b'username', b'time', b'time_end'))
+                decode_dict(archive, (b'name', b'comment', b'hostname', b'username', b'time', b'time_end'))
                 archive[b'cmdline'] = [arg.decode('utf-8', 'surrogateescape') for arg in archive[b'cmdline']]
                 items_buffer = ChunkBuffer(self.key)
                 items_buffer.write_chunk = add_callback

+ 29 - 1
borg/archiver.py

@@ -262,7 +262,7 @@ class Archiver:
                               args.keep_tag_files, skip_inodes, path, restrict_dev,
                               read_special=args.read_special, dry_run=dry_run)
             if not dry_run:
-                archive.save(timestamp=args.timestamp)
+                archive.save(comment=args.comment, timestamp=args.timestamp)
                 if args.progress:
                     archive.stats.show_progress(final=True)
                 if args.stats:
@@ -628,6 +628,16 @@ class Archiver:
         cache.commit()
         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)
     def do_delete(self, args, repository, manifest, key, cache):
         """Delete an existing repository or archive"""
@@ -735,6 +745,7 @@ class Archiver:
         stats = archive.calc_stats(cache)
         print('Name:', archive.name)
         print('Fingerprint: %s' % hexlify(archive.id).decode('ascii'))
+        print('Comment:', archive.metadata.get(b'comment', ''))
         print('Hostname:', archive.metadata[b'hostname'])
         print('Username:', archive.metadata[b'username'])
         print('Time (start): %s' % format_time(to_localtime(archive.ts)))
@@ -1179,6 +1190,8 @@ class Archiver:
                                           formatter_class=argparse.RawDescriptionHelpFormatter,
                                           help='create backup')
         subparser.set_defaults(func=self.do_create)
+        subparser.add_argument('--comment', dest='comment', metavar='COMMENT', default='',
+                               help='add a comment text to the archive')
         subparser.add_argument('-s', '--stats', dest='stats',
                                action='store_true', default=False,
                                help='print statistics for the created archive')
@@ -1356,6 +1369,21 @@ class Archiver:
                                type=archivename_validator(),
                                help='the new archive name to use')
 
+        comment_epilog = textwrap.dedent("""
+        This command sets the archive comment.
+        """)
+        subparser = subparsers.add_parser('comment', parents=[common_parser],
+                                          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("""
         This command deletes an archive from the repository or the complete repository.
         Disk space is reclaimed accordingly. If you delete the complete repository, the

+ 13 - 0
borg/testsuite/archiver.py

@@ -754,6 +754,19 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         self.assert_in('test.3', manifest.archives)
         self.assert_in('test.4', manifest.archives)
 
+    def test_comment(self):
+        self.create_regular_file('file1', size=1024 * 80)
+        self.cmd('init', self.repository_location)
+        self.cmd('create', self.repository_location + '::test1', 'input')
+        self.cmd('create', '--comment', 'this is the comment', self.repository_location + '::test2', 'input')
+        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')
+
+        self.cmd('comment', self.repository_location + '::test1', 'added comment')
+        self.cmd('comment', self.repository_location + '::test2', 'modified comment')
+        assert 'Comment: added comment' in self.cmd('info', self.repository_location + '::test1')
+        assert 'Comment: modified comment' in self.cmd('info', self.repository_location + '::test2')
+
     def test_delete(self):
         self.create_regular_file('file1', size=1024 * 80)
         self.create_regular_file('dir2/file2', size=1024 * 80)

+ 21 - 1
docs/usage.rst

@@ -335,6 +335,26 @@ Examples
     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
 
 Examples
@@ -825,4 +845,4 @@ for e.g. regular pruning.
 
 Further protections can be implemented, but are outside of Borgs scope. For example,
 file system snapshots or wrapping ``borg serve`` to set special permissions or ACLs on
-new data files.
+new data files.

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

@@ -0,0 +1,35 @@
+.. _borg_comment:
+
+borg comment
+------------
+::
+
+    usage: borg comment [-h] [-v] [--debug] [--lock-wait N] [--show-version]
+                        [--show-rc] [--no-files-cache] [--umask M]
+                        [--remote-path PATH]
+                        ARCHIVE COMMENT
+    
+    Set the archive comment
+    
+    positional arguments:
+      ARCHIVE               archive to rename
+      COMMENT               the new archive comment
+    
+    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")
+    
+Description
+~~~~~~~~~~~
+
+This command sets the archive comment.

+ 2 - 1
docs/usage/create.rst.inc

@@ -6,7 +6,7 @@ borg create
 
     usage: borg create [-h] [-v] [--debug] [--lock-wait N] [--show-version]
                        [--show-rc] [--no-files-cache] [--umask M]
-                       [--remote-path PATH] [-s] [-p] [--list]
+                       [--remote-path PATH] [--comment COMMENT] [-s] [-p] [--list]
                        [--filter STATUSCHARS] [-e PATTERN]
                        [--exclude-from EXCLUDEFILE] [--exclude-caches]
                        [--exclude-if-present FILENAME] [--keep-tag-files]
@@ -36,6 +36,7 @@ borg create
                             detect unchanged files
       --umask M             set umask to M (local and remote, default: 0077)
       --remote-path PATH    set remote path to executable (default: "borg")
+      --comment COMMENT     add a comment text to the archive
       -s, --stats           print statistics for the created archive
       -p, --progress        show progress display while creating the archive,
                             showing Original, Compressed and Deduplicated sizes,