diff --git a/borg/archive.py b/borg/archive.py index db7f906ba..80b4fd1af 100644 --- a/borg/archive.py +++ b/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(), @@ -884,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 diff --git a/borg/archiver.py b/borg/archiver.py index 81ce565c2..38446e49a 100644 --- a/borg/archiver.py +++ b/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 diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index dc50d0837..d256cd2aa 100644 --- a/borg/testsuite/archiver.py +++ b/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) diff --git a/docs/usage.rst b/docs/usage.rst index a81281045..255456477 100644 --- a/docs/usage.rst +++ b/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. \ No newline at end of file +new data files. diff --git a/docs/usage/comment.rst.inc b/docs/usage/comment.rst.inc new file mode 100644 index 000000000..47af3cf0d --- /dev/null +++ b/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. diff --git a/docs/usage/create.rst.inc b/docs/usage/create.rst.inc index fee55218f..098c023bc 100644 --- a/docs/usage/create.rst.inc +++ b/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,