borg/docs/man
Kazuki Kanda 57538db831
known-repos: new command to list locally known repositories, fixes #10453
* known-repos: new command to list locally known repositories

Lists repositories this local Borg installation has state for (security
info and/or a local cache) without accessing any repository, local or
remote. Scans both the current borg2 security/cache directories and,
separately, the legacy borg 1.x security/cache directories (which can be
different default locations, e.g. on macOS).

Does not follow symlinks anywhere in the scanned trees (repo-id
directories, or the location/key-type files inside them), since borg
only ever writes plain directories and regular files there.

Key-type labels are derived from the key classes themselves
(LEGACY_KEY_TYPES + AVAILABLE_KEY_TYPES), matching how "borg repo-info"
names crypto suites, rather than a separately hand-maintained table.

dir_size() pushes each directory handle (root, and any subdirectory
opened while walking the cache tree) onto its explicit stack before
calling list_names() on it, not after, so cleanup can always close it
even if list_names() raises something other than OSError (it only
catches OSError itself) - e.g. KeyboardInterrupt. Covered by two unit
tests that monkeypatch list_names() to raise and then check the fd via
os.fstat().

has_cache and has_security_info are tri-state (True/False/None, not a
plain bool): an inspection failure (e.g. EACCES on a repo-id directory,
or on the security/cache root itself, including a root that opens but
fails during its own listing) is reported as unknown rather than being
collapsed into a false "no". A repo-id directory that could be listed
but not opened/classified is still included in the results with its
fields set to unknown, instead of silently disappearing from the
output. The JSON/text field for which state-directory layout a
repository was discovered under is named "layout" (values "borg2" /
"legacy" / null), not "version", so it cannot be confused with Borg's
actual repository format version (see "borg repo-info").

Adds docs/usage/known-repos.rst{,.inc} and the usage.rst toctree entry, and
the generated man page.

* known-repos: simplify after maintainer review, drop leftover defenses

Rewrite the scanner around ordinary OS file access instead of extra
symlink/size defenses, collapse duplicated scan/table/test logic, and
surface real inspection failures (ENOTDIR, unparseable cache config)
instead of treating them as absence.

Also forward create=False through legacy get_keys_dir(), refresh the
usage example, keep tests to the remaining behavioral surface, and
shorten a few overly verbose test docstrings.

* known-repos: fix black formatting, restore generated usage/man docs

black wants the process_epilog("""...""") call's opening triple-quote on
its own line and the closing paren on a separate line.

docs/usage/known-repos.rst still includes known-repos.rst.inc, which a
previous commit deleted along with the generated man page - this broke
the docs build (InputError: no such file). Regenerate both with
scripts/make.py build_usage/build_man, following the convention other
recently added commands (copy, find) already use.

* known-repos: address second-review minor points

- location_mismatch now compares the security location and cache
  previous_location through normalize_local_path(), the same way
  assert_location_matches() does, so an NFC vs. NFD unicode-form
  difference alone isn't flagged as a mismatch on macOS (#2913).
  Added a regression test for it.
- The table no longer truncates the repository id to 15 chars - users
  acting on an entry (e.g. removing a stale cache dir) need the full id.
  Strengthened the existing test assertion to match. Updated the
  epilog and the usage example accordingly (full id was previously
  JSON-only), and regenerated docs/usage/known-repos.rst.inc and
  docs/man/borg-known-repos.1 to match.
- legacy/fs.py: dropped the "(and now also the known-repos command)"
  historical phrasing from the module docstring; the following
  paragraph already lists both callers.
- Epilog: mentioned that a security-only repo gets layout "unknown"
  when BORG_SECURITY_DIR makes the borg2 and legacy security roots
  coincide. Thomas noted the behavior itself is fine either way, just
  worth a brief mention.

* known-repos: address PyPy msgpack warning and Windows encoding failures

Fixes two CI failures reported in maintainer follow-up review:

- PyPy: test_known_repos_without_repo_argument and
  test_known_repos_multiple_sorted_by_id failed with JSONDecodeError
  because pure-python msgpack emits PURE_PYTHON_MSGPACK_WARNING on PyPy,
  which contaminated exec_cmd() output. Introduce cmd_without_repo()
  helper that filters the warning line identically to cmd() while avoiding
  --repo injection, as suggested.
- Windows: test_known_repos_location_mismatch_ignores_nfc_nfd_difference
  crashed with UnicodeEncodeError writing NFD path in cp1252. Skip the NFD
  test on Windows, as suggested.
- Windows/scanner: read state files using default locale encoding matching
  borg's SaveFile/SyncFile implementation, dropping explicit encoding="utf-8",
  as suggested.

* known-repos: use security location mtime + cache newest-file mtime for last_written

PR #10497 (sealed files cache) removed CacheConfig.save(), so on
current master the borg2 cache config file's mtime stays at cache
creation time and no longer tracks last use.

last_written is now the newer of:
- the security location file's mtime (rewritten by Cache.close() via
  security_manager.save() on every run; also gives security-only
  repos a value)
- the newest mtime of the regular files below the cache dir, tracked
  during the existing cache_stats() walk that already computes size

The JSON field is renamed from cache_config_mtime to last_written.
Epilog/docs/man page say where the timestamp comes from and say
"regular file" to match what the code tracks. The long run-on epilog
sentence is split into shorter sentences.

Tests assert which source wins (location-newer, cache-newer including
a nested file) and the cache-only case now checks the exact mtime
instead of only non-null.
2026-11-01 18:21:55 +01:00
..
borg-analyze.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-benchmark-cpu.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-benchmark-crud.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-benchmark.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-break-lock.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-check.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-common.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-compact.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-completion.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-compression.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-config.1 build_usage / build_man 2024-07-19 20:40:15 +02:00
borg-copy.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-create.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-delete.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-diff.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-environment.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-export-tar.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-extract.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-find.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-import-tar.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-info.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-add.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-change-algorithm.1 build_usage build_man 2022-06-26 13:56:33 +02:00
borg-key-change-location.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-change-passphrase.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-export.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-import.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-list.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key-migrate-to-repokey.1 build_man 2022-02-19 18:48:13 +01:00
borg-key-remove.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-key.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-known-repos.1 known-repos: new command to list locally known repositories, fixes #10453 2026-11-01 18:21:55 +01:00
borg-list.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-match-archives.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-mount.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-patterns.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-placeholders.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-prune.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-recreate.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-rename.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-repo-compress.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-repo-create.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-repo-delete.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-repo-info.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-repo-list.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-repo-space.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-serve.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-tag.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-transfer.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-umount.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-undelete.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-upgrade.1 Docs grammar fixes 2022-12-29 22:26:54 +00:00
borg-version.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-webdav.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg-with-lock.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borg.1 build_usage build_man 2026-09-27 23:02:35 +02:00
borgfs.1 build_usage build_man 2026-09-27 23:02:35 +02:00