* feat(ui): block branch switches on dirty trees * feat(ui): show unpushed commits in git branch selector * feat(ui): show recent branches in git selector * fix(ui): persist recent branch status * feat(ui): add mobile branch picker * fix(ui): guard mobile branch checkout * fix(i18n): restore Turkish git empty state labels * feat(ui): flag dirty draft directories on the branch selector Replaces the draft dirty-directory banner with an indicator on the branch selector: a warning icon plus a hover tooltip that opens by itself for five seconds when the dirty state first appears, then stays hover-only. The copy states the situation and the options (commit or worktree) without prescribing either. * feat(ui): optional push in the dirty branch switch dialog Commit-and-switch gains an opt-in "Push after commit" checkbox. When the push fails the commit stands but the switch is cancelled with an explicit toast, so the user is never moved off a branch without knowing its push did not happen. Without the checkbox the toast states the commit is local only. * fix(i18n): align dirty-directory copy across locales * fix(a11y): name the unpushed-commit badge in the branch picker The badge showed a bare arrow and number with no accessible name or tooltip. Both the desktop recents list and the mobile picker now carry a localized "N commits not pushed" title and aria-label. * fix(mobile): push before switching dirty branches Honor the dirty-switch dialog's push option on the mobile Changes surface. A failed push leaves the new commit on its source branch, refreshes state, and cancels checkout. Mobile branch selection now also shows the existing dirty switch notice.
589 lines
23 KiB
TypeScript
589 lines
23 KiB
TypeScript
import * as gitService from './gitService';
|
|
import type { BridgeResponse } from './bridge';
|
|
|
|
type BridgeMessageInput = {
|
|
id: string;
|
|
type: string;
|
|
payload?: unknown;
|
|
};
|
|
|
|
const requireDirectory = (id: string, type: string, directory?: string): BridgeResponse | null => {
|
|
if (!directory) {
|
|
return { id, type, success: false, error: 'Directory is required' };
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const isValidCommitHash = (hash: string | undefined): hash is string => (
|
|
typeof hash === 'string' && /^[0-9a-fA-F]{7,40}$/.test(hash)
|
|
);
|
|
|
|
export async function handleStandardGitBridgeMessage(message: BridgeMessageInput): Promise<BridgeResponse | null> {
|
|
const { id, type, payload } = message;
|
|
|
|
switch (type) {
|
|
case 'api:git/check': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const isRepo = await gitService.checkIsGitRepository(directory!);
|
|
return { id, type, success: true, data: isRepo };
|
|
}
|
|
|
|
case 'api:git/worktree-type': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const isLinked = await gitService.isLinkedWorktree(directory!);
|
|
return { id, type, success: true, data: isLinked };
|
|
}
|
|
|
|
case 'api:git/status': {
|
|
const { directory, mode } = (payload || {}) as { directory?: string; mode?: 'light' };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const status = await gitService.getGitStatus(directory!, mode === 'light' ? { mode } : undefined);
|
|
return { id, type, success: true, data: status };
|
|
}
|
|
|
|
case 'api:git/branches': {
|
|
const { directory, method, name, startPoint, force } = (payload || {}) as {
|
|
directory?: string;
|
|
method?: string;
|
|
name?: string;
|
|
startPoint?: string;
|
|
force?: boolean;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
|
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
|
|
if (normalizedMethod === 'GET') {
|
|
const branches = await gitService.getGitBranches(directory!);
|
|
return { id, type, success: true, data: branches };
|
|
}
|
|
|
|
if (normalizedMethod === 'POST') {
|
|
if (!name) {
|
|
return { id, type, success: false, error: 'Branch name is required' };
|
|
}
|
|
const result = await gitService.createBranch(directory!, name, startPoint);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
if (normalizedMethod === 'DELETE') {
|
|
if (!name) {
|
|
return { id, type, success: false, error: 'Branch name is required' };
|
|
}
|
|
const result = await gitService.deleteGitBranch(directory!, name, force);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
|
}
|
|
|
|
case 'api:git/branch-push-status': {
|
|
const { directory, branches } = (payload || {}) as { directory?: string; branches?: string[] };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!Array.isArray(branches) || branches.some((branch) => typeof branch !== 'string')) {
|
|
return { id, type, success: false, error: 'branches must be an array of branch names' };
|
|
}
|
|
return { id, type, success: true, data: await gitService.getGitUnpushedBranchCounts(directory!, branches) };
|
|
}
|
|
|
|
case 'api:git/remote-branches': {
|
|
const { directory, branch, remote } = (payload || {}) as {
|
|
directory?: string;
|
|
branch?: string;
|
|
remote?: string;
|
|
};
|
|
if (!directory || !branch) {
|
|
return { id, type, success: false, error: 'Directory and branch are required' };
|
|
}
|
|
const result = await gitService.deleteRemoteBranch(directory, branch, remote);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/checkout': {
|
|
const { directory, branch } = (payload || {}) as { directory?: string; branch?: string };
|
|
if (!directory || !branch) {
|
|
return { id, type, success: false, error: 'Directory and branch are required' };
|
|
}
|
|
const result = await gitService.checkoutBranch(directory, branch);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/worktrees': {
|
|
const { directory, method } = (payload || {}) as {
|
|
directory?: string;
|
|
method?: string;
|
|
body?: unknown;
|
|
directoryPath?: string;
|
|
deleteLocalBranch?: boolean;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
|
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
|
|
if (normalizedMethod === 'GET') {
|
|
const worktrees = await gitService.listGitWorktrees(directory!);
|
|
return { id, type, success: true, data: worktrees };
|
|
}
|
|
|
|
if (normalizedMethod === 'POST') {
|
|
const created = await gitService.createWorktree(directory!, (payload || {}) as gitService.CreateGitWorktreePayload);
|
|
return { id, type, success: true, data: created };
|
|
}
|
|
|
|
if (normalizedMethod === 'DELETE') {
|
|
const removePayload = payload as {
|
|
body?: { directory?: string; deleteLocalBranch?: boolean };
|
|
directory?: string;
|
|
deleteLocalBranch?: boolean;
|
|
};
|
|
const bodyDirectory = typeof removePayload?.body?.directory === 'string'
|
|
? removePayload.body.directory
|
|
: '';
|
|
const legacyDirectory = typeof removePayload?.directory === 'string' ? removePayload.directory : '';
|
|
const worktreeDirectory = bodyDirectory || legacyDirectory || '';
|
|
|
|
if (!worktreeDirectory) {
|
|
return { id, type, success: false, error: 'Worktree directory is required' };
|
|
}
|
|
const removed = await gitService.removeWorktree(directory!, {
|
|
directory: worktreeDirectory,
|
|
deleteLocalBranch: removePayload?.body?.deleteLocalBranch === true || removePayload?.deleteLocalBranch === true,
|
|
});
|
|
return { id, type, success: true, data: { success: Boolean(removed) } };
|
|
}
|
|
|
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
|
}
|
|
|
|
case 'api:git/worktrees/validate': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.validateWorktreeCreate(directory!, (payload || {}) as gitService.CreateGitWorktreePayload);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/worktrees/bootstrap-status': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.getWorktreeBootstrapStatus(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/worktrees/preview': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.previewWorktreeCreate(directory!, (payload || {}) as gitService.CreateGitWorktreePayload);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/validate-directory': {
|
|
const { directory, worktreeRoot } = (payload || {}) as { directory?: string; worktreeRoot?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.validateWorktreeDirectory(directory!, worktreeRoot!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/canonicalize-worktree-state': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.canonicalizeWorktreeState(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/diff': {
|
|
const { directory, path: filePath, staged, contextLines } = (payload || {}) as {
|
|
directory?: string;
|
|
path?: string;
|
|
staged?: boolean;
|
|
contextLines?: number;
|
|
};
|
|
if (!directory || !filePath) {
|
|
return { id, type, success: false, error: 'Directory and path are required' };
|
|
}
|
|
const result = await gitService.getGitDiff(directory, filePath, staged, contextLines);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/file-diff': {
|
|
const { directory, path: filePath, staged } = (payload || {}) as {
|
|
directory?: string;
|
|
path?: string;
|
|
staged?: boolean;
|
|
};
|
|
if (!directory || !filePath) {
|
|
return { id, type, success: false, error: 'Directory and path are required' };
|
|
}
|
|
const result = await gitService.getGitFileDiff(directory, filePath, staged);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/revert': {
|
|
const { directory, path: filePath, scope } = (payload || {}) as { directory?: string; path?: string; scope?: 'all' | 'working' };
|
|
if (!directory || !filePath) {
|
|
return { id, type, success: false, error: 'Directory and path are required' };
|
|
}
|
|
await gitService.revertGitFile(directory, filePath, { scope });
|
|
return { id, type, success: true, data: { success: true } };
|
|
}
|
|
|
|
case 'api:git/stage': {
|
|
const { directory, path: filePath, paths } = (payload || {}) as { directory?: string; path?: string; paths?: string[] };
|
|
const filePaths = (Array.isArray(paths) ? paths : [filePath])
|
|
.filter((value): value is string => typeof value === 'string' && value.trim().length > 0);
|
|
if (!directory || filePaths.length === 0) {
|
|
return { id, type, success: false, error: 'Directory and path are required' };
|
|
}
|
|
await gitService.stageGitFiles(directory, filePaths);
|
|
return { id, type, success: true, data: { success: true } };
|
|
}
|
|
|
|
case 'api:git/unstage': {
|
|
const { directory, path: filePath, paths } = (payload || {}) as { directory?: string; path?: string; paths?: string[] };
|
|
const filePaths = (Array.isArray(paths) ? paths : [filePath])
|
|
.filter((value): value is string => typeof value === 'string' && value.trim().length > 0);
|
|
if (!directory || filePaths.length === 0) {
|
|
return { id, type, success: false, error: 'Directory and path are required' };
|
|
}
|
|
await gitService.unstageGitFiles(directory, filePaths);
|
|
return { id, type, success: true, data: { success: true } };
|
|
}
|
|
|
|
case 'api:git/apply-hunk': {
|
|
const { directory, path: filePath, patch, action } = (payload || {}) as {
|
|
directory?: string;
|
|
path?: string;
|
|
patch?: string;
|
|
action?: 'stage' | 'unstage' | 'discard';
|
|
};
|
|
if (!directory || !filePath || typeof patch !== 'string' || !patch.trim()) {
|
|
return { id, type, success: false, error: 'Directory, path, and patch are required' };
|
|
}
|
|
if (action !== 'stage' && action !== 'unstage' && action !== 'discard') {
|
|
return { id, type, success: false, error: 'action must be stage, unstage, or discard' };
|
|
}
|
|
await gitService.applyGitHunk(directory, filePath, patch, action);
|
|
return { id, type, success: true, data: { success: true } };
|
|
}
|
|
|
|
case 'api:git/commit': {
|
|
const { directory, message, addAll, files, stageFiles } = (payload || {}) as {
|
|
directory?: string;
|
|
message?: string;
|
|
addAll?: boolean;
|
|
files?: string[];
|
|
stageFiles?: string[];
|
|
};
|
|
if (!directory || !message) {
|
|
return { id, type, success: false, error: 'Directory and message are required' };
|
|
}
|
|
const result = await gitService.createGitCommit(directory, message, { addAll, files, stageFiles });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/push': {
|
|
const { directory, remote, branch, options } = (payload || {}) as {
|
|
directory?: string;
|
|
remote?: string;
|
|
branch?: string;
|
|
options?: string[] | Record<string, unknown>;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.gitPush(directory!, { remote, branch, options });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/pull': {
|
|
const { directory, remote, branch, rebase } = (payload || {}) as {
|
|
directory?: string;
|
|
remote?: string;
|
|
branch?: string;
|
|
rebase?: boolean;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.gitPull(directory!, { remote, branch, rebase });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/fetch': {
|
|
const { directory, remote, branch } = (payload || {}) as {
|
|
directory?: string;
|
|
remote?: string;
|
|
branch?: string;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.gitFetch(directory!, { remote, branch });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/stashes': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
return { id, type, success: true, data: { stashes: await gitService.listGitStashes(directory!) } };
|
|
}
|
|
|
|
case 'api:git/stashes/file-counts': {
|
|
const { directory, refs } = (payload || {}) as { directory?: string; refs?: string[] };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
return { id, type, success: true, data: { counts: await gitService.countGitStashFiles(directory!, refs ?? []) } };
|
|
}
|
|
|
|
case 'api:git/stash': {
|
|
const { directory, message } = (payload || {}) as { directory?: string; message?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
return { id, type, success: true, data: await gitService.stashGitChanges(directory!, { message }) };
|
|
}
|
|
|
|
case 'api:git/stash/apply':
|
|
case 'api:git/stash/pop':
|
|
case 'api:git/stash/drop': {
|
|
const { directory, ref } = (payload || {}) as { directory?: string; ref?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const stashRef = ref || 'stash@{0}';
|
|
const data = type === 'api:git/stash/apply'
|
|
? await gitService.applyGitStash(directory!, { ref: stashRef })
|
|
: type === 'api:git/stash/pop'
|
|
? await gitService.popGitStash(directory!, { ref: stashRef })
|
|
: await gitService.dropGitStash(directory!, { ref: stashRef });
|
|
return { id, type, success: true, data };
|
|
}
|
|
|
|
case 'api:git/remotes': {
|
|
const { directory, method, remote } = (payload || {}) as {
|
|
directory?: string;
|
|
method?: string;
|
|
remote?: string;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
|
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
if (normalizedMethod === 'GET') {
|
|
const result = await gitService.getRemotes(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
if (normalizedMethod === 'DELETE') {
|
|
if (!remote) {
|
|
return { id, type, success: false, error: 'Remote name is required' };
|
|
}
|
|
const result = await gitService.removeRemote(directory!, remote);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
|
}
|
|
|
|
case 'api:git/rebase': {
|
|
const { directory, onto } = (payload || {}) as { directory?: string; onto?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!onto) {
|
|
return { id, type, success: false, error: 'onto is required' };
|
|
}
|
|
const result = await gitService.rebase(directory!, { onto });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/rebase/abort': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.abortRebase(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/merge': {
|
|
const { directory, branch } = (payload || {}) as { directory?: string; branch?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!branch) {
|
|
return { id, type, success: false, error: 'branch is required' };
|
|
}
|
|
const result = await gitService.merge(directory!, { branch });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/merge/abort': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.abortMerge(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/rebase/continue': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.continueRebase(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/merge/continue': {
|
|
const { directory } = (payload || {}) as { directory?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.continueMerge(directory!);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/checkout-commit': {
|
|
const { directory, hash } = (payload || {}) as { directory?: string; hash?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!isValidCommitHash(hash)) {
|
|
return { id, type, success: false, error: 'Invalid commit hash' };
|
|
}
|
|
const result = await gitService.checkoutCommit(directory!, hash);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/cherry-pick': {
|
|
const { directory, hash } = (payload || {}) as { directory?: string; hash?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!isValidCommitHash(hash)) {
|
|
return { id, type, success: false, error: 'Invalid commit hash' };
|
|
}
|
|
const result = await gitService.cherryPick(directory!, hash);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/revert-commit': {
|
|
const { directory, hash } = (payload || {}) as { directory?: string; hash?: string };
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!isValidCommitHash(hash)) {
|
|
return { id, type, success: false, error: 'Invalid commit hash' };
|
|
}
|
|
const result = await gitService.revertCommit(directory!, hash);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/reset-to-commit': {
|
|
const { directory, hash, mode, force } = (payload || {}) as {
|
|
directory?: string;
|
|
hash?: string;
|
|
mode?: 'soft' | 'mixed' | 'hard';
|
|
force?: boolean;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
if (!isValidCommitHash(hash)) {
|
|
return { id, type, success: false, error: 'Invalid commit hash' };
|
|
}
|
|
if (!mode || !['soft', 'mixed', 'hard'].includes(mode)) {
|
|
return { id, type, success: false, error: 'mode must be soft, mixed, or hard' };
|
|
}
|
|
const result = await gitService.resetToCommit(directory!, hash, mode, force);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/log': {
|
|
const { directory, maxCount, from, to, file, all } = (payload || {}) as {
|
|
directory?: string;
|
|
maxCount?: number;
|
|
from?: string;
|
|
to?: string;
|
|
file?: string;
|
|
all?: boolean;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
const result = await gitService.getGitLog(directory!, { maxCount, from, to, file, all });
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/commit-files': {
|
|
const { directory, hash } = (payload || {}) as { directory?: string; hash?: string };
|
|
if (!directory || !hash) {
|
|
return { id, type, success: false, error: 'Directory and hash are required' };
|
|
}
|
|
const result = await gitService.getCommitFiles(directory, hash);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/commit-file-diff': {
|
|
const { directory, hash, path: filePath, binary } = (payload || {}) as {
|
|
directory?: string;
|
|
hash?: string;
|
|
path?: string;
|
|
binary?: boolean;
|
|
};
|
|
if (!directory || !hash || !filePath) {
|
|
return { id, type, success: false, error: 'Directory, hash, and path are required' };
|
|
}
|
|
if (!/^[0-9a-fA-F]{7,40}$/.test(hash)) {
|
|
return { id, type, success: false, error: 'hash must be a valid commit SHA' };
|
|
}
|
|
const result = await gitService.getCommitFileDiff(directory, hash, filePath, Boolean(binary));
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
case 'api:git/identity': {
|
|
const { directory, method, userName, userEmail, sshKey, signCommits, signingKey } = (payload || {}) as {
|
|
directory?: string;
|
|
method?: string;
|
|
userName?: string;
|
|
userEmail?: string;
|
|
sshKey?: string | null;
|
|
signCommits?: boolean;
|
|
signingKey?: string | null;
|
|
};
|
|
const dirError = requireDirectory(id, type, directory);
|
|
if (dirError) return dirError;
|
|
|
|
const normalizedMethod = typeof method === 'string' ? method.toUpperCase() : 'GET';
|
|
|
|
if (normalizedMethod === 'GET') {
|
|
const identity = await gitService.getCurrentGitIdentity(directory!);
|
|
return { id, type, success: true, data: identity };
|
|
}
|
|
|
|
if (normalizedMethod === 'POST') {
|
|
if (!userName || !userEmail) {
|
|
return { id, type, success: false, error: 'userName and userEmail are required' };
|
|
}
|
|
const result = await gitService.setGitIdentity(
|
|
directory!,
|
|
userName,
|
|
userEmail,
|
|
sshKey,
|
|
signCommits === true,
|
|
signingKey ?? null
|
|
);
|
|
return { id, type, success: true, data: result };
|
|
}
|
|
|
|
return { id, type, success: false, error: `Unsupported method: ${normalizedMethod}` };
|
|
}
|
|
|
|
case 'api:git/ignore-openchamber': {
|
|
return { id, type, success: true, data: { success: true } };
|
|
}
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|