feat(submodules): support auth and checkout submodule revisions

Add credential-aware submodule operations and a command to checkout a specific
tag or commit in a submodule without staging the parent repository.

- Backend (src-tauri):
  - Export checkout_submodule_revision and implement checkout_revision which
    validates tag vs commit inputs, verifies refs locally, and checks out the
    submodule in detached mode without modifying the parent's index.
  - Add optional username/password parameters to add_submodule and submodule_action
    flows. Implement submodule_git to call run_git_authenticated when credentials
    are supplied and classify auth failures by prefixing errors with "AUTH_FAILED:".
  - Wire authenticated variants (operate_authenticated, add_authenticated) and
    update fetch/update actions to use credentials where needed.
  - Add unit tests covering authenticated submodule commands, auth failure
    classification, and checkout-by-tag/commit behavior.

- Frontend:
  - App.svelte: introduce credential prompt flow (withSubmoduleCredentials,
    submit/cancel handlers), surface credential dialog on auth failures, and
    wire credentialed calls for initialize/add/update/fetch operations. Hook up
    checkoutSubmoduleRevision and listTags to the submodule dialog.
  - SubmoduleDialog.svelte: add UI for selecting destination folder, loading
    tags and checking out revisions; expose fetch action.
  - CredentialDialog.svelte: include "submodule" action and adjust labels.

- Docs:
  - README: document "Change commit or tag" and "Fetch tags & commits" behaviors.

The commit focuses only on enabling credentialed submodule interactions and
safe local checkouts of tags/commits; no other git behavior changes are made.
This commit is contained in:
2026-09-17 19:39:40 +02:00
parent a9af02a697
commit b00e3e5c18
7 changed files with 433 additions and 29 deletions
+8 -4
View File
@@ -737,9 +737,13 @@ export function pullRequestAiGenerate(path: string, remote: string, sourceBranch
export function listSubmodules(path: string, recursive = true): Promise<GitSubmodule[]> {
return invoke("list_submodules", { path, recursive });
}
export function addSubmodule(path: string, url: string, destination: string, branch?: string): Promise<void> {
return invoke("add_submodule", { path, url, destination, branch: branch || null });
export function addSubmodule(path: string, url: string, destination: string, branch?: string, username?: string, password?: string): Promise<void> {
return invoke("add_submodule", { path, url, destination, branch: branch || null, username: username ?? null, password: password ?? null });
}
export function submoduleAction(path: string, modulePath: string, action: "update" | "stage" | "sync" | "initialize", recursive: boolean): Promise<void> {
return invoke("submodule_action", { path, modulePath, action, recursive });
export function submoduleAction(path: string, modulePath: string, action: "update" | "stage" | "sync" | "initialize" | "fetch", recursive: boolean, username?: string, password?: string): Promise<void> {
return invoke("submodule_action", { path, modulePath, action, recursive, username: username ?? null, password: password ?? null });
}
export function checkoutSubmoduleRevision(path: string, modulePath: string, revision: string, kind: "tag" | "commit"): Promise<void> {
return invoke("checkout_submodule_revision", { path, modulePath, revision, kind });
}