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:
@@ -28,9 +28,9 @@ export interface CrossRepoResult {
|
||||
/** Options de lancement d'une session de groupe (P6). */
|
||||
export interface GroupFeatureOptions {
|
||||
command: 'claude' | 'bash';
|
||||
/** présent → crée le worktree de cette branche dans chaque repo puis relie ; absent → checkouts principaux. */
|
||||
/** présent → crée/réutilise le worktree de cette branche dans chaque repo puis relie ; absent → checkouts principaux. */
|
||||
branch?: string;
|
||||
newBranch?: boolean;
|
||||
/** branche de base d'une branche créée (défaut serveur : branche par défaut du dépôt). */
|
||||
baseRef?: string;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export const useGroupsStore = defineStore('groups', () => {
|
||||
try {
|
||||
const res = await wt.createWorktree(repoId, {
|
||||
branch,
|
||||
newBranch: opts.newBranch ?? true,
|
||||
mode: 'auto', // détecte créer / checkout / suivi remote selon l'existence par dépôt
|
||||
...(opts.baseRef ? { baseRef: opts.baseRef } : {}),
|
||||
startSession: null,
|
||||
});
|
||||
@@ -176,6 +176,11 @@ export const useGroupsStore = defineStore('groups', () => {
|
||||
}),
|
||||
);
|
||||
}
|
||||
// Mode feature : si AUCUN worktree n'a pu être créé/résolu, ne pas tenter la session (elle
|
||||
// échouerait en « no resolvable worktree » et masquerait les vraies erreurs par dépôt).
|
||||
if (opts.branch && worktreeResults.length > 0 && !worktreeResults.some((r) => r.status === 'ok')) {
|
||||
return { worktreeResults, session: null, dirs: [], skipped: [] };
|
||||
}
|
||||
const res = await createGroupSession(groupId, {
|
||||
command: opts.command,
|
||||
...(opts.branch ? { branch: opts.branch } : {}),
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user