| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- import { exec } from "child_process"
- import { promisify } from "util"
- import { truncateOutput } from "../integrations/misc/extract-text"
- const execAsync = promisify(exec)
- const GIT_OUTPUT_LINE_LIMIT = 500
- export interface GitCommit {
- hash: string
- shortHash: string
- subject: string
- author: string
- date: string
- }
- async function checkGitRepo(cwd: string): Promise<boolean> {
- try {
- await execAsync("git rev-parse --git-dir", { cwd })
- return true
- } catch (error) {
- return false
- }
- }
- async function checkGitInstalled(): Promise<boolean> {
- try {
- await execAsync("git --version")
- return true
- } catch (error) {
- return false
- }
- }
- export async function searchCommits(query: string, cwd: string): Promise<GitCommit[]> {
- try {
- const isInstalled = await checkGitInstalled()
- if (!isInstalled) {
- console.error("Git is not installed")
- return []
- }
- const isRepo = await checkGitRepo(cwd)
- if (!isRepo) {
- console.error("Not a git repository")
- return []
- }
- // Search commits by hash or message, limiting to 10 results
- const { stdout } = await execAsync(
- `git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` + `--grep="${query}" --regexp-ignore-case`,
- { cwd },
- )
- let output = stdout
- if (!output.trim() && /^[a-f0-9]+$/i.test(query)) {
- // If no results from grep search and query looks like a hash, try searching by hash
- const { stdout: hashStdout } = await execAsync(
- `git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` + `--author-date-order ${query}`,
- { cwd },
- ).catch(() => ({ stdout: "" }))
- if (!hashStdout.trim()) {
- return []
- }
- output = hashStdout
- }
- const commits: GitCommit[] = []
- const lines = output
- .trim()
- .split("\n")
- .filter((line) => line !== "--")
- for (let i = 0; i < lines.length; i += 5) {
- commits.push({
- hash: lines[i],
- shortHash: lines[i + 1],
- subject: lines[i + 2],
- author: lines[i + 3],
- date: lines[i + 4],
- })
- }
- return commits
- } catch (error) {
- console.error("Error searching commits:", error)
- return []
- }
- }
- export async function getCommitInfo(hash: string, cwd: string): Promise<string> {
- try {
- const isInstalled = await checkGitInstalled()
- if (!isInstalled) {
- return "Git is not installed"
- }
- const isRepo = await checkGitRepo(cwd)
- if (!isRepo) {
- return "Not a git repository"
- }
- // Get commit info, stats, and diff separately
- const { stdout: info } = await execAsync(`git show --format="%H%n%h%n%s%n%an%n%ad%n%b" --no-patch ${hash}`, {
- cwd,
- })
- const [fullHash, shortHash, subject, author, date, body] = info.trim().split("\n")
- const { stdout: stats } = await execAsync(`git show --stat --format="" ${hash}`, { cwd })
- const { stdout: diff } = await execAsync(`git show --format="" ${hash}`, { cwd })
- const summary = [
- `Commit: ${shortHash} (${fullHash})`,
- `Author: ${author}`,
- `Date: ${date}`,
- `\nMessage: ${subject}`,
- body ? `\nDescription:\n${body}` : "",
- "\nFiles Changed:",
- stats.trim(),
- "\nFull Changes:",
- ].join("\n")
- const output = summary + "\n\n" + diff.trim()
- return truncateOutput(output, GIT_OUTPUT_LINE_LIMIT)
- } catch (error) {
- console.error("Error getting commit info:", error)
- return `Failed to get commit info: ${error instanceof Error ? error.message : String(error)}`
- }
- }
- export async function getWorkingState(cwd: string): Promise<string> {
- try {
- const isInstalled = await checkGitInstalled()
- if (!isInstalled) {
- return "Git is not installed"
- }
- const isRepo = await checkGitRepo(cwd)
- if (!isRepo) {
- return "Not a git repository"
- }
- // Get status of working directory
- const { stdout: status } = await execAsync("git status --short", { cwd })
- if (!status.trim()) {
- return "No changes in working directory"
- }
- // Get all changes (both staged and unstaged) compared to HEAD
- const { stdout: diff } = await execAsync("git diff HEAD", { cwd })
- const lineLimit = GIT_OUTPUT_LINE_LIMIT
- const output = `Working directory changes:\n\n${status}\n\n${diff}`.trim()
- return truncateOutput(output, lineLimit)
- } catch (error) {
- console.error("Error getting working state:", error)
- return `Failed to get working state: ${error instanceof Error ? error.message : String(error)}`
- }
- }
|