diff --git a/index.ts b/index.ts index 3e022ab..97a4b2c 100644 --- a/index.ts +++ b/index.ts @@ -401,7 +401,9 @@ async function createOrUpdateFile( content: string, commitMessage: string, branch: string, - previousPath?: string + previousPath?: string, + last_commit_id?: string, + commit_id?: string ): Promise { const encodedPath = encodeURIComponent(filePath); const url = new URL( @@ -410,7 +412,7 @@ async function createOrUpdateFile( )}/repository/files/${encodedPath}` ); - const body = { + const body: Record = { branch, content, commit_message: commitMessage, @@ -421,13 +423,37 @@ async function createOrUpdateFile( // Check if file exists let method = "POST"; try { - await getFileContents(projectId, filePath, branch); + // Get file contents to check existence and retrieve commit IDs + const fileData = await getFileContents(projectId, filePath, branch); method = "PUT"; + + // If fileData is not an array, it's a file content object with commit IDs + if (!Array.isArray(fileData)) { + // Use commit IDs from the file data if not provided in parameters + if (!commit_id && fileData.commit_id) { + body.commit_id = fileData.commit_id; + } else if (commit_id) { + body.commit_id = commit_id; + } + + if (!last_commit_id && fileData.last_commit_id) { + body.last_commit_id = fileData.last_commit_id; + } else if (last_commit_id) { + body.last_commit_id = last_commit_id; + } + } } catch (error) { if (!(error instanceof Error && error.message.includes("File not found"))) { throw error; } - // File doesn't exist, use POST + // File doesn't exist, use POST - no need for commit IDs for new files + // But still use any provided as parameters if they exist + if (commit_id) { + body.commit_id = commit_id; + } + if (last_commit_id) { + body.last_commit_id = last_commit_id; + } } const response = await fetch(url.toString(), { @@ -934,7 +960,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { args.content, args.commit_message, args.branch, - args.previous_path + args.previous_path, + args.last_commit_id, + args.commit_id ); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], diff --git a/schemas.ts b/schemas.ts index fa1ae3b..f8bc29b 100644 --- a/schemas.ts +++ b/schemas.ts @@ -43,7 +43,9 @@ export const GitLabFileContentSchema = z.object({ content_sha256: z.string(), // Changed from sha to match GitLab API ref: z.string(), // Added as GitLab requires branch reference blob_id: z.string(), // Added to match GitLab API + commit_id: z.string(), // ID of the current file version last_commit_id: z.string(), // Added to match GitLab API + execute_filemode: z.boolean().optional(), // Added to match GitLab API }); export const GitLabDirectoryContentSchema = z.object({ @@ -138,7 +140,7 @@ export const CreateBranchOptionsSchema = z.object({ export const GitLabCreateUpdateFileResponseSchema = z.object({ file_path: z.string(), branch: z.string(), - commit_id: z.string(), // Changed from sha to match GitLab API + commit_id: z.string().optional(), // Optional since it's not always returned by the API content: GitLabFileContentSchema.optional(), }); @@ -263,6 +265,14 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({ .string() .optional() .describe("Path of the file to move/rename"), + last_commit_id: z + .string() + .optional() + .describe("Last known file commit ID"), + commit_id: z + .string() + .optional() + .describe("Current file commit ID (for update operations)"), }); export const SearchRepositoriesSchema = z.object({