Desktop Material

Automatic remote URL refresh

Desktop Material repairs a checkout's configured GitHub remote when the provider reports that the repository was renamed or transferred. The repair is a preflight for network work, so the first fetch, pull, or push after a transfer can use the repository's current canonical location without asking the user to edit .git/config.

Behavior and configuration

The app resolves the repository through its selected account and updates the exact default remote that produced that match; it does not assume the remote is named origin and does not rewrite unrelated remotes. Canonicalization runs when a repository is opened and before fetch, pull, push, refspec fetches, repository-indicator fetches, and scheduled automation.

Transport remains stable. An SSH remote follows the provider's SSH clone URL, while an HTTP or HTTPS remote follows the web clone URL only when the exact URL origin (scheme, host, and port) is unchanged. A protocol switch, another host, an invalid URL, or a hand-authored value that normalization would alter is refused. This includes web URLs containing embedded credentials.

If the remote has an explicit pushurl, Desktop Material migrates it only when it exactly matches the old fetch URL. A deliberately different write-only target, deployment mirror, or other split fetch/push configuration is preserved. If the matching pushurl update fails after the fetch URL changed, the app attempts to restore the old fetch URL so the pair does not remain silently split.

There is no user setting to enable this repair. Provider metadata and the existing remote are its complete input. Explicit network actions revalidate on every run. Successful background checks share in-flight work and use a five-minute cache keyed by repository identity, checkout path, remote name, and remote URL; changing the configured remote therefore creates a new key.

Stale state, failures, and retries

The provider request and local config write are separated by network latency. Immediately before writing, the updater reads the exact fetch URL directly from Git and then uses an exact-old-value conditional update under Git's config lock. A concurrent user or tool edit wins: the stale provider result cannot overwrite it.

Only non-mutating background discovery is fail-soft. A provider lookup or safe config repair that cannot be proved stops an explicit fetch and every mutating pull or push instead of letting a possibly stale destination run. A matching fetch/push pair is accepted only after both final values are read back; if the second write or rollback cannot be proved, the result remains unproven and the operation aborts. Desktop Material never guesses a new owner or repository name. Failed or unproven work is not cached as success, so a later provider refresh can retry. Successful background discovery may wait up to five minutes before checking again.

Submodule viewer repositories are not rewritten. A separate explicit pushurl that differs from the old fetch URL is also intentionally outside the repair boundary and must be maintained by its owner.

Scheduled non-interactive Git

Scheduled commit/push and pull runs use only credentials already available from the selected account or credential vault. When none are available, the run fails and reports a non-blocking notification instead of opening a GitHub, generic credential, Git Credential Manager, SSH host-key, password, or passphrase prompt.

Unattended automation also routes commit and pull through a transient empty core.hooksPath, skips push hooks, and disables commit/merge signing so even non---no-verify hooks or GPG pinentry cannot open their own UI. Post-push SSH deployments use OpenSSH batch mode and disable AskPass. This policy applies only to scheduled/app-owned work: a user-initiated commit, pull, push, or SSH deployment remains interactive and continues to honor the user's normal hook and signing choices.

Skipping hooks and signatures is a deliberate unattended-safety tradeoff. A repository that requires local hooks or signed commits should use a reviewed manual operation or enforce the requirement remotely with branch protection and required checks.

Security considerations

Verification

Unit coverage in app/test/unit/stores/updates/update-remote-url-test.ts exercises HTTPS and SSH migration, stale snapshots and real concurrent config edits, cross-authority and scheme refusal, non-origin remotes, matching pushurl migration, proven rollback/final state, and preservation of divergent write targets. The authenticated fetch, pull, push, commit, local batching, and SSH deployment tests verify propagation of the background boundary, real prepare/post-commit hook suppression, signing suppression, and batch-mode SSH arguments.

For an integration check, rename or transfer a disposable GitHub repository, open its existing checkout, and run Fetch. Verify git remote get-url <name> uses the new canonical owner/name, an exact matching --push URL moved with it, and every unrelated or deliberately divergent remote value remained unchanged. Then remove usable credentials and trigger scheduled automation: it must fail through notification/history without opening a login or pinentry window.