Skip to content

Commit e662109

Browse files
authored
feat(commit): enhance commit message generation with streaming and callback support (#561)
1 parent 4ed60e3 commit e662109

File tree

6 files changed

+90
-46
lines changed

6 files changed

+90
-46
lines changed

src/activate/registerCommands.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,11 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
319319
action: "toggleAutoApprove",
320320
})
321321
},
322-
generateCommitMessage: async () => {
322+
generateCommitMessage: async (e: any) => {
323323
try {
324-
await handleGenerateCommitMessage(provider)
324+
await handleGenerateCommitMessage(provider, (mssage: string) => {
325+
e.inputBox.value = mssage
326+
})
325327
} catch (error) {
326328
const errorMessage = error instanceof Error ? error.message : String(error)
327329
vscode.window.showErrorMessage(`Failed to generate commit message: ${errorMessage}`)

src/api/providers/zgsm.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,10 +684,11 @@ export class ZgsmAiHandler extends BaseProvider implements SingleCompletionHandl
684684
| OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming,
685685
modelInfo: ModelInfo,
686686
): void {
687-
const _m = requestOptions.model.toLocaleLowerCase()
688-
689687
// Only add max_completion_tokens if includeMaxTokens is true
690-
if (this.options.includeMaxTokens === true || _m.includes("moonshot") || _m.includes("kimi")) {
688+
if (
689+
(this.options.includeMaxTokens === true && this.options.useZgsmCustomConfig) ||
690+
!this.options.useZgsmCustomConfig
691+
) {
691692
// Use user-configured modelMaxTokens if available, otherwise fall back to model's default maxTokens
692693
// Using max_completion_tokens as max_tokens is deprecated
693694
requestOptions[modelInfo.supportsMaxTokens ? "max_tokens" : "max_completion_tokens"] =

src/core/costrict/commit/__tests__/commitGenerator.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { describe, it, expect, vi, beforeEach } from "vitest"
22
import { CommitMessageGenerator } from "../commitGenerator"
33
import type { GitDiffInfo, CommitGenerationOptions } from "../types"
44

5-
// Mock child_process exec
5+
// Mock child_process
66
vi.mock("child_process", () => ({
77
exec: vi.fn(),
8+
spawn: vi.fn(),
89
}))
910

1011
// Mock vscode
@@ -305,5 +306,25 @@ index 1234567..89abcde 100644
305306
expect(result).toContain('console.log("hello")')
306307
expect(result).toContain('console.log("world")')
307308
})
309+
310+
describe("getGitDiffStreaming", () => {
311+
it("should handle repository without commits", async () => {
312+
// 由于测试环境的限制,我们只验证代码逻辑是否正确
313+
// 实际的 Git 操作将在真实环境中验证
314+
315+
// 创建 CommitMessageGenerator 实例
316+
const generator = new CommitMessageGenerator("/test/workspace")
317+
318+
// 验证 runGitDiff 方法存在
319+
expect(typeof (generator as any).runGitDiff).toBe("function")
320+
321+
// 验证 getGitDiffStreaming 方法存在
322+
expect(typeof (generator as any).getGitDiffStreaming).toBe("function")
323+
324+
// 这个测试主要确保我们的代码修改不会导致语法错误
325+
// 实际的功能测试将在真实环境中进行
326+
expect(true).toBe(true)
327+
})
328+
})
308329
})
309330
})

src/core/costrict/commit/commitGenerator.ts

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { truncateOutput } from "../../../integrations/misc/extract-text"
1212

1313
const execAsync = promisify(exec)
1414

