Refactor version handlers (#90)

refactor version handlers
This commit is contained in:
Will Chen
2025-05-06 10:16:09 -07:00
committed by GitHub
parent a4d3e04996
commit 1fcb58a141
3 changed files with 224 additions and 205 deletions

View File

@@ -454,210 +454,6 @@ export function registerAppHandlers() {
}
);
ipcMain.handle("list-versions", async (_, { appId }: { appId: number }) => {
const app = await db.query.apps.findFirst({
where: eq(apps.id, appId),
});
if (!app) {
throw new Error("App not found");
}
const appPath = getDyadAppPath(app.path);
// Just return an empty array if the app is not a git repo.
if (!fs.existsSync(path.join(appPath, ".git"))) {
return [];
}
try {
const commits = await git.log({
fs,
dir: appPath,
// KEEP UP TO DATE WITH ChatHeader.tsx
depth: 10_000, // Limit to last 10_000 commits for performance
});
return commits.map((commit) => ({
oid: commit.oid,
message: commit.commit.message,
timestamp: commit.commit.author.timestamp,
})) satisfies Version[];
} catch (error: any) {
logger.error(`Error listing versions for app ${appId}:`, error);
throw new Error(`Failed to list versions: ${error.message}`);
}
});
ipcMain.handle(
"revert-version",
async (
_,
{ appId, previousVersionId }: { appId: number; previousVersionId: string }
) => {
return withLock(appId, async () => {
const app = await db.query.apps.findFirst({
where: eq(apps.id, appId),
});
if (!app) {
throw new Error("App not found");
}
const appPath = getDyadAppPath(app.path);
try {
await git.checkout({
fs,
dir: appPath,
ref: "main",
force: true,
});
// Get status matrix comparing the target commit (previousVersionId as HEAD) with current working directory
const matrix = await git.statusMatrix({
fs,
dir: appPath,
ref: previousVersionId,
});
// Process each file to revert to the state in previousVersionId
for (const [
filepath,
headStatus,
workdirStatus,
stageStatus,
] of matrix) {
const fullPath = path.join(appPath, filepath);
// If file exists in HEAD (previous version)
if (headStatus === 1) {
// If file doesn't exist or has changed in working directory, restore it from the target commit
if (workdirStatus !== 1) {
const { blob } = await git.readBlob({
fs,
dir: appPath,
oid: previousVersionId,
filepath,
});
await fsPromises.mkdir(path.dirname(fullPath), {
recursive: true,
});
await fsPromises.writeFile(fullPath, Buffer.from(blob));
}
}
// If file doesn't exist in HEAD but exists in working directory, delete it
else if (headStatus === 0 && workdirStatus !== 0) {
if (fs.existsSync(fullPath)) {
await fsPromises.unlink(fullPath);
await git.remove({
fs,
dir: appPath,
filepath: filepath,
});
}
}
}
// Stage all changes
await git.add({
fs,
dir: appPath,
filepath: ".",
});
// Create a revert commit
await git.commit({
fs,
dir: appPath,
message: `Reverted all changes back to version ${previousVersionId}`,
author: await getGitAuthor(),
});
// Find the chat and message associated with the commit hash
const messageWithCommit = await db.query.messages.findFirst({
where: eq(messages.commitHash, previousVersionId),
with: {
chat: true,
},
});
// If we found a message with this commit hash, delete all subsequent messages (but keep this message)
if (messageWithCommit) {
const chatId = messageWithCommit.chatId;
// Find all messages in this chat with IDs > the one with our commit hash
const messagesToDelete = await db.query.messages.findMany({
where: and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id)
),
orderBy: desc(messages.id),
});
logger.log(
`Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`
);
// Delete the messages
if (messagesToDelete.length > 0) {
await db
.delete(messages)
.where(
and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id)
)
);
}
}
return { success: true };
} catch (error: any) {
logger.error(
`Error reverting to version ${previousVersionId} for app ${appId}:`,
error
);
throw new Error(`Failed to revert version: ${error.message}`);
}
});
}
);
ipcMain.handle(
"checkout-version",
async (_, { appId, versionId }: { appId: number; versionId: string }) => {
return withLock(appId, async () => {
const app = await db.query.apps.findFirst({
where: eq(apps.id, appId),
});
if (!app) {
throw new Error("App not found");
}
const appPath = getDyadAppPath(app.path);
try {
// Checkout the target commit
await git.checkout({
fs,
dir: appPath,
ref: versionId,
force: true,
});
return { success: true };
} catch (error: any) {
logger.error(
`Error checking out version ${versionId} for app ${appId}:`,
error
);
throw new Error(`Failed to checkout version: ${error.message}`);
}
});
}
);
ipcMain.handle(
"edit-app-file",
async (