Skip to content

tether 0.1.0b4

Pre-release
Pre-release

Choose a tag to compare

@elyall elyall released this 24 Sep 15:16
· 42 commits to main since this release

Security

  • git: a manifest's path must be absolute and outside the checkout; git
    runs no fsmonitor, hook, ext:: transport or implicit bare repository, and
    ignores an inherited GIT_DIR, GIT_WORK_TREE, GIT_INDEX_FILE or
    GIT_CONFIG_*.
  • git: a committed remote must name a configured remote; a URL goes in
    .tether/secrets.toml ([objects."<key>"] remote).
  • import: [import] query is read from .tether/secrets.toml, not
    tether.toml, and runs as one read-only statement.
  • dolt: credentials come only from the server's [uris."mysql://host:port"]
    entry in .tether/secrets.toml (password_env / user_env move there);
    $DOLT_PASSWORD went to any host a manifest named.
  • Object keys may not contain \ or a drive letter, and are checked when a
    manifest is read.
  • .tether/secrets.toml, workspace.toml and ops.jsonl are refused while
    jj or git tracks them, with the command that untracks them. The committed
    .tether/.gitignore was all that kept them out, so a cloned
    secrets.toml naming git_path ran that program on tether status.
  • git 2.38 is the minimum version, checked by the VCS adapter and the git
    backend; an older git ignores safe.bareRepository without a word.
  • Another live checkout's workspace.toml or ops.jsonl that the VCS tracks
    there is skipped, with a warning naming the checkout: a shipped op log could
    make gc release pins as this clone's.
  • open checks again whether the VCS tracks workspace.toml whenever the
    file has changed, so a long-lived Repo refuses one that a pull or commit
    put under version control after find.

Added

  • ObjectBackend.fork, promote and merge take expected=, the head the
    caller reviewed (or ABSENT): the ref moves only from there, else
    RefMovedError and nothing moves. Backends without the keyword work as
    before; the engine passes the heads its plans reviewed.

  • tether.plan.REQUIRED_PRECONDITIONS per command, and a workspace_bookmark
    precondition: the checkout's bookmark as workspace.toml and the VCS see it.

  • The conformance suite checks conditional forks, PROMOTE, MERGE,
    ancestor_of and opening an older recorded state.

  • gc --release-foreign (Repo.gc(release_foreign=)): also release
    unreferenced pins this clone did not create.

  • icechunk: allow_http and force_path_style in .tether/secrets.toml
    (per URI prefix or object) for an S3-compatible server such as SeaweedFS
    or MinIO; without them add --create could not reach one.

  • Capability.CONDITIONAL_REF declares that a backend's ref moves honour
    expected; conformance fails a backend that claims it and ignores it.

  • new --shared adopts a peer's uncommitted writes when they build on the
    bookmark's pin, instead of asking for --discard.

