remote.ts•4.96 kB
/**
* @fileoverview CLI provider git remote operation
* @module services/git/providers/cli/operations/remotes/remote
*/
import type { RequestContext } from '@/utils/index.js';
import type {
GitOperationContext,
GitRemoteOptions,
GitRemoteResult,
} from '../../../../types.js';
import {
buildGitCommand,
mapGitError,
parseGitRemote,
} from '../../utils/index.js';
/**
* Execute git remote operations.
*/
export async function executeRemote(
options: GitRemoteOptions,
context: GitOperationContext,
execGit: (
args: string[],
cwd: string,
ctx: RequestContext,
) => Promise<{ stdout: string; stderr: string }>,
): Promise<GitRemoteResult> {
try {
const args: string[] = [];
switch (options.mode) {
case 'list': {
args.push('-v');
const cmd = buildGitCommand({ command: 'remote', args });
const result = await execGit(
cmd,
context.workingDirectory,
context.requestContext,
);
const parsedRemotes = parseGitRemote(result.stdout);
// Group by remote name to combine fetch/push URLs
const remoteMap = new Map<
string,
{ fetchUrl?: string; pushUrl?: string }
>();
for (const remote of parsedRemotes) {
const existing = remoteMap.get(remote.name) || {};
if (remote.type === 'fetch') {
existing.fetchUrl = remote.url;
} else {
existing.pushUrl = remote.url;
}
remoteMap.set(remote.name, existing);
}
const remotes = Array.from(remoteMap.entries()).map(([name, urls]) => ({
name,
fetchUrl: urls.fetchUrl || '',
pushUrl: urls.pushUrl || urls.fetchUrl || '',
}));
const listResult = {
mode: 'list' as const,
remotes,
};
return listResult;
}
case 'add': {
if (!options.name || !options.url) {
throw new Error('Remote name and URL are required for add operation');
}
args.push('add', options.name, options.url);
const cmd = buildGitCommand({ command: 'remote', args });
await execGit(cmd, context.workingDirectory, context.requestContext);
const addResult = {
mode: 'add' as const,
added: { name: options.name, url: options.url },
};
return addResult;
}
case 'remove': {
if (!options.name) {
throw new Error('Remote name is required for remove operation');
}
args.push('remove', options.name);
const cmd = buildGitCommand({ command: 'remote', args });
await execGit(cmd, context.workingDirectory, context.requestContext);
const removeResult = {
mode: 'remove' as const,
removed: options.name,
};
return removeResult;
}
case 'rename': {
if (!options.name || !options.newName) {
throw new Error(
'Remote name and new name are required for rename operation',
);
}
args.push('rename', options.name, options.newName);
const cmd = buildGitCommand({ command: 'remote', args });
await execGit(cmd, context.workingDirectory, context.requestContext);
const renameResult = {
mode: 'rename' as const,
renamed: { from: options.name, to: options.newName },
};
return renameResult;
}
case 'get-url': {
if (!options.name) {
throw new Error('Remote name is required for get-url operation');
}
args.push('get-url', options.name);
if (options.push) {
args.push('--push');
}
const cmd = buildGitCommand({ command: 'remote', args });
const result = await execGit(
cmd,
context.workingDirectory,
context.requestContext,
);
const getUrlResult = {
mode: 'get-url' as const,
remotes: [
{
name: options.name,
fetchUrl: result.stdout.trim(),
pushUrl: result.stdout.trim(),
},
],
};
return getUrlResult;
}
case 'set-url': {
if (!options.name || !options.url) {
throw new Error(
'Remote name and URL are required for set-url operation',
);
}
args.push('set-url', options.name, options.url);
if (options.push) {
args.push('--push');
}
const cmd = buildGitCommand({ command: 'remote', args });
await execGit(cmd, context.workingDirectory, context.requestContext);
const setUrlResult = {
mode: 'set-url' as const,
};
return setUrlResult;
}
default:
throw new Error('Unknown remote operation mode');
}
} catch (error) {
throw mapGitError(error, 'remote');
}
}