Include parent commit hashes in raw dump commit reporting. - git log format now includes %P (parent hashes) - parseCommitLog extracts parent_ids as string array - CommitPayload type extended with parent_ids field - merge commits may have multiple parents Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
118 lines
3.6 KiB
TypeScript
118 lines
3.6 KiB
TypeScript
/**
|
||
* Raw Dump Git 辅助函数
|
||
* 仅依赖 node:child_process,与框架解耦
|
||
*/
|
||
|
||
import { execFile } from 'node:child_process'
|
||
import { promisify } from 'node:util'
|
||
|
||
const execFileAsync = promisify(execFile)
|
||
|
||
async function gitExec(args: string[], cwd: string): Promise<string> {
|
||
try {
|
||
const { stdout } = await execFileAsync('git', args, {
|
||
cwd,
|
||
encoding: 'utf-8',
|
||
maxBuffer: 50 * 1024 * 1024, // 50MB
|
||
windowsHide: true,
|
||
})
|
||
return stdout.trim()
|
||
} catch {
|
||
return ''
|
||
}
|
||
}
|
||
|
||
export async function getRepoInfo(cwd: string) {
|
||
const [repoAddr, repoBranch, gitUserName, gitUserEmail] = await Promise.all([
|
||
gitExec(['remote', 'get-url', 'origin'], cwd),
|
||
gitExec(['branch', '--show-current'], cwd),
|
||
gitExec(['config', 'user.name'], cwd),
|
||
gitExec(['config', 'user.email'], cwd),
|
||
])
|
||
|
||
return {
|
||
repo_addr: repoAddr,
|
||
repo_branch: repoBranch,
|
||
git_user_name: gitUserName,
|
||
git_user_email: gitUserEmail,
|
||
}
|
||
}
|
||
|
||
export async function getRawDiff(cwd: string, from?: string, to?: string): Promise<string> {
|
||
if (from && to && from !== to) {
|
||
return gitExec(['diff', '--no-ext-diff', from, to], cwd)
|
||
}
|
||
// Fallback: diff working tree against HEAD
|
||
return gitExec(['diff', 'HEAD'], cwd)
|
||
}
|
||
|
||
export async function getWorkingTreeDiff(cwd: string): Promise<string> {
|
||
return gitExec(['diff', 'HEAD'], cwd)
|
||
}
|
||
|
||
export function countDiffLines(diff: string): number {
|
||
let count = 0
|
||
for (const line of diff.split('\n')) {
|
||
if (line.startsWith('+') && !line.startsWith('+++')) count++
|
||
}
|
||
if (count === 0 && diff.trim()) return diff.trim().split('\n').length
|
||
return count
|
||
}
|
||
|
||
export function extractFilesFromDiff(diff: string): string[] {
|
||
const files = new Set<string>()
|
||
for (const line of diff.split('\n')) {
|
||
if (line.startsWith('+++ b/')) files.add(line.slice(6).trim())
|
||
else if (line.startsWith('--- a/')) files.add(line.slice(6).trim())
|
||
else if (line.startsWith('diff --git ')) {
|
||
const match = line.match(/^diff --git "?a\/(.+?)"? "?b\/(.+?)"?$/)
|
||
if (match?.[2]) files.add(match[2])
|
||
}
|
||
}
|
||
return Array.from(files)
|
||
}
|
||
|
||
export function parseCommitLog(output: string): Array<{
|
||
commit_id: string
|
||
commit_time: string
|
||
git_user_name: string
|
||
git_user_email: string
|
||
parent_ids: string[]
|
||
subject: string
|
||
}> {
|
||
if (!output.trim()) return []
|
||
return output
|
||
.split('\n')
|
||
.map((line) => {
|
||
const [commit_id, commit_time, git_user_name, git_user_email, parent_ids_str, ...rest] = line.split('|')
|
||
if (!commit_id || !git_user_email) return null
|
||
const parent_ids = parent_ids_str ? parent_ids_str.trim().split(' ').filter(Boolean) : []
|
||
return { commit_id, commit_time, git_user_name, git_user_email, parent_ids, subject: rest.join('|') }
|
||
})
|
||
.filter((item): item is NonNullable<typeof item> => !!item)
|
||
}
|
||
|
||
export async function getCommitLog(cwd: string, lastCommit?: string): Promise<string> {
|
||
const authorEmail = await gitExec(['config', 'user.email'], cwd)
|
||
const authorFilter = authorEmail ? ['--author', authorEmail] : []
|
||
|
||
if (lastCommit) {
|
||
return gitExec(
|
||
['log', `${lastCommit}..HEAD`, '--reverse', '--max-count=50', ...authorFilter, '--format=%H|%aI|%an|%ae|%P|%s'],
|
||
cwd,
|
||
)
|
||
}
|
||
return gitExec(
|
||
['log', '--since=1 day ago', '--reverse', '--max-count=50', ...authorFilter, '--format=%H|%aI|%an|%ae|%P|%s'],
|
||
cwd,
|
||
)
|
||
}
|
||
|
||
export async function getCommitDiff(cwd: string, commitId: string): Promise<string> {
|
||
return gitExec(['show', '--format=', '--diff-filter=ACDMR', commitId], cwd)
|
||
}
|
||
|
||
export function toCommitComment(subject: string): string {
|
||
return Array.from(subject).slice(0, 150).join('')
|
||
}
|