Changed

  • Partial success (an undo, repair, upgrade or forget-workspace that
    could not do everything) exits 3; 2 is Click's usage error.

  • --help keeps bracketed text such as [experimental]; add --kind lists
    each kind with its maturity.

  • A plan must carry the preconditions its command requires; one saved by an
    older tether, or edited, is refused as stale. commit, new, restore,
    promote, drop, gc and repair plans bind to the checkout that made
    them, and commit, restore, promote and drop plans to its bookmark;
    import, upgrade and forget-workspace plans bind to no checkout.

  • promote lands committed states only (tether commit uncommitted writes
    first). Merges run before fast-forwards, which are held when a merge does
    not land; the trunk moves to the commit the plan reviewed.

  • restore checks the head of every branch it would reset, deferred forks
    included, wants --discard for uncommitted writes, and refuses a head it
    cannot read.

  • undo reverses the newest operation only and refuses one it cannot undo
    rather than reaching past it; tether undo ID restores only the
    workspace.toml fields that entry changed. The new and gc a drop runs
    are its steps ((step of ID) in tether ops) and cannot be undone alone.

  • new, restore and the first writable open hold the repository lock
    while they check and fork, and fork only onto the head the plan saw.

  • Without fcntl (Windows), writing commands are refused; status, verify,
    diff, log, ops and gc --dry-run work.

  • gc and drop release only pins this clone created (recorded in
    tether-pinned.jsonl beside the repository lock, seeded from the op logs).
    Any other unreferenced pin is kept as informational keep-pin until it is
    fetched or --release-foreign is passed; GcReport.kept_pins and gc --json list them.

  • jj 0.43 is the minimum version; an older one is refused.

  • jj and git run with tether's own colour and pager settings, whatever the
    user's config says; tether tracks its own files by name, and its revsets
    use no name an alias can redefine. Colour forced on, all() aliased or
    auto-tracking off had corrupted commit ids, made empty commits, or shrunk
    the history gc walks.

  • file, icechunk, lance, delta: every spelling of a local path --
    /p, /p/, file:///p, a path through a symlinked parent such as macOS's
    /tmp -- is one store to pin ids, listings and gc. New pins of an
    object registered under another spelling get new ids.

  • neon: pins are unprotected unless protected_pins = true; Free has no
    protected branches, and paid plans allow a few.

  • The dataset format is version 5: run tether upgrade once on a 0.1.0b3
    dataset. It gives the stores in tether-touched.jsonl and
    tether-created.jsonl their new identities, stores listings again under
    their new names, records Lance (branch_id), Neon (commit_xid) and
    directory (symlinks) states in the new form where the data is unchanged,
    and makes DuckLake paths absolute. 0.1.0b3 refuses a version 5 dataset,
    so clones on the two releases cannot take turns.

  • Saved plans are format 3, with a digest binding their actions and context
    to their preconditions; re-run a plan saved in format 1 or 2.

  • gc and promote print what they applied along with the failures, and
    exit 3 when part of the work was done.

  • jj calls keep only your identity, signing, snapshot and git settings, and
    your immutable_heads() with the revset aliases it names.

  • A new file of yours that jj has not snapshotted stays in the change it was
    made in when tether moves the working copy.

  • Every open follows the checkout's current bookmark; on Windows a default
    open is read-only.

Removed

  • The lakefs backend, tether add --repository/--prefix, LakeFSHandle
    and the lakefs extra.

