Repository navigation
Catalogue and download private data from SSH, HTTPS, WebDAV and S3 servers - #62
Merged
denniswu28 merged 55 commits intoOct 21, 2026
Merged
denniswu28 merged 55 commits into
denniswu28 merged 55 commits into
Conversation
Remote add and set-config --remote-url now refuse URLs carrying a password, an HTTP(S) username or token, a query string or a fragment before any network access, so no secret reaches config.yml. Clone refuses a source URL with a password or an HTTP(S) username before creating anything, since Git would save it in .hm/.git/config. The errors do not repeat the URL. SSH usernames remain allowed. The private data guide says where credentials belong instead. Ported from e4e8376 on feat/private-data-v2; clone keeps codex's split into clone_catalog and _prepare_catalog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finish discovery before staging and report empty selections through the API and CLI. Keep the regression tests and user guidance with this behavior. Prerequisites: Existing remote discovery and catalogue staging APIs. Validation: whole-suite collection, CLI help, Ruff and focused tests (86 passed in 5.53s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 8bfc7da51e5db96dc924b516071b51d2eb727623 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Select compatible client options and validate keepalive, compression and session settings together with their transport tests, CI jobs and documentation. Prerequisites: Existing SSH transport and version-one auth profiles. Validation: whole-suite collection, CLI help, Ruff and focused tests (131 passed in 17.90s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 78c65daee229930eaba8cfb5dde6fefdd5edd965 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Persist published or server checksum provenance, migrate the shared catalogue predicate and update every importer in the same revision. Prerequisites: RemoteEntry metadata and repository state. Validation: whole-suite collection, CLI help, Ruff and focused tests (122 passed in 5.39s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 7f077ea3b1ec1edf01a09ba62caddae3a09c67ea Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Prepare source validation, path routing, legacy state migration and clone validation before exposing multiple-source additions. Prerequisites: Shared remote catalogue detection and RemoteSpec. Validation: whole-suite collection, CLI help, Ruff and focused tests (103 passed in 12.46s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 1b8dea8285a7d3db07cf7ea0960dac27033d81f9 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expose source selections only with complete add, configuration, plan and download behavior. Keep collision handling, pruning, CLI callers and lifecycle tests together. Prerequisites: Named source model, migration helpers and clone validation. Validation: whole-suite collection, CLI help, Ruff and focused tests (309 passed in 32.07s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: ce23eb85ba06f04fb16568e130ec96f88c4a80ef Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Provide streaming multi-digest verification and complete no-overwrite publication with the shared hard-link fallback constants needed by the cache. Prerequisites: Existing streaming checksum and temporary-file utilities. Validation: whole-suite collection, CLI help, Ruff and focused tests (24 passed in 2.14s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: d25f0db3da0770e4a998a18d0cdfb7d98a061d53 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Batch literal NUL-separated version paths and avoid rewriting unchanged records. Preserve normal catalogue save behavior while preparing bulk downloaded hash staging. Prerequisites: Version record serialization and source-aware catalogue state. Validation: whole-suite collection, CLI help, Ruff and focused tests (95 passed in 16.14s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 4121651849014a5c8827707155fce742a295efaf Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Introduce temporary cache files, read-only objects, hard-link placement, verified copy fallback and stale temporary cleanup. Repositories share the cache across their data worktrees. Prerequisites: Complete no-overwrite publication and object verification helpers. Validation: whole-suite collection, CLI help, Ruff and focused tests (71 passed in 14.73s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 82fb4aa994ff305aa4ff013ffb94b03b8ff6bb71 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reject truncated or oversized transfers even without a published digest. Carry known SHA-1 metadata through complete plan and transfer functions and update their callers and regression tests. Prerequisites: Multi-digest hashing and routed DownloadItem values. Validation: whole-suite collection, CLI help, Ruff and focused tests (176 passed in 7.19s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 1f6dc369acae2eb56e4d047ea0975a130eb21bbc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expose the cache workflow with verification, placement, state saving, interruption recovery and retry behavior complete. Include cached plan counts, dry-run output, CLI reporting and the user guide at the first usable revision. Prerequisites: Cache lifecycle operations, verified downloads, source routing and batched state persistence. Validation: whole-suite collection, CLI help, Ruff and focused tests (320 passed in 29.79s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 8154f0da23b57860f5ea5a39b80c9e306ac281ae Replay-Source: 9407bbb69d80b8a1f24ea3fa0980fccc00fe8ca0 Replay-Source: 057769766822b2c2b446991c8e9dd48fe530e9d3 Replay-Source: f26c74c8af2e225440afb9a858d9b24bbb9526a5 Replay-Source: 6ce26797e782ac1091efb923457a6a7b024ba6a2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the complete secret reference and resolution layer with its independent tests before auth profiles consume it. Values remain redacted and helper errors do not echo secret output. Prerequisites: Existing transport configuration and capability errors. Validation: whole-suite collection, CLI help, Ruff and focused tests (9 passed in 0.16s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: dee99043bd5e0fb5340599f090141353c5b2bf02 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add version-two SSH, HTTP and S3 profiles, host and endpoint bindings, ownership validation and operation-context resolution. Preserve version-one SSH behavior and complete profile tests. Prerequisites: Secret references and existing SSH settings. Validation: whole-suite collection, CLI help, Ruff and focused tests (156 passed in 7.13s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: dee99043bd5e0fb5340599f090141353c5b2bf02 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve credentials before requests, send them only to permitted hosts and explain authentication failures. Exercise discovery, downloads and cross-server redirects using loopback HTTP fixtures. Prerequisites: Typed HTTP profiles and backend auth resolution. Validation: whole-suite collection, CLI help, Ruff and focused tests (154 passed in 10.21s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: b22a47ae6c57b87e2db92acef882f1d2801b5675 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Introduce PROPFIND listing, metadata limits, checksum provenance, URL shorthand and backend selection as one usable backend. Reuse HTTP authentication and test complete loopback catalogue/download flows. Prerequisites: Authenticated HTTP requests, checksum provenance and backend registry. Validation: whole-suite collection, CLI help, Ruff and focused tests (143 passed in 13.79s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: 78635463da79b28a302570546a89b36ed7d45f99 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add S3 URL validation, paginated listings, whole-object digests, profile credentials and streaming transfers together with optional packaging dependencies, moto tests and CI. Prerequisites: Typed S3 profiles, multi-source routing and complete cache downloads. Validation: whole-suite collection, CLI help, Ruff and focused tests (92 passed in 3.15s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: c3ee60484ce64077198180ac86f116cbc86de76c Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Carry server-checksum selection through source persistence, discovery, SSH batching, API and CLI in one change. Preserve capability errors and test real disposable SSH and SFTP-only accounts. Prerequisites: Source metadata, checksum provenance and compatible SSH transport. Validation: whole-suite collection, CLI help, Ruff and focused tests (121 passed in 16.91s). Checks ran in an isolated tracked-tree snapshot; no live data services. Replay-Source: d02603c2bcd89bb32040650e28af068e6927b5be Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on notebook Introduce the final Python demo with a disposable local HTTP dataset, catalogue selections, cached downloads and staged SHA-1s. Include the notebook runner and README at the same revision; keep account-dependent examples illustrative. Prerequisites: Multiple sources, complete cache workflow, authentication and backend APIs. Validation: whole-suite collection, CLI help, CI-scoped Ruff and focused tests (4 passed in 36.05s). Checks ran in an isolated tracked-tree snapshot; no live data services. Broad Ruff reproduces the frozen source notebook E501 (89 > 88). Preserve that source line for exact tree fidelity. Replay-Source: 4b2dd1503a4dbd013308a292ed2fbaada8fd81d7 Replay-Source: d536fea66391d5df222118d2c449f45504652b11 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Downloads and committed local files now share .hm/objects, as git keeps all content in one object database, instead of a separate .hm/cache that could drift from it. Every object is read-only; checkout restores writable copies of local files, while downloads are still hard-linked read-only. The download path for callers without a store is removed. It had drifted from the store path: it discarded each file's SHA-1, reported different keys and left .part files behind. Its merging of repeated plan items now applies to every download. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DownloadPlan.cached_items becomes stored_items and the result key from_cache becomes from_store. Plan summaries and CLI messages name .hm/objects instead of a cache that no longer exists. These fields were added on this branch and never released. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A download is hard-linked to its object, so making it writable and editing it in place, or writing to it as root, also changed the object. Planning then reported the file as already downloaded, and later downloads linked the changed bytes into new paths as a success. Each object now has a record of its size and modification time in objects/stat/, as git's index and git-annex's inode cache keep for tracked files. A link, or an object about to be placed, is trusted while it matches the record. Otherwise the object is hashed: a touched object is recorded again, and a changed one is discarded and downloaded afresh. The edited file is reported as a conflict, and hm status lists it as modified without hashing anything. Objects stored before records existed are trusted once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows reached the catalogue through several writers that each formatted values
differently. Remote adds wrote parsed fields as JSON numbers that later saves
turned into strings, so adding a source rewrote every other source's records.
Re-adds compared "123.0" and "nan" against "123" and "", which dropped known
SHA-1s. hm add PATTERN kept "005" while hm add PATH and set-config --fmt
stored "5". data.tsv kept insertion order and the platform's line endings,
while restore wrote sorted LF text.
save_state now writes every row through normalize_rows: text values, missing
values as "", whole-number floats without ".0", sorted by path, LF endings.
That is the form load_state returns, so saving a loaded catalogue changes
nothing. Field values are read as the text of the name everywhere, local and
remote, as hm add PATTERN always did: a remote "{day:03d}" field now stores
"002" rather than "2".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every save wrote the catalogue twice: as data.tsv and as one versions/ JSON file per row. The two could disagree. Loading read versions/ from the index, download selection read data.tsv from disk, and unstaging or a rename detected by git could leave them different. Each row also cost a file write on every save and a blob read on every load, and versions/ names collided on case-insensitive filesystems. data.tsv, sorted by path with one line per row, is now the only record, so git still shows each row's changes on its own line. The first save in an existing repository stops tracking versions/ and versions.yml, which older commits keep and load_state still reads. Unstaging rows swaps them in the catalogue from HEAD and, when nothing else changed, restores HEAD's exact data.tsv; the copy on disk stays as it was. Downloads select rows from the loaded catalogue rather than the file on disk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…umns
is_remote_catalog took any "source" column as proof of a remote catalogue.
A local pattern with a {source} field, the config template's own example,
was therefore treated as remote: hm commit stored none of its files, and
checkout restored none. An older remote catalogue, recognized by a path
column without sha1, became indistinguishable from local files once a
download added that column; hm commit then failed with KeyError: 'sha1' and
hm add URL was refused.
A catalogue is remote when config.yml lists its sources, or while it keeps
the older shape. Downloads record an older catalogue's source before adding
SHA-1s, as hm add URL does; one naming several remotes keeps its shape and
the download says why no SHA-1s were recorded. Filename patterns may no
longer name fields after the catalogue's own columns, which downloads read
as sizes and checksums; remote patterns used to drop such fields silently.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Catalogue paths rejected .hm and .git only as the first folder, so a published or cloned catalogue row such as x/.git/hooks/post-checkout passed validation and a download would plant a git repository inside the data folder. Listings and source prefixes each kept their own copy of the check, one of them case-sensitive. names_repository_metadata now refuses either name at any depth and in any case, and every check uses it. Secrets in URLs were refused when typed into hm add and set-config, but not in configurations loaded or received through a clone, and a clone source could store a ?token= query in .hm/.git/config. RemoteSpec.from_url now refuses HTTP credentials, queries and fragments itself, and clone applies the same rule to every URL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A download stages SHA-1s when it ends, but it saved the catalogue it had loaded when it began. An hm add saved by another command during a long download was erased from the index and disk. A remote add likewise saved the copy it took before a scan that can last minutes. Both now save under an advisory lock on .hm. Download staging reads the catalogue again and fills in only SHA-1 cells, so concurrent changes are kept; any failure there is reported in staging_error, since the files are already in place. An add whose catalogue changed during its scan refuses to save and asks to be run again. Checkout resolves its target once and loads that revision's state, instead of loading it twice and classifying a commit id by the current state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Digests from a plain checksums.txt were catalogued with the algorithm "unknown" and marked published, but downloads read "unknown" as no checksum, so those files were never verified. Each supported algorithm has its own digest length, so discovery now records the algorithm a digest's length names, and downloads infer it for older rows. The algorithm list itself now comes from one place. A catalogue's single unnamed remote was "origin" when routing files but not when selecting a remote, so hm download --remote origin failed and plans reported no remote name. remote_name_of now names remotes for every caller. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A remote catalogue refused checkout with "Commit hallmark state changes" even when nothing was staged: after hm restore --staged, .hm keeps the unstaged change on disk, as git keeps the working copy, and committing could not help. The refusal now names staged changes and how to commit or unstage them, or names the changed .hm files and the git command that discards them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Downloads read responses through Requests' iter_content, which undoes any Content-Encoding. A server that labels .gz files with Content-Encoding: gzip, as Apache's AddEncoding does, delivered them decompressed, so their size and checksums failed. Requests also advertises gzip, inviting servers to compress text files on the fly. Payload requests now ask for the identity encoding and store the response body exactly as sent. Directory listings and other metadata are still decoded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rwise Index pages, checksum files and published catalogues were decoded three ways. HTTP used the encoding Requests guesses, ISO-8859-1 for any text without a declared charset, and replaced undecodable bytes; S3 also replaced them; SSH required UTF-8. The same snapshot cloned over HTTPS got garbled non-ASCII paths, and manifest names silently stopped matching their files. decode_metadata now applies one rule on every backend: the charset the server declares, otherwise UTF-8, and an error rather than replacement characters. Reading an empty S3 metadata object no longer fails with InvalidRange, and the loopback host list is defined once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every remote text, from config.yml to a directory listing, shared one fixed 16 MiB limit. A catalogue table grows by about 250 bytes per file, so a published snapshot of around 70,000 files could not be cloned, and a WebDAV folder of some 30,000 files could not be listed, while git clones, SFTP and S3 listings had no such limit. Documents that list files (listing pages, checksum manifests and catalogue tables) now get a 1 GiB bound; config.yml and meta.yml keep 16 MiB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An S3 listing names an object's SHA checksum algorithm but not the digest, so the backend sent a HEAD request for every listed object, one after another and before filters applied: cataloguing one run of a 100,000-object prefix cost 100,000 sequential requests, on every re-add. Backends can now complete selected entries after filtering (DataBackend.complete_entries). S3 sends those HEAD requests in parallel, within its connection pool, and skips objects that already have an equally strong published checksum. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each command translated its own selection of exceptions. A typo in a hand-edited config.yml, a full disk or a locked git index therefore showed a Python traceback for some commands, including hm info. A command group now reports every error a user can cause or fix in one line, for every command and while opening the repository; anything else is a bug and keeps its traceback. Repo.download transfers only with approved=True, but Repo.clone downloaded without asking unless given an approve callback. Clone now needs the same approval, an approve callback or approved=True, and otherwise keeps the catalogue without files, as when a download is declined. The CLI already asks for approval. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
history._load_revision_yaml and changes.filter_files_in_directory had no callers, and history._parse_data_tsv was used only by its own tests, which now check how Dothm.load_state reads data.tsv instead. "Used by" notes named functions that no longer exist, and _bound_hosts took a name it never used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Click 8.2 and later print "No such option '--force'." where 8.1 printed "No such option: --force", so the exact match failed on every job except Python 3.9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ilure Rocky images lock root, and sshd with UsePAM no refuses a locked account even for key logins, so every SSH test in those jobs failed to connect. A '*' password field unlocks key logins without allowing passwords. The client discards its stderr, so a failed SSH test now prints the test server's log. Rocky 9 ships OpenSSH 9.9, not 8.7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait() can return several finished transfers at once. When one of them raised KeyboardInterrupt, the loop stopped before recording the others, so files already in the worktree were left unstaged (seen on macOS CI). Record the whole batch before raising, and after stopping the workers, record the transfers that finished while they shut down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A source added again during a download can change a row without a checksum: only its size or mtime moves. Staging compared checksums alone, so it recorded the SHA-1 of the old bytes against the new row. Compare the planned size and mtime too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n .hm/objects When a file changed on the server and its source was added again, its row lost the staged SHA-1. If the server published no checksum, the next download took any link into the object store as current, so it kept the stale file and staged its old SHA-1 against the new row. If it did publish one, the download refused and asked the user to delete the file. An existing file whose bytes are an unchanged stored object is now outdated rather than a conflict: the plan lists it, and once approved the new version is linked over it atomically. The old object stays, so checking out an earlier commit places it again without a transfer. A row with no digest cannot vouch for a link, so the file is fetched to compare; identical content is only staged. This also re-fetches such files after a failed save, a case nothing on disk can tell apart. Files the user created or edited stay conflicts, nothing is replaced unless the approved plan listed it, a file edited during the transfer is kept, and a file that two paths differing only in letter case share is never replaced for either of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adding a source again already updated changed rows and dropped their staged SHA-1, but said nothing about it, so a user could not tell that downloaded files were now outdated. hm add now lists downloaded files whose size, modification time or checksum the server reports changed, and points to hm download to replace them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two sources sometimes catalog the same path. It may be one file on two mirrors, or two different files that share a name, and the refusal did not say which. Each clashing path is now labelled same content, different content, or cannot compare, from the checksums and recorded SHA-1 the rows share, their sizes, or the fact that overlapping patterns in one folder name the same server file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A path two sources share may be one file on two mirrors, which needs one row, or two different files with one name, which need both. hm add refused either way and only suggested --prefix. At a terminal it now lists the paths with how their content compares and asks whether to skip them, keeping the catalogued rows, keep both under a folder, or abort. Elsewhere --skip-duplicates skips them. In Python, Repo.add takes on_duplicate: "skip" or a callable that receives the clashes. The choice is not saved. Skipped paths never become the new source's rows, so adding it again never reports them removed; it meets the same clashes and asks again. File and folder clashes are still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M87.fits and m87.fits are two catalogue rows but one file on macOS and Windows, so only one of them can be downloaded there. hm add now warns about such paths, whether they come from one listing or from different sources. It does not refuse them, since they are fine on Linux, and the download check already keeps either file from replacing the other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover a replacement whose temporary name is taken, digests saved without their algorithm, long clash listings, and worktree checks for bare repositories and paths outside the worktree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With root unlocked, the Rocky jobs reached two last failures. Server-side hashing runs xargs, which the rockylinux:8 image lacks, so findutils is installed. Root reads a file whatever its permissions, so the test that a server's unreadable file fails to download cannot run there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in origin/codex/function-atomic-replay-20260928: repeated --fmt with one TSV per format, file: entries for files no format matches, re-sorting when a format joins, the multi-format matcher and hm detect. Sam's storage is adopted. Formats belong to the whole catalogue, in config.yml's data list, and sort the files of every source; a source is now only a selection (remote, folder, prefix, filters). Everything that read data.tsv alone now reads the catalogue's rows in every TSV and its file: entries: re-adding a source replaces them, clashes and changed files are found across them, downloads select and route them, SHA-1s are staged into them, and status and restore --staged cover them. The state loads and saves every TSV its configuration names. The column naming a file's source is now catalog_source, because hm detect names a field "source" for objects such as M87, and formats may use it. file: entries name their source only when there are several. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rt every source Re-adding a source silently removed catalogued files the server no longer lists, which a user may not expect. hm add now lists them and asks whether to remove them or keep their rows; --prune and --keep-missing answer in advance, and Python passes prune=True or False. Without a choice, Repo.add refuses, as it does for resort. When a new format joins, the resort question also offers "all" when other sources have sorted files: their files are sorted again by their recorded paths, without listing their servers. A resort callable gets the number of other sources' sorted files as a second argument only when there are any, so one-argument callables keep working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_validate_folder meant "." to name the root, but it validated the path first, and the path check rejects "." as empty, so --prefix ./ failed with "Remote path cannot be empty". "/" is still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
restore --staged . cleared the entries of one index object and wrote another: each access to Dothm.index reads the index afresh, so nothing was unstaged before the first commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
glob_search filled in the format together with the base path, so braces
in a folder name such as run_{id} became wildcards and hm add PATTERN
picked up a sibling folder's files. Only the format is filled in now,
and the base path is escaped for glob.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Error paths and less common branches: interrupted and racing downloads, SFTP replies and failures, SSH time limits, auth profile validation, HTTP and WebDAV responses, S3 listings and errors, CLI reports and prompts, path and checksum helpers, and format detection merges. SFTP and SSH cases run against local pipes and child processes, not sshd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The S3 tests run only in the job that installs boto3 and moto, and that job uploaded no coverage, so the S3 backend counted as untested. It now uploads its data, the coverage job waits for it, and it also runs the new S3 edge-case tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
denniswu28
merged commit Oct 21, 2026
4af4f20
into
codex/function-atomic-replay-20260928
19 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hallmark can now catalogue data that lives on private servers and download it on demand, with every file verified and recorded for commit.
Closes #51. Closes #54.
What this adds
pip install hallmark[s3]). Credentials come from typed profiles in a per-userauth.yml, never from the repository or a URL.sources:inconfig.yml. Adding a source again updates it:--prune/--keep-missing).hm addthen offers to skip them, keep both under a folder, or abort (--skip-duplicatesin scripts). Paths that differ only in letter case get a warning.file:entries;hm detectsuggests formats;hm addasks whether to re-sort this source's files, every source's files (all), or only unsorted ones.--server-checksum). Thechecksum_sourcecolumn records where each came from..hm/objects→ worktree → staged SHA-1..hm/objects, so checking out an older commit restores them without a transfer.Changes to existing behaviour
catalog_source, so{source}stays free as a format field (for example M87).--filterdoes.Testing
-W.demo_private_data.ipynbanddemo_add.ipynbrun as tests.hmagainst local HTTP servers covered:.hm/objectson a branch switch;hm detect --addfollowed by download and status.Also fixed
Edge-case tests found three older bugs, fixed here with regression tests:
--prefix ./(or.) failed with "Remote path cannot be empty".restore --staged .did nothing on a branch with no commits.run_{id}, globbed a sibling folder's files.Not in this PR
hm gcfor objects no commit references.file:entry withrestore --staged(restore.hm/config.ymlinstead).hm.clone("eht/2026:science-release-1")), planned as a separate branch.🤖 Generated with Claude Code