Skip to content

Commit b3cae9d

Browse files
committed
Add pull command
1 parent d2b521a commit b3cae9d

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

src/commands/pull.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Pull agent configs from remote repository
3+
*/
4+
5+
import * as p from "@clack/prompts";
6+
import { getConfig } from "../config/manager";
7+
import type { GlobalConfig } from "../config/types";
8+
import {
9+
fetch,
10+
getBehindCount,
11+
getCurrentBranch,
12+
getGitStatus,
13+
getRemoteUrl,
14+
hasChanges,
15+
pull,
16+
} from "../utils/git";
17+
import { expandHome } from "../utils/paths";
18+
19+
export async function pullCommand() {
20+
p.intro("Pull from Remote");
21+
22+
let config: GlobalConfig;
23+
try {
24+
config = getConfig();
25+
} catch (_error) {
26+
p.cancel(
27+
"Configuration not found. Run 'syncode new' or 'syncode init' first.",
28+
);
29+
return;
30+
}
31+
32+
const _repoPath = expandHome(config.repoPath);
33+
34+
const remoteUrl = await getRemoteUrl();
35+
if (!remoteUrl) {
36+
p.cancel(
37+
"No remote repository configured. Add a remote with: git remote add origin <url>",
38+
);
39+
return;
40+
}
41+
42+
const branch = await getCurrentBranch();
43+
p.log.info(`Branch: ${branch}`);
44+
p.log.info(`Remote: ${remoteUrl}`);
45+
46+
console.log("");
47+
48+
// Check for uncommitted changes
49+
if (await hasChanges()) {
50+
p.log.warning("Uncommitted changes detected");
51+
52+
const gitStatus = await getGitStatus();
53+
if (gitStatus) {
54+
console.log(
55+
gitStatus
56+
.split("\n")
57+
.map((l) => ` ${l}`)
58+
.join("\n"),
59+
);
60+
console.log("");
61+
}
62+
63+
p.log.warning(
64+
"Cannot pull with uncommitted changes. Commit or stash them first.",
65+
);
66+
p.outro("Pull cancelled");
67+
return;
68+
}
69+
70+
// Fetch to get latest remote state
71+
const fetchSpinner = p.spinner();
72+
fetchSpinner.start("Fetching from remote");
73+
74+
const fetchResult = await fetch();
75+
if (!fetchResult.success) {
76+
fetchSpinner.stop("Fetch failed");
77+
p.log.error(fetchResult.message);
78+
p.outro("Pull cancelled");
79+
return;
80+
}
81+
82+
fetchSpinner.stop("Fetched from remote");
83+
84+
// Check if we're behind
85+
const behindCount = await getBehindCount();
86+
if (behindCount === 0) {
87+
p.log.success("Already up to date");
88+
p.outro("No changes to pull");
89+
return;
90+
}
91+
92+
p.log.info(`${behindCount} commit(s) behind remote`);
93+
94+
// Pull from remote
95+
const spinner = p.spinner();
96+
spinner.start(`Pulling from ${branch}`);
97+
98+
const result = await pull();
99+
100+
if (result.success) {
101+
spinner.stop(`Pulled ${behindCount} commit(s) from ${branch}`);
102+
p.outro("Successfully pulled from remote");
103+
} else {
104+
spinner.stop("Pull failed");
105+
const { logError, getErrorLogFile } = require("../utils/trace");
106+
const logFile = logError(new Error(result.message), "pull");
107+
p.log.error(result.message);
108+
p.cancel(`Failed to pull from remote\n${getErrorLogFile(logFile)}`);
109+
}
110+
}

src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as p from "@clack/prompts";
77
import { initCommand } from "./commands/init";
88
import { machineCommand } from "./commands/machine";
99
import { newCommand } from "./commands/new";
10+
import { pullCommand } from "./commands/pull";
1011
import { pushCommand } from "./commands/push";
1112
import { statusCommand } from "./commands/status";
1213
import { syncCommand } from "./commands/sync";
@@ -29,6 +30,7 @@ const commands = {
2930
unsync: unsyncCommand,
3031
status: statusCommand,
3132
push: pushCommand,
33+
pull: pullCommand,
3234
};
3335

3436
async function main() {
@@ -114,6 +116,11 @@ async function main() {
114116
label: "Push to remote",
115117
hint: "Push config changes to git remote",
116118
},
119+
{
120+
value: "pull",
121+
label: "Pull from remote",
122+
hint: "Pull config changes from git remote",
123+
},
117124
],
118125
});
119126

@@ -146,6 +153,7 @@ Sync Commands:
146153
unsync Remove symlinks and keep local configs
147154
status Show status of synced agents
148155
push Push config changes to git remote
156+
pull Pull config changes from git remote
149157
150158
Machine Commands:
151159
machine Machine setup tools (deps, status)
@@ -162,6 +170,7 @@ Examples:
162170
syncode sync # Sync agent configs
163171
syncode unsync # Remove symlinks
164172
syncode push # Push changes to remote
173+
syncode pull # Pull changes from remote
165174
syncode machine # Machine setup tools
166175
167176
Quick Start:

src/utils/git.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,40 @@ export async function push(): Promise<{ success: boolean; message: string }> {
7272
return { success: false, message: result.stderr || "Push failed" };
7373
}
7474

75+
export async function pull(): Promise<{ success: boolean; message: string }> {
76+
const repoRoot = getRepoRoot();
77+
const result = await exec(`git -C "${repoRoot}" pull --rebase`);
78+
if (result.success) {
79+
return { success: true, message: "Pulled from remote" };
80+
}
81+
return { success: false, message: result.stderr || "Pull failed" };
82+
}
83+
84+
export async function fetch(): Promise<{ success: boolean; message: string }> {
85+
const repoRoot = getRepoRoot();
86+
const result = await exec(`git -C "${repoRoot}" fetch`);
87+
if (result.success) {
88+
return { success: true, message: "Fetched from remote" };
89+
}
90+
return { success: false, message: result.stderr || "Fetch failed" };
91+
}
92+
93+
export async function getAheadCount(): Promise<number> {
94+
const repoRoot = getRepoRoot();
95+
const result = await exec(
96+
`git -C "${repoRoot}" rev-list --count @{upstream}..HEAD 2>/dev/null || echo "0"`,
97+
);
98+
return Number.parseInt(result.stdout, 10) || 0;
99+
}
100+
101+
export async function getBehindCount(): Promise<number> {
102+
const repoRoot = getRepoRoot();
103+
const result = await exec(
104+
`git -C "${repoRoot}" rev-list --count HEAD..@{upstream} 2>/dev/null || echo "0"`,
105+
);
106+
return Number.parseInt(result.stdout, 10) || 0;
107+
}
108+
75109
export async function getCurrentBranch(): Promise<string> {
76110
const repoRoot = getRepoRoot();
77111
const result = await exec(`git -C "${repoRoot}" branch --show-current`);

0 commit comments

Comments
 (0)