mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
fix(sync): skip git pull when repo has no origin remote
`gbrain sync` ran `git pull` unconditionally and printed scary stderr on every cycle for brains that have no `origin` remote (local-only workflows, single-machine setups, brains initialized via `gbrain init --pglite` against an arbitrary directory). The pull failed harmlessly but the noise was confusing and made operators think sync was broken. `hasOriginRemote()` probes `git remote get-url origin` with stdio ignored; on failure (`no such remote`), skip the pull, print a single informational line, and proceed with the local working tree. Cherry-picked from PR #1119. Co-Authored-By: hnshah <hnshah@users.noreply.github.com>
This commit is contained in:
+19
-1
@@ -230,6 +230,19 @@ function git(repoPath: string, args: string[], configs: string[] = []): string {
|
||||
}).trim();
|
||||
}
|
||||
|
||||
function hasOriginRemote(repoPath: string): boolean {
|
||||
try {
|
||||
execFileSync('git', buildGitInvocation(repoPath, ['remote', 'get-url', 'origin']), {
|
||||
encoding: 'utf-8',
|
||||
timeout: 30000,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isDetachedHead(repoPath: string): boolean {
|
||||
try {
|
||||
git(repoPath, ['symbolic-ref', '--quiet', 'HEAD']);
|
||||
@@ -450,7 +463,12 @@ async function performSyncInner(engine: BrainEngine, opts: SyncOpts): Promise<Sy
|
||||
// hardening that cloneRepo applies. Route through pullRepo from
|
||||
// git-remote.ts so the flag set is consistent across initial clone and
|
||||
// ongoing pulls — single source of truth for the defensive flags.
|
||||
if (!opts.noPull && !detachedHead) {
|
||||
const originRemotePresent = !opts.noPull && !detachedHead ? hasOriginRemote(repoPath) : false;
|
||||
if (!opts.noPull && !detachedHead && !originRemotePresent) {
|
||||
console.error(`No origin remote on ${repoPath}; skipping git pull. Syncing from local working tree.`);
|
||||
}
|
||||
|
||||
if (!opts.noPull && !detachedHead && originRemotePresent) {
|
||||
const _t0 = Date.now();
|
||||
console.error(`[gbrain phase] sync.git_pull start`);
|
||||
try {
|
||||
|
||||
@@ -347,6 +347,26 @@ describe('performSync dry-run never writes', () => {
|
||||
expect(await engine.getConfig('sync.repo_path')).toBeNull();
|
||||
});
|
||||
|
||||
test('first sync without origin skips git pull noise and uses local working tree', async () => {
|
||||
const { performSync } = await import('../src/commands/sync.ts');
|
||||
const messages: string[] = [];
|
||||
const originalError = console.error;
|
||||
console.error = (...args: unknown[]) => { messages.push(args.map(String).join(' ')); };
|
||||
try {
|
||||
const result = await performSync(engine, {
|
||||
repoPath,
|
||||
noEmbed: true,
|
||||
});
|
||||
expect(result.status).toBe('first_sync');
|
||||
} finally {
|
||||
console.error = originalError;
|
||||
}
|
||||
|
||||
expect(messages.some(m => m.includes('No origin remote') && m.includes('skipping git pull'))).toBe(true);
|
||||
expect(messages.some(m => m.includes('sync.git_pull start'))).toBe(false);
|
||||
expect(messages.some(m => m.includes('git pull failed'))).toBe(false);
|
||||
});
|
||||
|
||||
test('incremental dry-run does NOT write to DB or advance the bookmark', async () => {
|
||||
const { performSync } = await import('../src/commands/sync.ts');
|
||||
// First do a real sync to seed the bookmark.
|
||||
|
||||
Reference in New Issue
Block a user