Fixed

  • Delta history and diff attached the wrong commit to each version below
    the head.

  • Lance states off main carry the branch id, so a state from a re-created
    branch verifies as missing instead of opening another branch's data.

  • tether diff with no arguments compares against jj's @-, not the working
    copy commit.

  • file: allow_http and the other HTTP client options work on S3; symlinks
    to directories and dangling ones count by their target; the racy-timestamp
    guard covers every timestamp granularity.

  • tether status labels an unreadable object error, reports the rest and
    exits 1 instead of aborting.

  • tether init --json prints only JSON.

  • Two --shared checkouts materializing one lazy fork could throw the first
    one's write away.

  • Undoing an older new after a commit on its branch deleted the branch, for
    pin = "record" the only copy of that state; it now needs --discard, and
    the workspace no longer rolls back to that time's bookmark. Undoing an older
    add moved the checkout to main, so the next write went to the store's
    main.

  • Two undos after a drop revived the abandoned commit.

  • jj: undoing a commit other commits were built on rewrote them; refused now
    (jj backout reverts in place).

  • promote landed uncommitted writes and moved the trunk to a commit
    recording an older state; a conflicted merge left its fast-forwards landed;
    a saved plan applied on another bookmark moved the trunk there; the reset
    after a merge discarded a concurrent write.

  • A long-lived Repo's writable open ignored a new another process ran
    since it was constructed.

  • new kept the previous bookmark's snapshot cache, so status and
    commit --no-snapshot used the old branch's head under the new bookmark.

  • AWS profile or role credentials were never refreshed (now five minutes
    before expiry), and two prefixes of one bucket with different credential
    rules shared the first's.

  • The locks were re-entrant per Repo, not per thread; a second thread could
    leave that Repo unable to lock again.

  • jj: drop from a bookmark whose working copy had edits planned no leave and
    left workspace.toml naming the dropped bookmark.

  • gc counts every live checkout's working-tree manifests and the pins of
    running or interrupted operations as references; --prune-bookmarks keeps
    every branch a live checkout works on or has pending.

  • gc and drop refuse while jj reports a conflicted bookmark or a
    .tether/ conflict no later commit resolved, and promote while its trunk
    is conflicted; status and commit name a conflicted bookmark instead of
    calling it gone.

  • commit raises when the new commit's tree lacks a manifest (a dataset under
    an ignored directory made an empty commit status called clean).

  • git: a hook-refused git commit left the manifests staged; the index is
    reset.

  • jj: the history walk reads every side of a conflicted commit, so gc
    counts the pins each side names.

  • file: a local path holding # or ? was cut short there, and
    add --create on a file:// URI made a stray file: directory.

  • A saved drop plan applies only in the checkout that made it, and only
    while that checkout is still on (or off) the bookmark as the VCS sees it.

  • A checkout writes its workspace id on first open, so a plan saved in a
    fresh clone or worktree applies there.

  • drop closes its journal entry when the store half fails.

  • An op-log entry appended after a torn line is no longer lost with it.

  • --from-plan refuses --dry-run and --plan; commit --from-plan p.json --dry-run committed.

  • An [objects."<key>"] entry in .tether/secrets.toml reaches the store
    add --create makes and, once the object is removed, the store
    gc --delete-stores reclaims; both used the default endpoint and ambient
    credentials.

  • icechunk: a profile or role_arn entry hands Icechunk a refresh
    callback, so a handle held past the role's expiry keeps writing; only new
    opens used to get fresh keys.

  • gc never releases a pin some manifest names, whichever spelling of the
    store that manifest uses; one store named two ways lost pins HEAD
    referenced. gc --delete-stores also matches an indexed store by the
    identity its locator has now, so a created store a manifest still names is
    no longer deleted.

  • gc (a dry run too), gc --delete-stores and verify --all-history skip,
    with a note, the manifests history holds of a backend tether no longer
    has; a dataset that ever held a lakeFS object failed them even after
    remove. Their pins still count as references.

  • promote landed uncommitted writes after tether undo or
    commit --no-vcs: it checks forks against the bookmark's commit.

  • tether undo ID of an older new -b sent the checkout to main; it
    reverts only the fields nothing has changed since.

  • Lance's conditional fork replaced a peer's branch, and git's moved a
    branch checked out in another worktree; repair's refork is conditional.

  • git: status.showUntrackedFiles = no hid new manifests from commit.

  • The pin index claimed pins other clones had made, and kept released ones.

  • A writable open re-read every manifest; only changed files are parsed.

  • A plain status hid the errors a status --snapshot had cached.

  • tether diff on a jj merge working copy showed every object as added.

  • file: client options in another spelling (AWS_ALLOW_HTTP) panicked, and
    a non-string value (timeout = 5) raised.

  • Threads resolving one AWS profile or role made one STS call each.

  • git: abandon left a manifest the abandoned commit had added deleted in
    the worktree, so the next commit deleted it.

  • gc listed one failure when a ref of one name (a bookmark's branch) failed
    in two stores; each failure now names its object.

Experimental

  • neon: add requires database and role; connection URIs name their
    endpoint; busy answers (423, 429, 503) are retried with backoff; a compute
    restart no longer reads as a write.
  • dolt: a merge with conflicts or constraint violations raises
    MergeConflict and writes nothing.
  • iceberg: tables with no snapshot yet are accepted; requires
    pyiceberg >= 0.11.
  • ducklake: a relative metadata path is stored absolute.
  • ducklake: a leading ~ in metadata or data_path is expanded (it was
    stored as <cwd>/~/...), and a bare sqlite: or duckdb: metadata path
    is stored absolute too.
  • neon: objects on one branch fingerprinted at once each created its
    endpoint; Neon allows one read-write endpoint per branch.