118 lines
4.1 KiB
Java
118 lines
4.1 KiB
Java
package com.codechecker.cache.git;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.file.Path;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* 基于 git 命令的差异扫描与双版本文件内容读取。
|
|
*
|
|
* <p>通过 {@code git diff --name-only} 获取变更的 .java 文件,通过 {@code git show sha:path}
|
|
* 读取指定提交下的文件内容,避免检出两个完整 worktree。</p>
|
|
*/
|
|
public class GitDiffScanner {
|
|
|
|
private final Path repoRoot;
|
|
|
|
public GitDiffScanner(Path repoRoot) {
|
|
this.repoRoot = repoRoot;
|
|
}
|
|
|
|
/**
|
|
* 返回 old..new 之间变更的 Java 文件路径(相对仓库根,使用 / 分隔)。
|
|
*/
|
|
public List<String> changedJavaFiles(String oldSha, String newSha) throws GitException {
|
|
List<String> lines = runLines(
|
|
"git", "diff", "--name-only", "--diff-filter=ACMR", oldSha, newSha, "--", "*.java");
|
|
List<String> result = new ArrayList<>();
|
|
for (String line : lines) {
|
|
String trimmed = line.trim();
|
|
if (!trimmed.isEmpty() && trimmed.endsWith(".java")) {
|
|
result.add(trimmed);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 读取某提交下指定文件的内容;文件在该提交不存在时返回 null。
|
|
*/
|
|
public String fileContentAt(String sha, String path) throws GitException {
|
|
try {
|
|
ProcessResult pr = run("git", "show", sha + ":" + path);
|
|
if (pr.exitCode != 0) {
|
|
return null;
|
|
}
|
|
return pr.stdout;
|
|
} catch (GitException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private List<String> runLines(String... command) throws GitException {
|
|
ProcessResult pr = run(command);
|
|
if (pr.exitCode != 0) {
|
|
throw new GitException("git 命令执行失败(exit=" + pr.exitCode + "): " + String.join(" ", command)
|
|
+ "\n" + pr.stderr);
|
|
}
|
|
List<String> lines = new ArrayList<>();
|
|
try (BufferedReader reader = new BufferedReader(
|
|
new InputStreamReader(new java.io.ByteArrayInputStream(
|
|
pr.stdout.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8))) {
|
|
String line;
|
|
while ((line = reader.readLine()) != null) {
|
|
lines.add(line);
|
|
}
|
|
} catch (IOException e) {
|
|
throw new GitException("读取 git 输出失败", e);
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
private ProcessResult run(String... command) throws GitException {
|
|
ProcessBuilder pb = new ProcessBuilder(command);
|
|
pb.directory(repoRoot.toFile());
|
|
pb.redirectErrorStream(false);
|
|
try {
|
|
Process process = pb.start();
|
|
String stdout = readStream(process.getInputStream());
|
|
String stderr = readStream(process.getErrorStream());
|
|
int exit = process.waitFor();
|
|
return new ProcessResult(exit, stdout, stderr);
|
|
} catch (IOException e) {
|
|
throw new GitException("无法启动 git 进程: " + String.join(" ", command), e);
|
|
} catch (InterruptedException e) {
|
|
Thread.currentThread().interrupt();
|
|
throw new GitException("git 进程被中断", e);
|
|
}
|
|
}
|
|
|
|
private static String readStream(InputStream in) throws IOException {
|
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
byte[] chunk = new byte[8192];
|
|
int read;
|
|
while ((read = in.read(chunk)) != -1) {
|
|
buffer.write(chunk, 0, read);
|
|
}
|
|
return new String(buffer.toByteArray(), StandardCharsets.UTF_8);
|
|
}
|
|
|
|
private static final class ProcessResult {
|
|
final int exitCode;
|
|
final String stdout;
|
|
final String stderr;
|
|
|
|
ProcessResult(int exitCode, String stdout, String stderr) {
|
|
this.exitCode = exitCode;
|
|
this.stdout = stdout;
|
|
this.stderr = stderr;
|
|
}
|
|
}
|
|
}
|