mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-09 00:32:37 -04:00
Add archive comments
- Archives now have a new metadata field 'comment' - 'info' command shows a comment if it's present - 'create' command now has option '--comment' for adding comments to archives. - A new command 'comment' is added for modifying the comments on existing archives. Resolves #842.
This commit is contained in:
parent
7e3849367b
commit
327c7219df
6 changed files with 104 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
docs/usage/comment.rst.inc
Normal file
35
docs/usage/comment.rst.inc
Normal file
|
|
@ -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.
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue