Skip to content

Make repository ids self-descriptive and consolidate remote persistence#462

Merged
sbertix merged 5 commits into
mainfrom
sbertix/refactor-repository-identity
Jun 22, 2026
Merged

Make repository ids self-descriptive and consolidate remote persistence#462
sbertix merged 5 commits into
mainfrom
sbertix/refactor-repository-identity

Conversation

@sbertix

@sbertix sbertix commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Remote repositories now persist as self-descriptive [user@]host[:port]<path> ids in a top-level SettingsFile.remoteRepositoryRoots (the sibling of repositoryRoots), instead of RemoteRepositoryConfig entries nested in GlobalSettings. Both lists merge into the one in-memory roster.
  • Dropped the remote:// id scheme prefix: the authority already disambiguates, and a local id is always an absolute path (leading /) while a remote authority never is, so that is the local-vs-remote discriminator.
  • Dropped the folder: worktree-id prefix; git-vs-folder is now a runtime Worktree.kind rather than baked into a persisted id (a directory can be (un)initialized as a git repo at any time). The kind argument is required so a worktree can't be silently misclassified.
  • Dissolved RemoteRepositoryConfig; removed the dead displayName and RemoteHost.worktreeBasePath fields.
  • Added RemoteHost.init?(authority:) and RepositoryLocation.parse(persistedID:) to round-trip an id back to host + path (IPv6 literals are bracketed).
  • Taught RepositoryPathNormalizer.normalize to leave remote ids verbatim, so a pinned / focused / archived remote worktree id is no longer mangled by URL(fileURLWithPath:).

Migration

A one-shot launch migration (sidebar schema v1 -> v2) drains the retired global.remoteRepositories into remoteRepositoryRoots and strips the remote:// / folder: prefixes from persisted repository, pin, sidebar, and terminal-layout ids. Idempotent and gated on the schema version.

Data safety

  • settings.json + sidebar.json are snapshotted to *.pre-remote-id-migration.bak before any migration write or settings hydration, so a botched migration or a downgrade is recoverable by hand.
  • Legacy remotes are captured before any save can strip them, and drained ahead of the sidebar gates; a present-but-unreadable settings / sidebar / layouts file defers the pass to the next launch rather than overwriting good data.
  • The legacy remote array decodes per-element lossily, so one malformed entry can't strand the rest.

Known limitation

An IPv6-literal remote host's sidebar customization / pins / layouts will not carry over (legacy keys were unbracketed, new ids are bracketed). The repository itself still migrates, and the state is recoverable from the snapshot. This is vanishingly rare (IPv6 literal SSH hosts).

Downgrade

One-way: an older build won't see migrated remotes (and never round-tripped the dropped displayName). The pre-migration snapshot allows manual recovery.

Testing

make test green (1936 tests, 11 pre-existing known issues); make check clean.

sbertix added 5 commits June 22, 2026 23:42
…al settings

Remote repositories now persist as self-descriptive `[user@]host[:port]<path>`
ids in a top-level `SettingsFile.remoteRepositoryRoots` (the sibling of
`repositoryRoots`), instead of `RemoteRepositoryConfig` entries nested in
`GlobalSettings`. Both lists merge into the one in-memory repository roster.

- Drop the `remote://` scheme prefix: the authority already disambiguates, and
  a local id is always an absolute path (leading `/`) while a remote authority
  never is, so that is the local-vs-remote discriminator.
- Drop the `folder:` worktree-id prefix; git-vs-folder is a runtime
  classification carried by `Worktree.kind`, not baked into a persisted id. The
  kind argument is now required so a worktree can't be silently misclassified.
- Dissolve `RemoteRepositoryConfig` into `RepositoryLocation.remote`, and drop
  the dead `displayName` and `RemoteHost.worktreeBasePath` fields.
- Add `RemoteHost.init?(authority:)` and `RepositoryLocation.parse(persistedID:)`
  to round-trip an id back to host + path.
- Teach `RepositoryPathNormalizer.normalize` to leave remote ids verbatim so a
  pinned / focused / archived remote worktree id is no longer mangled by
  `URL(fileURLWithPath:)`.
- One-shot launch migration (sidebar schema v2): drains the retired
  `global.remoteRepositories` into `remoteRepositoryRoots` and strips the
  `remote://` / `folder:` prefixes from persisted repository, pin, and sidebar
  ids. Idempotent and gated on the schema version.
- Capture the retired global.remoteRepositories before the v0->v1 sidebar
  migration can re-encode settings and drop the field; skip both passes when
  settings.json is present but unreadable so a save can't strip it first.
- Treat a present-but-unreadable/undecodable sidebar.json as a transient error:
  bail instead of overwriting the layout with empty state and stamping the
  schema, which would skip the migration permanently.
- Re-key terminal layouts (layouts.json) off the retired remote:// / folder:
  prefixes so a remote/folder worktree's tabs restore after upgrade.
- Decode the legacy remote array per-element lossily so one malformed entry
  doesn't strand the rest.
- Bracket IPv6 hosts in authority/displayAuthority so a bare IPv6 literal
  round-trips through RemoteHost(authority:); the ssh CLI still gets the bare host.
- Log unparseable persisted remote ids instead of dropping them silently.
- Add regression tests for each.
split(separator:) omits empty subsequences, so the leaf is never an empty
string: drop the redundant emptiness check and force-unwrap.
… gates

- Snapshot settings.json + sidebar.json (raw) before any migration or settings
  hydration can rewrite them, so a botched migration or a downgrade is
  recoverable by hand.
- Drain captured legacy remotes into remoteRepositoryRoots before the sidebar
  read/gate, so a corrupt sidebar.json can't discard them.
- Bail the migration (retry next launch) when a present layouts.json can't be
  re-keyed, instead of stamping the schema and orphaning its keys.
- Add tests for the snapshot, drain-before-sidebar, and layout re-key paths.
@sbertix sbertix enabled auto-merge (squash) June 22, 2026 22:42
@tuist

tuist Bot commented Jun 22, 2026

Copy link
Copy Markdown

🛠️ Tuist Run Report 🛠️

Builds 🔨

Scheme Status Duration Commit
supacode 1m 6s dabcdcecf

@sbertix sbertix merged commit 8696d61 into main Jun 22, 2026
1 check passed
@sbertix sbertix deleted the sbertix/refactor-repository-identity branch June 22, 2026 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant