mirror of
https://github.com/borgbackup/borg.git
synced 2025-03-06 11:40:31 +00:00
tag: set, add, remove tags
This commit is contained in:
parent
789ffb0c1b
commit
e274860983
9 changed files with 326 additions and 2 deletions
docs
src/borg
98
docs/man/borg-tag.1
Normal file
98
docs/man/borg-tag.1
Normal file
|
@ -0,0 +1,98 @@
|
|||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.
|
||||
.nr rst2man-indent-level 0
|
||||
.
|
||||
.de1 rstReportMargin
|
||||
\\$1 \\n[an-margin]
|
||||
level \\n[rst2man-indent-level]
|
||||
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
-
|
||||
\\n[rst2man-indent0]
|
||||
\\n[rst2man-indent1]
|
||||
\\n[rst2man-indent2]
|
||||
..
|
||||
.de1 INDENT
|
||||
.\" .rstReportMargin pre:
|
||||
. RS \\$1
|
||||
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||
. nr rst2man-indent-level +1
|
||||
.\" .rstReportMargin post:
|
||||
..
|
||||
.de UNINDENT
|
||||
. RE
|
||||
.\" indent \\n[an-margin]
|
||||
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.nr rst2man-indent-level -1
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.TH "BORG-TAG" 1 "2024-10-02" "" "borg backup tool"
|
||||
.SH NAME
|
||||
borg-tag \- Manage tags
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
borg [common options] tag [options] [NAME]
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
Manage archive tags.
|
||||
.sp
|
||||
Borg archives can have a set of tags which can be used for matching archives.
|
||||
.sp
|
||||
You can set the tags to a specific set of tags or you can add or remove
|
||||
tags from the current set of tags.
|
||||
.SH OPTIONS
|
||||
.sp
|
||||
See \fIborg\-common(1)\fP for common options of Borg commands.
|
||||
.SS arguments
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B NAME
|
||||
specify the archive name
|
||||
.UNINDENT
|
||||
.SS optional arguments
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.BI \-\-set \ TAG
|
||||
set tags (can be given multiple times)
|
||||
.TP
|
||||
.BI \-\-add \ TAG
|
||||
add tags (can be given multiple times)
|
||||
.TP
|
||||
.BI \-\-remove \ TAG
|
||||
remove tags (can be given multiple times)
|
||||
.UNINDENT
|
||||
.SS Archive filters
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.BI \-a \ PATTERN\fR,\fB \ \-\-match\-archives \ PATTERN
|
||||
only consider archives matching all patterns. see \(dqborg help match\-archives\(dq.
|
||||
.TP
|
||||
.BI \-\-sort\-by \ KEYS
|
||||
Comma\-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp
|
||||
.TP
|
||||
.BI \-\-first \ N
|
||||
consider first N archives after other filters were applied
|
||||
.TP
|
||||
.BI \-\-last \ N
|
||||
consider last N archives after other filters were applied
|
||||
.TP
|
||||
.BI \-\-oldest \ TIMESPAN
|
||||
consider archives between the oldest archive\(aqs timestamp and (oldest + TIMESPAN), e.g. 7d or 12m.
|
||||
.TP
|
||||
.BI \-\-newest \ TIMESPAN
|
||||
consider archives between the newest archive\(aqs timestamp and (newest \- TIMESPAN), e.g. 7d or 12m.
|
||||
.TP
|
||||
.BI \-\-older \ TIMESPAN
|
||||
consider archives older than (now \- TIMESPAN), e.g. 7d or 12m.
|
||||
.TP
|
||||
.BI \-\-newer \ TIMESPAN
|
||||
consider archives newer than (now \- TIMESPAN), e.g. 7d or 12m.
|
||||
.UNINDENT
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fIborg\-common(1)\fP
|
||||
.SH AUTHOR
|
||||
The Borg Collective
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
|
@ -51,8 +51,9 @@ Usage
|
|||
usage/create
|
||||
usage/extract
|
||||
usage/check
|
||||
usage/rename
|
||||
usage/list
|
||||
usage/tag
|
||||
usage/rename
|
||||
usage/diff
|
||||
usage/delete
|
||||
usage/prune
|
||||
|
|
1
docs/usage/tag.rst
Normal file
1
docs/usage/tag.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: tag.rst.inc
|
93
docs/usage/tag.rst.inc
Normal file
93
docs/usage/tag.rst.inc
Normal file
|
@ -0,0 +1,93 @@
|
|||
.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit!
|
||||
|
||||
.. _borg_tag:
|
||||
|
||||
borg tag
|
||||
--------
|
||||
.. code-block:: none
|
||||
|
||||
borg [common options] tag [options] [NAME]
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. class:: borg-options-table
|
||||
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| **positional arguments** |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``NAME`` | specify the archive name |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| **optional arguments** |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--set TAG`` | set tags (can be given multiple times) |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--add TAG`` | add tags (can be given multiple times) |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--remove TAG`` | remove tags (can be given multiple times) |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| .. class:: borg-common-opt-ref |
|
||||
| |
|
||||
| :ref:`common_options` |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| **Archive filters** — Archive filters can be applied to repository targets. |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``-a PATTERN``, ``--match-archives PATTERN`` | only consider archives matching all patterns. see "borg help match-archives". |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--sort-by KEYS`` | Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--first N`` | consider first N archives after other filters were applied |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--last N`` | consider last N archives after other filters were applied |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--oldest TIMESPAN`` | consider archives between the oldest archive's timestamp and (oldest + TIMESPAN), e.g. 7d or 12m. |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--newest TIMESPAN`` | consider archives between the newest archive's timestamp and (newest - TIMESPAN), e.g. 7d or 12m. |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--older TIMESPAN`` | consider archives older than (now - TIMESPAN), e.g. 7d or 12m. |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
| | ``--newer TIMESPAN`` | consider archives newer than (now - TIMESPAN), e.g. 7d or 12m. |
|
||||
+-----------------------------------------------------------------------------+----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script type='text/javascript'>
|
||||
$(document).ready(function () {
|
||||
$('.borg-options-table colgroup').remove();
|
||||
})
|
||||
</script>
|
||||
|
||||
.. only:: latex
|
||||
|
||||
NAME
|
||||
specify the archive name
|
||||
|
||||
|
||||
optional arguments
|
||||
--set TAG set tags (can be given multiple times)
|
||||
--add TAG add tags (can be given multiple times)
|
||||
--remove TAG remove tags (can be given multiple times)
|
||||
|
||||
|
||||
:ref:`common_options`
|
||||
|
|
||||
|
||||
Archive filters
|
||||
-a PATTERN, --match-archives PATTERN only consider archives matching all patterns. see "borg help match-archives".
|
||||
--sort-by KEYS Comma-separated list of sorting keys; valid keys are: timestamp, archive, name, id, tags, host, user; default is: timestamp
|
||||
--first N consider first N archives after other filters were applied
|
||||
--last N consider last N archives after other filters were applied
|
||||
--oldest TIMESPAN consider archives between the oldest archive's timestamp and (oldest + TIMESPAN), e.g. 7d or 12m.
|
||||
--newest TIMESPAN consider archives between the newest archive's timestamp and (newest - TIMESPAN), e.g. 7d or 12m.
|
||||
--older TIMESPAN consider archives older than (now - TIMESPAN), e.g. 7d or 12m.
|
||||
--newer TIMESPAN consider archives newer than (now - TIMESPAN), e.g. 7d or 12m.
|
||||
|
||||
|
||||
Description
|
||||
~~~~~~~~~~~
|
||||
|
||||
Manage archive tags.
|
||||
|
||||
Borg archives can have a set of tags which can be used for matching archives.
|
||||
|
||||
You can set the tags to a specific set of tags or you can add or remove
|
||||
tags from the current set of tags.
|
|
@ -89,6 +89,7 @@ from .repo_delete_cmd import RepoDeleteMixIn
|
|||
from .repo_list_cmd import RepoListMixIn
|
||||
from .repo_space_cmd import RepoSpaceMixIn
|
||||
from .serve_cmd import ServeMixIn
|
||||
from .tag_cmd import TagMixIn
|
||||
from .tar_cmds import TarMixIn
|
||||
from .transfer_cmd import TransferMixIn
|
||||
from .version_cmd import VersionMixIn
|
||||
|
@ -120,6 +121,7 @@ class Archiver(
|
|||
RepoListMixIn,
|
||||
RepoSpaceMixIn,
|
||||
ServeMixIn,
|
||||
TagMixIn,
|
||||
TarMixIn,
|
||||
TransferMixIn,
|
||||
VersionMixIn,
|
||||
|
@ -359,6 +361,7 @@ class Archiver(
|
|||
self.build_parser_rename(subparsers, common_parser, mid_common_parser)
|
||||
self.build_parser_repo_space(subparsers, common_parser, mid_common_parser)
|
||||
self.build_parser_serve(subparsers, common_parser, mid_common_parser)
|
||||
self.build_parser_tag(subparsers, common_parser, mid_common_parser)
|
||||
self.build_parser_tar(subparsers, common_parser, mid_common_parser)
|
||||
self.build_parser_transfer(subparsers, common_parser, mid_common_parser)
|
||||
self.build_parser_version(subparsers, common_parser, mid_common_parser)
|
||||
|
|
95
src/borg/archiver/tag_cmd.py
Normal file
95
src/borg/archiver/tag_cmd.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
import argparse
|
||||
|
||||
from ._common import with_repository, define_archive_filters_group
|
||||
from ..archive import Archive
|
||||
from ..constants import * # NOQA
|
||||
from ..helpers import bin_to_hex, archivename_validator, tag_validator
|
||||
from ..manifest import Manifest
|
||||
|
||||
from ..logger import create_logger
|
||||
|
||||
logger = create_logger()
|
||||
|
||||
|
||||
class TagMixIn:
|
||||
@with_repository(cache=True, compatibility=(Manifest.Operation.WRITE,))
|
||||
def do_tag(self, args, repository, manifest, cache):
|
||||
"""Manage tags"""
|
||||
|
||||
def tags_set(tags):
|
||||
"""return a set of tags, removing empty tags"""
|
||||
return set(tag for tag in tags if tag)
|
||||
|
||||
if args.name:
|
||||
archive_infos = [manifest.archives.get_one([args.name])]
|
||||
else:
|
||||
archive_infos = manifest.archives.list_considering(args)
|
||||
|
||||
for archive_info in archive_infos:
|
||||
archive = Archive(manifest, archive_info.id, cache=cache)
|
||||
if args.set_tags:
|
||||
archive.tags = tags_set(args.set_tags)
|
||||
if args.add_tags:
|
||||
archive.tags |= tags_set(args.add_tags)
|
||||
if args.remove_tags:
|
||||
archive.tags -= tags_set(args.remove_tags)
|
||||
old_id = archive.id
|
||||
archive.set_meta("tags", list(sorted(archive.tags)))
|
||||
if old_id != archive.id:
|
||||
manifest.archives.delete_by_id(old_id)
|
||||
print(
|
||||
f"id: {bin_to_hex(old_id):.8} -> {bin_to_hex(archive.id):.8}, "
|
||||
f"tags: {','.join(sorted(archive.tags))}."
|
||||
)
|
||||
|
||||
def build_parser_tag(self, subparsers, common_parser, mid_common_parser):
|
||||
from ._common import process_epilog
|
||||
|
||||
tag_epilog = process_epilog(
|
||||
"""
|
||||
Manage archive tags.
|
||||
|
||||
Borg archives can have a set of tags which can be used for matching archives.
|
||||
|
||||
You can set the tags to a specific set of tags or you can add or remove
|
||||
tags from the current set of tags.
|
||||
"""
|
||||
)
|
||||
subparser = subparsers.add_parser(
|
||||
"tag",
|
||||
parents=[common_parser],
|
||||
add_help=False,
|
||||
description=self.do_tag.__doc__,
|
||||
epilog=tag_epilog,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
help="tag archives",
|
||||
)
|
||||
subparser.set_defaults(func=self.do_tag)
|
||||
subparser.add_argument(
|
||||
"--set",
|
||||
dest="set_tags",
|
||||
metavar="TAG",
|
||||
type=tag_validator,
|
||||
action="append",
|
||||
help="set tags (can be given multiple times)",
|
||||
)
|
||||
subparser.add_argument(
|
||||
"--add",
|
||||
dest="add_tags",
|
||||
metavar="TAG",
|
||||
type=tag_validator,
|
||||
action="append",
|
||||
help="add tags (can be given multiple times)",
|
||||
)
|
||||
subparser.add_argument(
|
||||
"--remove",
|
||||
dest="remove_tags",
|
||||
metavar="TAG",
|
||||
type=tag_validator,
|
||||
action="append",
|
||||
help="remove tags (can be given multiple times)",
|
||||
)
|
||||
define_archive_filters_group(subparser)
|
||||
subparser.add_argument(
|
||||
"name", metavar="NAME", nargs="?", type=archivename_validator, help="specify the archive name"
|
||||
)
|
|
@ -34,7 +34,7 @@ from .parseformat import format_file_size, parse_file_size, FileSize, parse_stor
|
|||
from .parseformat import sizeof_fmt, sizeof_fmt_iec, sizeof_fmt_decimal, Location, text_validator
|
||||
from .parseformat import format_line, replace_placeholders, PlaceholderError, relative_time_marker_validator
|
||||
from .parseformat import format_archive, parse_stringified_list, clean_lines
|
||||
from .parseformat import location_validator, archivename_validator, comment_validator
|
||||
from .parseformat import location_validator, archivename_validator, comment_validator, tag_validator
|
||||
from .parseformat import BaseFormatter, ArchiveFormatter, ItemFormatter, DiffFormatter, file_status
|
||||
from .parseformat import swidth_slice, ellipsis_truncate
|
||||
from .parseformat import BorgJsonEncoder, basic_json_data, json_print, json_dump, prepare_dump_dict
|
||||
|
|
|
@ -685,6 +685,7 @@ def text_validator(*, name, max_length, min_length=0, invalid_ctrl_chars="\0", i
|
|||
|
||||
|
||||
comment_validator = text_validator(name="comment", max_length=10000)
|
||||
tag_validator = text_validator(name="tag", min_length=0, max_length=10, invalid_chars=" ,$")
|
||||
|
||||
|
||||
def archivename_validator(text):
|
||||
|
|
32
src/borg/testsuite/archiver/tag_cmd.py
Normal file
32
src/borg/testsuite/archiver/tag_cmd.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from ...constants import * # NOQA
|
||||
from . import cmd, generate_archiver_tests, RK_ENCRYPTION
|
||||
|
||||
pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local") # NOQA
|
||||
|
||||
|
||||
def test_tag_set(archivers, request):
|
||||
archiver = request.getfixturevalue(archivers)
|
||||
cmd(archiver, "repo-create", RK_ENCRYPTION)
|
||||
cmd(archiver, "create", "archive", archiver.input_path)
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--set", "aa")
|
||||
assert "tags: aa." in output
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--set", "bb")
|
||||
assert "tags: bb." in output
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--set", "bb", "--set", "aa")
|
||||
assert "tags: aa,bb." in output # sorted!
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--set", "")
|
||||
assert "tags: ." in output # no tags!
|
||||
|
||||
|
||||
def test_tag_add_remove(archivers, request):
|
||||
archiver = request.getfixturevalue(archivers)
|
||||
cmd(archiver, "repo-create", RK_ENCRYPTION)
|
||||
cmd(archiver, "create", "archive", archiver.input_path)
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--add", "aa")
|
||||
assert "tags: aa." in output
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--add", "bb")
|
||||
assert "tags: aa,bb." in output
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--remove", "aa")
|
||||
assert "tags: bb." in output
|
||||
output = cmd(archiver, "tag", "-a", "archive", "--remove", "bb")
|
||||
assert "tags: ." in output
|
Loading…
Add table
Reference in a new issue