fix(gitrepo): 補抓淺層 checkout 的 base 歷史
This commit is contained in:
+40
-8
@@ -44,6 +44,25 @@ function gitTrim(cwd, ...args) {
|
||||
return git(cwd, ...args).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 嘗試同步執行 git 指令,失敗時回傳 false,成功時回傳 true。
|
||||
*
|
||||
* @param {string} cwd - git 工作目錄(repo 的 checkout 路徑)。
|
||||
* @param {...string} args - 傳給 git 的參數。
|
||||
* @returns {boolean} git 指令是否成功結束。
|
||||
* @remarks
|
||||
* 使用情境:修復淺層 checkout 的歷史不足時,部分 fetch 策略可能因 runner
|
||||
* 或遠端版本不同而失敗;呼叫端可依序嘗試多種策略,不讓第一個失敗中斷流程。
|
||||
*/
|
||||
function tryGit(cwd, ...args) {
|
||||
try {
|
||||
git(cwd, ...args);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得目前 HEAD 最新一筆 commit 的訊息標題(commit message 第一行)。
|
||||
*
|
||||
@@ -64,14 +83,14 @@ function latestCommitSubject(cwd) {
|
||||
/**
|
||||
* 解析 PR base 分支與目前 HEAD 的 merge-base commit SHA。
|
||||
*
|
||||
* 先嘗試 `git fetch origin <baseRef>` 更新 base 分支資料(失敗時靜默忽略,
|
||||
* 因 fetch-depth: 0 的 checkout 通常已含 base 分支,可直接沿用本地資料),
|
||||
* 再以 `git merge-base origin/<baseRef> HEAD` 取得共同祖先。
|
||||
* 先以 refspec 明確更新 `origin/<baseRef>`,再以 `git merge-base origin/<baseRef> HEAD`
|
||||
* 取得共同祖先。若 checkout 是淺層歷史而導致 merge-base 失敗,會補抓完整或更深的
|
||||
* base/head 歷史後重試,避免 PR workflow 因 checkout 預設深度不足而中斷。
|
||||
*
|
||||
* @param {string} cwd - git 工作目錄(repo 的 checkout 路徑)。
|
||||
* @param {string} baseRef - PR 目標(base)分支名稱,例如 'master' 或 'develop';不含 'origin/' 前綴。
|
||||
* @returns {string} merge-base 的 commit SHA(40 碼十六進位字串)。
|
||||
* @throws {Error} 本地不存在 origin/<baseRef>、或兩者無共同祖先時,`git merge-base` 失敗並拋出(fetch 失敗不會拋出)。
|
||||
* @throws {Error} 補抓歷史後仍無法取得共同祖先時,丟出含 baseRef 的明確錯誤。
|
||||
* @remarks
|
||||
* 使用情境:AI code review 以此結果作為 diff 比較基準——
|
||||
* 先 `resolveMergeBase(cwd, pr.base.ref)` 取得基準 SHA,
|
||||
@@ -79,12 +98,25 @@ function latestCommitSubject(cwd) {
|
||||
* 避免把 base 分支後續演進誤算進 diff。
|
||||
*/
|
||||
function resolveMergeBase(cwd, baseRef) {
|
||||
const remoteBase = `origin/${baseRef}`;
|
||||
tryGit(cwd, 'fetch', '--no-tags', 'origin', `+refs/heads/${baseRef}:refs/remotes/${remoteBase}`);
|
||||
try {
|
||||
git(cwd, 'fetch', 'origin', baseRef);
|
||||
} catch {
|
||||
// fetch-depth: 0 的 checkout 通常已含 base 分支,抓不到時直接沿用本地資料。
|
||||
return gitTrim(cwd, 'merge-base', remoteBase, 'HEAD');
|
||||
} catch (firstError) {
|
||||
const isShallow = gitTrim(cwd, 'rev-parse', '--is-shallow-repository') === 'true';
|
||||
if (isShallow) {
|
||||
tryGit(cwd, 'fetch', '--no-tags', '--unshallow', 'origin');
|
||||
}
|
||||
tryGit(cwd, 'fetch', '--no-tags', '--deepen=1000', 'origin', `+refs/heads/${baseRef}:refs/remotes/${remoteBase}`);
|
||||
tryGit(cwd, 'fetch', '--no-tags', '--deepen=1000', 'origin', 'HEAD');
|
||||
try {
|
||||
return gitTrim(cwd, 'merge-base', remoteBase, 'HEAD');
|
||||
} catch {
|
||||
const error = new Error(`無法解析 origin/${baseRef} 與 HEAD 的 merge-base;請確認 checkout 有足夠歷史,或設定 checkout fetch-depth: 0。`);
|
||||
error.cause = firstError;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return gitTrim(cwd, 'merge-base', `origin/${baseRef}`, 'HEAD');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user