15-
const GIT_OUTPUT_CHAR_LIMIT = 40000
15+
const GIT_OUTPUT_CHAR_LIMIT = 50_000
1616
/**
1717
* Commit message generator service
1818
*/
@@ -71,40 +71,56 @@ export class CommitMessageGenerator {
7171
*/
7272
private async getGitDiffStreaming(): Promise<string> {
7373
return new Promise((resolve, reject) => {
74-
const git = spawn("git", ["diff", "HEAD"], { cwd: this.workspaceRoot })
75-
let output = ""
76-
let outputSize = 0
77-
const MAX_OUTPUT_SIZE = 1024 * 1024 * 10 // 10MB limit
78-
79-
git.stdout.on("data", (data) => {
80-
const chunk = data.toString()
81-
outputSize += chunk.length
82-
83-
// Check if we're approaching the size limit
84-
if (outputSize > MAX_OUTPUT_SIZE) {
85-
git.kill()
86-
reject(new Error(`Git diff output exceeds size limit of ${MAX_OUTPUT_SIZE} bytes`))
87-
return
88-
}
74+
// 首先检查是否有任何提交
75+
execAsync("git rev-parse --verify HEAD", { cwd: this.workspaceRoot })
76+
.then(() => {
77+
// 有提交,使用 git diff HEAD
78+
this.runGitDiff(["diff", "HEAD"], resolve, reject)
79+
})
80+
.catch(() => {
81+
// 没有提交,使用 git diff 获取所有更改
82+
this.runGitDiff(["diff"], resolve, reject)
83+
})
84+
})
85+
}
8986

90-
output += chunk
91-
})
87+
/**
88+
* Run git diff command with specified arguments
89+
*/
90+
private runGitDiff(args: string[], resolve: (value: string) => void, reject: (reason?: any) => void): void {
91+
const git = spawn("git", args, { cwd: this.workspaceRoot })
92+
let output = ""
93+
let outputSize = 0
94+
const MAX_OUTPUT_SIZE = 1024 * 1024 * 10 // 10MB limit
95+
96+
git.stdout.on("data", (data) => {
97+
const chunk = data.toString()
98+
outputSize += chunk.length
99+
100+
// Check if we're approaching the size limit
101+
if (outputSize > MAX_OUTPUT_SIZE) {
102+
git.kill()
103+
reject(new Error(`Git diff output exceeds size limit of ${MAX_OUTPUT_SIZE} bytes`))
104+
return
105+
}
92106

93-
git.stderr.on("data", (data) => {
94-
console.error("Git diff stderr:", data.toString())
95-
})
107+
output += chunk
108+
})
96109

97-
git.on("close", (code) => {
98-
if (code === 0) {
99-
resolve(output)
100-
} else {
101-
reject(new Error(`Git diff process exited with code ${code}`))
102-
}
103-
})
110+
git.stderr.on("data", (data) => {
111+
console.error("Git diff stderr:", data.toString())
112+
})
104113

105-
git.on("error", (error) => {
106-
reject(error)
107-
})
114+
git.on("close", (code) => {
115+
if (code === 0) {
116+
resolve(output)
117+
} else {
118+
reject(new Error(`Git diff process exited with code ${code}`))
119+
}
120+
})
121+
122+
git.on("error", (error) => {
123+
reject(error)
108124
})
109125
}
110126

src/core/costrict/commit/commitService.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class CommitService {
2929
/**
3030
* Generate commit message and populate it in SCM input
3131
*/
32-
async generateAndPopulateCommitMessage(): Promise<void> {
32+
async generateAndPopulateCommitMessage(cb: any): Promise<void> {
3333
if (!this.generator) {
3434
throw new Error(t("commit:commit.error.notInitialized"))
3535
}
@@ -64,7 +64,7 @@ export class CommitService {
6464
progress.report({ increment: 80 })
6565

6666
// Populate the commit message in SCM input
67-
await this.populateCommitMessage(suggestion)
67+
await this.populateCommitMessage(suggestion, cb)
6868

6969
progress.report({ increment: 100 })
7070
},
@@ -78,18 +78,22 @@ export class CommitService {
7878
/**
7979
* Populate the generated commit message in SCM input
8080
*/
81-
private async populateCommitMessage(suggestion: CommitMessageSuggestion): Promise<void> {
81+
private async populateCommitMessage(suggestion: CommitMessageSuggestion, cb: any): Promise<void> {
8282
// Build the full commit message
8383
let commitMessage = suggestion.subject
8484

8585
if (suggestion.body) {
8686
commitMessage += `\n\n${suggestion.body}`
8787
}
8888
try {
89-
const gitExtension = vscode.extensions.getExtension("vscode.git")?.exports
90-
const api = gitExtension.getAPI(1)
91-
const repo = api.repositories[0]
92-
repo.inputBox.value = commitMessage
89+
if (cb && cb instanceof Function) {
90+
cb(commitMessage)
91+
} else {
92+
// const gitExtension = vscode.extensions.getExtension("vscode.git")?.exports
93+
// const api = gitExtension.getAPI(1)
94+
// const repo = api.repositories[0]
95+
// repo.inputBox.value = commitMessage
96+
}
9397
} catch (error) {
9498
await vscode.env.clipboard.writeText(commitMessage)
9599
vscode.window.showInformationMessage(t("commit:commit.message.copiedToClipboard"))

src/core/costrict/commit/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let commitServiceInstance: CommitService | null = null
2121
// Execution lock to prevent concurrent calls
2222
let isExecuting = false
2323

24-
export async function handleGenerateCommitMessage(provider: ClineProvider): Promise<void> {
24+
export async function handleGenerateCommitMessage(provider: ClineProvider, cb: any): Promise<void> {
2525
const workspaceRoot = CommitService.getWorkspaceRoot()
2626
if (!workspaceRoot) {
2727
throw new Error(t("commit:commit.error.noWorkspace"))
@@ -48,7 +48,7 @@ export async function handleGenerateCommitMessage(provider: ClineProvider): Prom
4848
commitServiceInstance.initialize(workspaceRoot, provider)
4949
}
5050

51-
await commitServiceInstance.generateAndPopulateCommitMessage()
51+
await commitServiceInstance.generateAndPopulateCommitMessage(cb)
5252
isExecuting = false
5353
} catch (error) {
5454
// Reset instance on error to allow recovery

0 commit comments

Comments
 (0)