Skip to content

Commit c834ebc

Browse files
author
Martim Pimentel
committed
feat: add branch comparison functionality and update related schemas
1 parent 005b46a commit c834ebc

File tree

2 files changed

+105
-15
lines changed

2 files changed

+105
-15
lines changed

index.ts

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import {
5353
CreateMergeRequestSchema,
5454
ForkRepositorySchema,
5555
CreateBranchSchema,
56-
GitLabMergeRequestDiffSchema,
56+
GitLabDiffSchema,
5757
GetMergeRequestSchema,
5858
GetMergeRequestDiffsSchema,
5959
UpdateMergeRequestSchema,
@@ -126,6 +126,9 @@ import {
126126
GetRepositoryTreeSchema,
127127
type GitLabTreeItem,
128128
type GetRepositoryTreeOptions,
129+
GitLabCompareResult,
130+
GitLabCompareResultSchema,
131+
GetBranchDiffsSchema,
129132
} from "./schemas.js";
130133

131134
/**
@@ -261,6 +264,12 @@ const allTools = [
261264
"Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)",
262265
inputSchema: zodToJsonSchema(GetMergeRequestDiffsSchema),
263266
},
267+
{
268+
name: "get_branch_diffs",
269+
description:
270+
"Get the changes/diffs between two branches or commits in a GitLab project",
271+
inputSchema: zodToJsonSchema(GetBranchDiffsSchema),
272+
},
264273
{
265274
name: "update_merge_request",
266275
description:
@@ -436,6 +445,7 @@ const readOnlyTools = [
436445
"get_file_contents",
437446
"get_merge_request",
438447
"get_merge_request_diffs",
448+
"get_branch_diffs",
439449
"mr_discussions",
440450
"list_issues",
441451
"get_issue",
@@ -1552,7 +1562,50 @@ async function getMergeRequestDiffs(
15521562

15531563
await handleGitLabError(response);
15541564
const data = (await response.json()) as { changes: unknown };
1555-
return z.array(GitLabMergeRequestDiffSchema).parse(data.changes);
1565+
return z.array(GitLabDiffSchema).parse(data.changes);
1566+
}
1567+
1568+
/**
1569+
* Get branch comparison diffs
1570+
*
1571+
* @param {string} projectId - The ID or URL-encoded path of the project
1572+
* @param {string} from - The branch name or commit SHA to compare from
1573+
* @param {string} to - The branch name or commit SHA to compare to
1574+
* @param {boolean} [straight] - Comparison method: false for '...' (default), true for '--'
1575+
* @returns {Promise<GitLabCompareResult>} Branch comparison results
1576+
*/
1577+
async function getBranchDiffs(
1578+
projectId: string,
1579+
from: string,
1580+
to: string,
1581+
straight?: boolean
1582+
): Promise<GitLabCompareResult> {
1583+
projectId = decodeURIComponent(projectId); // Decode project ID
1584+
1585+
const url = new URL(
1586+
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/repository/compare`
1587+
);
1588+
1589+
url.searchParams.append("from", from);
1590+
url.searchParams.append("to", to);
1591+
1592+
if (straight !== undefined) {
1593+
url.searchParams.append("straight", straight.toString());
1594+
}
1595+
1596+
const response = await fetch(url.toString(), {
1597+
...DEFAULT_FETCH_CONFIG,
1598+
});
1599+
1600+
if (!response.ok) {
1601+
const errorBody = await response.text();
1602+
throw new Error(
1603+
`GitLab API error: ${response.status} ${response.statusText}\n${errorBody}`
1604+
);
1605+
}
1606+
1607+
const data = await response.json();
1608+
return GitLabCompareResultSchema.parse(data);
15561609
}
15571610

15581611
/**
@@ -2414,6 +2467,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
24142467
};
24152468
}
24162469

2470+
case "get_branch_diffs": {
2471+
const args = GetBranchDiffsSchema.parse(request.params.arguments);
2472+
const diffs = await getBranchDiffs(
2473+
args.project_id,
2474+
args.from,
2475+
args.to,
2476+
args.straight
2477+
);
2478+
return {
2479+
content: [{ type: "text", text: JSON.stringify(diffs, null, 2) }],
2480+
};
2481+
}
2482+
24172483
case "search_repositories": {
24182484
const args = SearchRepositoriesSchema.parse(request.params.arguments);
24192485
const results = await searchProjects(

schemas.ts

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,15 @@ export const CreateMergeRequestOptionsSchema = z.object({
271271
draft: z.boolean().optional(),
272272
});
273273

274-
export const CreateBranchOptionsSchema = z.object({
275-
name: z.string(), // Changed from ref to match GitLab API
276-
ref: z.string(), // The source branch/commit for the new branch
274+
export const GitLabDiffSchema = z.object({
275+
old_path: z.string(),
276+
new_path: z.string(),
277+
a_mode: z.string(),
278+
b_mode: z.string(),
279+
diff: z.string(),
280+
new_file: z.boolean(),
281+
renamed_file: z.boolean(),
282+
deleted_file: z.boolean(),
277283
});
278284

279285
// Response schemas for operations
@@ -291,6 +297,27 @@ export const GitLabSearchResponseSchema = z.object({
291297
items: z.array(GitLabRepositorySchema),
292298
});
293299

300+
// create branch schemas
301+
export const CreateBranchOptionsSchema = z.object({
302+
name: z.string(), // Changed from ref to match GitLab API
303+
ref: z.string(), // The source branch/commit for the new branch
304+
});
305+
306+
export const GitLabCompareResultSchema = z.object({
307+
commit: z.object({
308+
id: z.string().optional(),
309+
short_id: z.string().optional(),
310+
title: z.string().optional(),
311+
author_name: z.string().optional(),
312+
author_email: z.string().optional(),
313+
created_at: z.string().optional(),
314+
}).optional(),
315+
commits: z.array(GitLabCommitSchema),
316+
diffs: z.array(GitLabDiffSchema),
317+
compare_timeout: z.boolean().optional(),
318+
compare_same_ref: z.boolean().optional(),
319+
});
320+
294321
// Issue related schemas
295322
export const GitLabLabelSchema = z.object({
296323
id: z.number(),
@@ -596,20 +623,16 @@ export const ForkRepositorySchema = ProjectParamsSchema.extend({
596623
namespace: z.string().optional().describe("Namespace to fork to (full path)"),
597624
});
598625

626+
// Branch related schemas
599627
export const CreateBranchSchema = ProjectParamsSchema.extend({
600628
branch: z.string().describe("Name for the new branch"),
601629
ref: z.string().optional().describe("Source branch/commit for new branch"),
602630
});
603631

604-
export const GitLabMergeRequestDiffSchema = z.object({
605-
old_path: z.string(),
606-
new_path: z.string(),
607-
a_mode: z.string(),
608-
b_mode: z.string(),
609-
diff: z.string(),
610-
new_file: z.boolean(),
611-
renamed_file: z.boolean(),
612-
deleted_file: z.boolean(),
632+
export const GetBranchDiffsSchema = ProjectParamsSchema.extend({
633+
from: z.string().describe("The base branch or commit SHA to compare from"),
634+
to: z.string().describe("The target branch or commit SHA to compare to"),
635+
straight: z.boolean().optional().describe("Comparison method: false for '...' (default), true for '--'"),
613636
});
614637

615638
export const GetMergeRequestSchema = ProjectParamsSchema.extend({
@@ -1082,6 +1105,7 @@ export type GitLabDirectoryContent = z.infer<
10821105
export type GitLabContent = z.infer<typeof GitLabContentSchema>;
10831106
export type FileOperation = z.infer<typeof FileOperationSchema>;
10841107
export type GitLabTree = z.infer<typeof GitLabTreeSchema>;
1108+
export type GitLabCompareResult = z.infer<typeof GitLabCompareResultSchema>;
10851109
export type GitLabCommit = z.infer<typeof GitLabCommitSchema>;
10861110
export type GitLabReference = z.infer<typeof GitLabReferenceSchema>;
10871111
export type CreateRepositoryOptions = z.infer<
@@ -1097,7 +1121,7 @@ export type GitLabCreateUpdateFileResponse = z.infer<
10971121
>;
10981122
export type GitLabSearchResponse = z.infer<typeof GitLabSearchResponseSchema>;
10991123
export type GitLabMergeRequestDiff = z.infer<
1100-
typeof GitLabMergeRequestDiffSchema
1124+
typeof GitLabDiffSchema
11011125
>;
11021126
export type CreateNoteOptions = z.infer<typeof CreateNoteSchema>;
11031127
export type GitLabIssueLink = z.infer<typeof GitLabIssueLinkSchema>;

0 commit comments

Comments
 (0)