feat: add fast-forward repo sync #41
3 changed files with 48 additions and 9 deletions
|
|
@ -467,8 +467,6 @@ repo
|
|||
if (!config || !config.url || !config.token) {
|
||||
throw new Error('Not authenticated. Run: stoke auth login');
|
||||
}
|
||||
if (!options.branch) throw new Error('--branch is required');
|
||||
|
||||
const base = config.url.replace(/\/+$/, '');
|
||||
const forgeUrl = `${base}/${encodeURIComponent(options.owner)}/${encodeURIComponent(options.repo)}.git`;
|
||||
const result = syncRepository({
|
||||
|
|
@ -477,7 +475,11 @@ repo
|
|||
branch: options.branch,
|
||||
env: gitAuthEnv({ ...config, url: base }),
|
||||
});
|
||||
console.log(`${result.branch} ${result.oldSha}..${result.newSha}`);
|
||||
if (result.changed) {
|
||||
console.log(`${result.branch} ${result.oldSha}..${result.newSha}`);
|
||||
} else {
|
||||
console.log(`${result.branch} is up to date at ${result.newSha}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Repository sync failed: ${err.message}`);
|
||||
process.exit(1);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ function syncRepository({ forgeUrl, upstreamUrl, branch, env }) {
|
|||
|
||||
try {
|
||||
runGit(['init', '--bare', directory], { cwd: directory, env });
|
||||
if (!branch) {
|
||||
const symbolicHead = runGit(['ls-remote', '--symref', forgeUrl, 'HEAD'], {
|
||||
cwd: directory,
|
||||
env,
|
||||
}).stdout;
|
||||
const match = symbolicHead.match(/^ref:\s+refs\/heads\/(.+)\s+HEAD$/m);
|
||||
if (!match) throw new Error('Could not resolve the forge repository default branch');
|
||||
branch = match[1];
|
||||
}
|
||||
runGit(['fetch', '--no-tags', forgeUrl, `refs/heads/${branch}:${forgeRef}`], {
|
||||
cwd: directory,
|
||||
env,
|
||||
|
|
@ -45,7 +54,7 @@ function syncRepository({ forgeUrl, upstreamUrl, branch, env }) {
|
|||
env,
|
||||
});
|
||||
}
|
||||
return { branch, oldSha, newSha };
|
||||
return { branch, oldSha, newSha, changed: oldSha !== newSha };
|
||||
} finally {
|
||||
fs.rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,17 +55,18 @@ function fixture() {
|
|||
};
|
||||
}
|
||||
|
||||
function runSync(fx, extra = []) {
|
||||
return spawnSync(process.execPath, [
|
||||
function runSync(fx, extra = [], { branch = 'main' } = {}) {
|
||||
const args = [
|
||||
CLI,
|
||||
'repo',
|
||||
'sync',
|
||||
'-o', 'o',
|
||||
'-r', 'r',
|
||||
'--from', `file://${fx.upstreamRepo}`,
|
||||
'--branch', 'main',
|
||||
...extra,
|
||||
], {
|
||||
];
|
||||
if (branch) args.push('--branch', branch);
|
||||
args.push(...extra);
|
||||
return spawnSync(process.execPath, args, {
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env, STOKE_CONFIG_FILE: fx.config },
|
||||
});
|
||||
|
|
@ -83,3 +84,30 @@ test('repo sync fast-forwards an undiverged forge branch', () => {
|
|||
fx.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test('repo sync resolves an omitted branch from the forge symbolic HEAD', () => {
|
||||
const fx = fixture();
|
||||
try {
|
||||
const result = runSync(fx, [], { branch: null });
|
||||
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal(git(['rev-parse', 'refs/heads/main'], fx.forgeRepo), fx.newSha);
|
||||
assert.match(result.stdout, new RegExp(`main ${fx.oldSha}\\.\\.${fx.newSha}`));
|
||||
} finally {
|
||||
fx.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test('repo sync reports an already-current branch as a no-op', () => {
|
||||
const fx = fixture();
|
||||
try {
|
||||
assert.equal(runSync(fx).status, 0);
|
||||
const result = runSync(fx);
|
||||
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal(git(['rev-parse', 'refs/heads/main'], fx.forgeRepo), fx.newSha);
|
||||
assert.match(result.stdout, new RegExp(`main is up to date at ${fx.newSha}`));
|
||||
} finally {
|
||||
fx.cleanup();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue