Skip to main content
Glama

Git MCP Server

branch.ts4.28 kB
/** * @fileoverview Git branch operations * @module services/git/providers/cli/operations/branches/branch */ import type { RequestContext } from '@/utils/index.js'; import type { GitBranchOptions, GitBranchResult, GitOperationContext, } from '../../../../types.js'; import { buildGitCommand, GIT_FIELD_DELIMITER, mapGitError, parseBranchRef, } from '../../utils/index.js'; /** * Execute git branch operations. */ export async function executeBranch( options: GitBranchOptions, context: GitOperationContext, execGit: ( args: string[], cwd: string, ctx: RequestContext, ) => Promise<{ stdout: string; stderr: string }>, ): Promise<GitBranchResult> { try { const args: string[] = []; switch (options.mode) { case 'list': { // Build a custom format for git for-each-ref using field delimiters // This provides structured, machine-readable output that's more stable // than parsing the human-readable output of `git branch -v` const format = [ '%(refname)', // Full ref name (e.g., refs/heads/main) '%(objectname)', // Commit hash '%(upstream:short)', // Upstream branch name (e.g., origin/main) '%(upstream:track)', // Tracking info (e.g., "ahead 1, behind 2") '%(HEAD)', // '*' for current branch ].join(GIT_FIELD_DELIMITER); // Choose the ref prefix based on whether we want remote or local branches const refPrefix = options.remote ? 'refs/remotes' : 'refs/heads'; args.push(`--format=${format}`, refPrefix); // Add merge filtering if specified if (options.merged !== undefined) { const mergedRef = typeof options.merged === 'string' ? options.merged : 'HEAD'; args.push(`--merged=${mergedRef}`); } if (options.noMerged !== undefined) { const noMergedRef = typeof options.noMerged === 'string' ? options.noMerged : 'HEAD'; args.push(`--no-merged=${noMergedRef}`); } const cmd = buildGitCommand({ command: 'for-each-ref', args }); const result = await execGit( cmd, context.workingDirectory, context.requestContext, ); const branches = parseBranchRef(result.stdout); return { mode: 'list' as const, branches, }; } case 'create': { if (!options.branchName) { throw new Error('Branch name is required for create operation'); } args.push(options.branchName); if (options.startPoint) { args.push(options.startPoint); } if (options.force) { args.push('--force'); } const cmd = buildGitCommand({ command: 'branch', args }); await execGit(cmd, context.workingDirectory, context.requestContext); return { mode: 'create' as const, created: options.branchName, }; } case 'delete': { if (!options.branchName) { throw new Error('Branch name is required for delete operation'); } args.push(options.force ? '-D' : '-d', options.branchName); const cmd = buildGitCommand({ command: 'branch', args }); await execGit(cmd, context.workingDirectory, context.requestContext); return { mode: 'delete' as const, deleted: options.branchName, }; } case 'rename': { if (!options.branchName || !options.newBranchName) { throw new Error( 'Both branch names are required for rename operation', ); } args.push('-m', options.branchName, options.newBranchName); if (options.force) { args.push('--force'); } const cmd = buildGitCommand({ command: 'branch', args }); await execGit(cmd, context.workingDirectory, context.requestContext); return { mode: 'rename' as const, renamed: { from: options.branchName, to: options.newBranchName, }, }; } default: throw new Error('Unknown branch operation mode'); } } catch (error) { throw mapGitError(error, 'branch'); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cyanheads/git-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server