From e1897f2d33091d1b11bc980992ef987e3c39f90f Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 17 Sep 2026 07:07:01 +0000 Subject: [PATCH] =?UTF-8?q?test(helpers):=20=E5=8A=A0=E4=B8=8A=E3=80=8C?= =?UTF-8?q?=E6=9C=89=E9=81=A0=E7=AB=AF=E3=80=8D=E7=9A=84=E8=87=A8=E6=99=82?= =?UTF-8?q?=20repo=EF=BC=8C=E4=B8=A6=E6=94=B6=E6=8E=89=E4=B8=89=E4=BB=BD?= =?UTF-8?q?=E9=87=8D=E8=A4=87=E7=9A=84=20git=20=E5=9F=B7=E8=A1=8C=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit branch-prep 的重點行為都繞著遠端打轉——來源分支在遠端已存在時要 pull 而不是重建、 目標分支已存在時不能覆蓋他人進度。這些事沒有遠端就驗不出來,所以補一個 bare origin 加工作用 clone,並附 pushFromElsewhere 模擬「別人推了東西上去」。 順手把 makeTempRepo 與新 helper 各自重寫一次的 git 執行器與 seed 收成共用的兩支。 Co-Authored-By: Claude Opus 5 (1M context) --- test/helpers/temp-repo.js | 80 +++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/test/helpers/temp-repo.js b/test/helpers/temp-repo.js index 39ad0b2..e7235ca 100644 --- a/test/helpers/temp-repo.js +++ b/test/helpers/temp-repo.js @@ -13,19 +13,77 @@ import { tmpRoot } from './run-script.js'; export function makeTempRepo() { mkdirSync(tmpRoot, { recursive: true }); const dir = mkdtempSync(join(tmpRoot, 'repo-')); - const git = (...args) => - execFileSync('git', args, { - cwd: dir, - encoding: 'utf8', - env: { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' }, - }); + const git = gitIn(dir); git('init', '-q', '-b', 'master'); - git('config', 'user.name', 'tester'); - git('config', 'user.email', 'tester@example.com'); - writeFileSync(join(dir, 'README.md'), '# temp\n'); - git('add', '-A'); - git('commit', '-qm', 'initial'); + seed(git, dir, 'tester', 'tester@example.com'); return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) }; } + +/** + * 綁在某個目錄上的 git 執行器。 + * 全域與系統設定一律切掉:測試的結果不該隨開發者機器上的 git 設定而變。 + */ +function gitIn(cwd) { + return (...args) => + execFileSync('git', args, { + cwd, + encoding: 'utf8', + env: { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' }, + }).trim(); +} + +/** 設好身分並放一顆起始 commit,讓 repo 有東西可以長分支 */ +function seed(git, dir, name, email) { + git('config', 'user.name', name); + git('config', 'user.email', email); + writeFileSync(join(dir, 'README.md'), '# temp\n'); + git('add', '-A'); + git('commit', '-qm', 'initial'); +} + +/** + * 開一個「有遠端」的臨時 repo:一個 bare 當 origin,一個工作用的 clone。 + * + * branch-prep 的重點行為都繞著遠端打轉——來源分支在遠端已存在時要 pull 而不是重建、 + * 目標分支已存在時不能覆蓋他人進度。這些事沒有遠端就驗不出來。 + * git 不做 mock:在臨時 repo 上跑真的 git 比假的 git 可信,成本也低。 + * + * @returns {{dir: string, origin: string, git: Function, pushFromElsewhere: Function, cleanup: Function}} + * dir 為工作用 clone,origin 為 bare repo,pushFromElsewhere 用來模擬別人推東西上去 + */ +export function makeTempRepoWithRemote() { + mkdirSync(tmpRoot, { recursive: true }); + const base = mkdtempSync(join(tmpRoot, 'remote-')); + const origin = join(base, 'origin.git'); + const dir = join(base, 'work'); + + execFileSync('git', ['init', '-q', '--bare', '-b', 'master', origin]); + execFileSync('git', ['clone', '-q', origin, dir]); + + const git = gitIn(dir); + seed(git, dir, 'tester', 'tester@example.com'); + git('push', '-q', 'origin', 'master'); + + return { + dir, + origin, + git, + /** 模擬「別人推了東西上去」:在 origin 上直接造一顆 commit */ + pushFromElsewhere: (branch, filename, content) => { + const other = join(base, `other-${filename}`); + execFileSync('git', ['clone', '-q', origin, other]); + const otherGit = gitIn(other); + otherGit('config', 'user.name', 'someone-else'); + otherGit('config', 'user.email', 'other@example.com'); + otherGit('checkout', '-q', '-B', branch); + writeFileSync(join(other, filename), content); + otherGit('add', '-A'); + otherGit('commit', '-qm', `別人的進度:${filename}`); + otherGit('push', '-q', 'origin', branch); + rmSync(other, { recursive: true, force: true }); + }, + cleanup: () => rmSync(base, { recursive: true, force: true }), + }; +}