- add `--tags TAG [TAG ...]` option to `borg create` to tag newly created archives.
- validate the tags exactly like `borg tag` does, including checking that any special tags starting with `@` are known `SPECIAL_TAGS`.
- add `test_create_tags` and `test_create_invalid_tags` to ensure proper behavior.
- Added `--hostname` and `--username` command-line options to `borg create`
- Updated Archive to capture and store these explicit values, falling back to system defaults
- Added `test_explicit_hostname_and_username` to verify the functionality
test_with_lock previously relied on a hardcoded timeout
(`time.sleep(4)`) to ensure the first background command acquired
the repository lock before the second command tried to get it. On
extremely slow CI runners, this was sometimes too short, allowing
the second command to falsely acquire the lock.
This commit replaces the arbitrary sleep with true synchronization:
- The first command now blocks indefinitely using `sys.stdin.read()`.
- The test deterministically waits for lock acquisition by reading
`p1.stdout.readline()` which guarantees the lock is held.
- After the second command correctly fails, the first command is
smoothly unblocked and terminated by passing `input=""` to
`p1.communicate()`.
Python's `os.truncate()` on Windows relies on `SetEndOfFile()`, which does
not initialize the extended disk space with zeroes. This means that
trailing sparse holes simply leave uninitialized garbage data at the end
of the file.
During sparse file extraction, when the very last chunk is a sparse hole,
the VDL (Valid Data Length) is not properly advanced by `os.truncate()`.
As a result, reading from the end of the file fetches random disk garbage
instead of zeroes, causing spurious test failures at boundaries (like
2MB or 8MB) depending on what was in the uninitialized disk sectors.
Fix this by tracking trailing holes and manually writing a single `b"\0"`
byte at the end of the file before truncating on Windows. Writing explicit
data forces NTFS to officially advance the VDL and securely zero-fill the
preceding hole space.
Re-enable `test_sparse_file` on Windows.
This adds the `--paths-from-shell-command` option to the `create` command, enabling the use of shell-specific features like pipes and redirection when specifying input paths. Includes related test coverage.
Borg's ArgumentParser (in borg.helpers.argparsing) now subclasses
jsonargparse's ArgumentParser and pre-sets two defaults that every
borg parser uses:
formatter_class = RawDescriptionHelpFormatter
add_help = False
The old code worked around argparse's flat namespace by appending
_maincommand / _midcommand / _subcommand suffixes to every common
option's dest (e.g. log_level_subcommand), then resolving them with
CommonOptions.resolve() after parsing. This polluted config key names
and env var names (BORG_LOG_LEVEL_SUBCOMMAND instead of BORG_LOG_LEVEL).
jsonargparse nests subcommand arguments automatically, so the workaround
is no longer needed. Each parser level now registers common options with
their clean dest name. flatten_namespace() is updated to a two-pass
depth-first walk so the most-specific (innermost) value wins naturally:
borg --info create --debug → log_level = "debug" (subcommand wins)
borg --info create → log_level = "info" (top-level fills gap)
For append-action options (--debug-topic) values from all levels are
merged (outer + inner) to preserve the accumulation behaviour.
Previously, ArgparsePatternAction and ArgparsePatternFileAction
appended recursion roots directly to args.paths. This mixed
CLI positional paths with paths derived from patterns
(e.g., using the `R` root path command in a pattern file),
complicating downstream argument parsing and future jsonargparse
integration.
This commit introduces `args.pattern_roots` as a dedicated list
for these accumulated root paths:
- All argparse definition sites now initialize `pattern_roots=[]` alongside `paths=[]`
- ArgparsePatternAction and ArgparsePatternFileAction write directly to `args.pattern_roots`
- The build_matcher utility accepts both `include_paths` and `pattern_roots` and concatenates them internally
- `create_cmd` iterations explicitly concatenate both lists before processing
This ensures `args.paths` strictly reflects exactly what the
user provided positionally, paving the way for a clean
jsonargparse implementation without regressions in pattern behavior.