feat(worktrees): création intelligente (auto) + commit/push/promotion + grille agrandissable

- addWorktree résout la branche par dépôt (branchExists) : checkout si présente, suivi de origin/<b> si seulement remote, sinon création (-b) depuis la branche par défaut. Corrige le 'fatal: invalid reference' sur les groupes hétérogènes ; newBranch -> mode (déprécié, mappé en route).
- Nouvelles opérations git : commit (add -A), push (upstream auto), promotion « en principal » (la branche du worktree devient le checkout principal, sans merge ni conflit, ancienne branche conservée). Routes /worktrees/{commit,push,promote} auditées + GET /repos/:id/branches.
- Web : actions Commit/Push/Promouvoir sur WorktreeCard, sélecteur de branche de base (datalist) dans RepoSection et GroupSessionModal, agrandissement d'un terminal au choix dans TerminalGrid.
- Court-circuite la session de groupe quand aucun worktree n'a pu être créé (fini le masquage par « no resolvable worktree »).
- Tests : git.ts (branchExists/listBranches/commitAll/auto), worktree-manager (commit/promote/branches) ; acceptance P5 étend le scénario worktree de groupe sur branche neuve + commit + promotion.
This commit is contained in:
2026-06-22 19:55:45 +02:00
parent e84c6a7f6d
commit f4fb6c3b52
18 changed files with 732 additions and 62 deletions

View File

@@ -4,12 +4,14 @@ import type {
CreateWorktreeRequest,
CreateWorktreeResponse,
DiscoverReposResponse,
RepoBranchesResponse,
RepoResponse,
ReposListResponse,
RepoSummary,
SessionResponse,
SessionSummary,
StartRepoSessionRequest,
WorktreeResponse,
WorktreeSummary,
WorktreesListResponse,
} from '@arboretum/shared';
@@ -127,6 +129,32 @@ export const useWorktreesStore = defineStore('worktrees', () => {
await refreshRepoWorktrees(repoId);
}
/** Branches du repo (locales/remote + défaut) pour le sélecteur de branche de base. */
async function fetchBranches(repoId: string): Promise<RepoBranchesResponse> {
return api.get<RepoBranchesResponse>(`/api/v1/repos/${repoId}/branches`);
}
/** Commit (add -A + commit) dans un worktree ; le worktree mis à jour arrive aussi par WS. */
async function commitWorktree(repoId: string, path: string, message: string): Promise<WorktreeSummary> {
const res = await api.post<WorktreeResponse>(`/api/v1/repos/${repoId}/worktrees/commit`, { path, message });
upsertWorktree(res.worktree);
return res.worktree;
}
/** Push de la branche d'un worktree (upstream auto si absent). */
async function pushWorktree(repoId: string, path: string): Promise<WorktreeSummary> {
const res = await api.post<WorktreeResponse>(`/api/v1/repos/${repoId}/worktrees/push`, { path });
upsertWorktree(res.worktree);
return res.worktree;
}
/** « Passer en principal » : la branche du worktree devient le checkout principal (worktree supprimé). */
async function promoteWorktree(repoId: string, path: string, force = false): Promise<void> {
await api.post<{ worktree: WorktreeSummary | null }>(`/api/v1/repos/${repoId}/worktrees/promote`, { path, force });
removeWorktreeLocal(path);
await refreshRepoWorktrees(repoId);
}
function startRealtime(): void {
unsubscribe ??= wsClient.subscribeWorktrees(onEvent);
}
@@ -151,6 +179,10 @@ export const useWorktreesStore = defineStore('worktrees', () => {
startMainSession,
deleteWorktree,
prune,
fetchBranches,
commitWorktree,
pushWorktree,
promoteWorktree,
startRealtime,
stopRealtime,
};