(workingDir: string)
| 1027 | } |
| 1028 | |
| 1029 | async function collectGitSummaryData(workingDir: string): Promise<GitSummaryData> { |
| 1030 | // Get current branch name (returns "HEAD" if detached) |
| 1031 | const branchResult = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir) |
| 1032 | let branch: string | null = null |
| 1033 | if (branchResult.success) { |
| 1034 | const branchName = branchResult.stdout.trim() |
| 1035 | branch = branchName === "HEAD" ? null : branchName |
| 1036 | } |
| 1037 | |
| 1038 | // Get short HEAD commit SHA |
| 1039 | const headResult = await execGit(["rev-parse", "--short", "HEAD"], workingDir) |
| 1040 | const headCommit = headResult.success ? headResult.stdout.trim() : "" |
| 1041 | |
| 1042 | // Get ahead count (commits ahead of upstream tracking branch) |
| 1043 | let ahead: number | null = null |
| 1044 | if (branch) { |
| 1045 | const aheadResult = await execGit(["rev-list", "--count", "@{upstream}..HEAD"], workingDir) |
| 1046 | if (aheadResult.success) { |
| 1047 | ahead = parseInt(aheadResult.stdout.trim(), 10) || 0 |
| 1048 | } else { |
| 1049 | const fallbackResult = await execGit(["rev-list", "--count", "origin/HEAD..HEAD"], workingDir) |
| 1050 | if (fallbackResult.success) { |
| 1051 | ahead = parseInt(fallbackResult.stdout.trim(), 10) || 0 |
| 1052 | } |
| 1053 | } |
| 1054 | } |
| 1055 | |
| 1056 | // Use numstat + name-status so tray refreshes avoid generating full patches. |
| 1057 | const stagedNumstatResult = await execGit(["diff", "--cached", "--numstat", "-M"], workingDir) |
| 1058 | const stagedNumstat = stagedNumstatResult.success ? stagedNumstatResult.stdout : "" |
| 1059 | const stagedFiles = stagedNumstatResult.success |
| 1060 | ? parseNumstatOutput(stagedNumstatResult.stdout) |
| 1061 | : [] |
| 1062 | const stagedStats = parseNumstatStats(stagedNumstat) |
| 1063 | |
| 1064 | const stagedNameStatusResult = await execGit(["diff", "--cached", "--name-status", "-M"], workingDir) |
| 1065 | if (stagedNameStatusResult.success) { |
| 1066 | mergeFileStatuses(stagedFiles, parseNameStatusOutput(stagedNameStatusResult.stdout)) |
| 1067 | } |
| 1068 | |
| 1069 | const unstagedNumstatResult = await execGit(["diff", "--numstat", "-M"], workingDir) |
| 1070 | const unstagedNumstat = unstagedNumstatResult.success ? unstagedNumstatResult.stdout : "" |
| 1071 | const unstagedFiles = unstagedNumstatResult.success |
| 1072 | ? parseNumstatOutput(unstagedNumstatResult.stdout) |
| 1073 | : [] |
| 1074 | const unstagedStats = parseNumstatStats(unstagedNumstat) |
| 1075 | |
| 1076 | const unstagedNameStatusResult = await execGit(["diff", "--name-status", "-M"], workingDir) |
| 1077 | if (unstagedNameStatusResult.success) { |
| 1078 | mergeFileStatuses(unstagedFiles, parseNameStatusOutput(unstagedNameStatusResult.stdout)) |
| 1079 | } |
| 1080 | |
| 1081 | const untrackedResult = await execGit(["ls-files", "--others", "--exclude-standard"], workingDir) |
| 1082 | const untracked: GitFileInfo[] = untrackedResult.success |
| 1083 | ? untrackedResult.stdout |
| 1084 | .split("\n") |
| 1085 | .filter((f: string) => f.trim()) |
| 1086 | .map((filePath: string) => ({ path: filePath, binary: isBinaryByExtension(filePath) })) |
no test coverage detected