115 lines
4.3 KiB
JavaScript
115 lines
4.3 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, writeFileSync, rmSync, readFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { Git } from './git.js';
|
|
|
|
function g(cwd, args) {
|
|
return execFileSync('git', args, { cwd, encoding: 'utf8' });
|
|
}
|
|
|
|
/** 建立 bare remote + working clone,於 target/source 製造會衝突的變更。 */
|
|
function setupRepo() {
|
|
const root = mkdtempSync(join(tmpdir(), 'git-test-'));
|
|
const bare = join(root, 'remote.git');
|
|
const work = join(root, 'work');
|
|
execFileSync('git', ['init', '-q', '--bare', bare]);
|
|
execFileSync('git', ['clone', '-q', bare, work]);
|
|
g(work, ['config', 'user.email', 'test@example.com']);
|
|
g(work, ['config', 'user.name', 'tester']);
|
|
g(work, ['config', 'commit.gpgsign', 'false']);
|
|
|
|
writeFileSync(join(work, 'file.txt'), 'base\n');
|
|
g(work, ['add', '-A']);
|
|
g(work, ['commit', '-q', '-m', 'base']);
|
|
const def = g(work, ['rev-parse', '--abbrev-ref', 'HEAD']).trim();
|
|
|
|
g(work, ['checkout', '-q', '-b', 'target']);
|
|
writeFileSync(join(work, 'file.txt'), 'target change\n');
|
|
g(work, ['commit', '-qam', 'target change']);
|
|
|
|
g(work, ['checkout', '-q', def]);
|
|
g(work, ['checkout', '-q', '-b', 'source']);
|
|
writeFileSync(join(work, 'file.txt'), 'source change\n');
|
|
g(work, ['commit', '-qam', 'source change']);
|
|
|
|
g(work, ['push', '-q', 'origin', 'target', 'source', def]);
|
|
return { root, bare, work };
|
|
}
|
|
|
|
test('detectConflict: 偵測到衝突並回傳衝突檔,事後還原暫存分支', () => {
|
|
const { root, bare, work } = setupRepo();
|
|
try {
|
|
const git = new Git({ cwd: work, remoteUrl: bare, token: 'x' });
|
|
git.configure();
|
|
git.fetchBranches(['target', 'source']);
|
|
|
|
assert.ok(git.countAheadCommits('target', 'source') >= 1);
|
|
|
|
const det = git.detectConflict('target', 'source');
|
|
assert.equal(det.hasConflict, true);
|
|
assert.ok(det.files.includes('file.txt'));
|
|
|
|
// 暫存分支應已清除
|
|
assert.ok(!g(work, ['branch']).includes('__conflict_check'));
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('createResolveBranch: 推送含衝突標記的解衝突分支到 remote', () => {
|
|
const { root, bare, work } = setupRepo();
|
|
try {
|
|
const git = new Git({ cwd: work, remoteUrl: bare, token: 'x' });
|
|
git.configure();
|
|
git.fetchBranches(['target', 'source']);
|
|
|
|
const res = git.createResolveBranch({ target: 'target', source: 'source', resolveBranch: 'resolve-x' });
|
|
assert.ok(res.files.includes('file.txt'));
|
|
|
|
// remote 應有 resolve-x 分支
|
|
assert.ok(execFileSync('git', ['--git-dir', bare, 'branch'], { encoding: 'utf8' }).includes('resolve-x'));
|
|
// 已 commit 的檔案應保留衝突標記
|
|
assert.ok(readFileSync(join(work, 'file.txt'), 'utf8').includes('<<<<<<<'));
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('detectConflict: 可順利合併時回報無衝突', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'git-test-'));
|
|
try {
|
|
const bare = join(root, 'remote.git');
|
|
const work = join(root, 'work');
|
|
execFileSync('git', ['init', '-q', '--bare', bare]);
|
|
execFileSync('git', ['clone', '-q', bare, work]);
|
|
g(work, ['config', 'user.email', 'test@example.com']);
|
|
g(work, ['config', 'user.name', 'tester']);
|
|
g(work, ['config', 'commit.gpgsign', 'false']);
|
|
|
|
writeFileSync(join(work, 'a.txt'), 'base\n');
|
|
g(work, ['add', '-A']);
|
|
g(work, ['commit', '-qm', 'base']);
|
|
const def = g(work, ['rev-parse', '--abbrev-ref', 'HEAD']).trim();
|
|
|
|
g(work, ['checkout', '-q', '-b', 'target']); // target 不動
|
|
g(work, ['checkout', '-q', def]);
|
|
g(work, ['checkout', '-q', '-b', 'source']);
|
|
writeFileSync(join(work, 'b.txt'), 'new file\n'); // 改不同檔,無衝突
|
|
g(work, ['add', '-A']);
|
|
g(work, ['commit', '-qm', 'add b']);
|
|
g(work, ['push', '-q', 'origin', 'target', 'source', def]);
|
|
|
|
const git = new Git({ cwd: work, remoteUrl: bare, token: 'x' });
|
|
git.configure();
|
|
git.fetchBranches(['target', 'source']);
|
|
const det = git.detectConflict('target', 'source');
|
|
assert.equal(det.hasConflict, false);
|
|
assert.deepEqual(det.files, []);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|