Skip to content
68 changes: 68 additions & 0 deletions apps/desktop/src/lib/trpc/routers/changes/security/git-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,71 @@ export async function gitUnstageAll(worktreePath: string): Promise<void> {
const git = simpleGit(worktreePath);
await git.reset(["HEAD"]);
}

/**
* Discard all unstaged changes (modified and deleted files).
*
* Uses `git checkout -- .` to restore all tracked files to HEAD state.
* Does NOT affect untracked files.
*/
export async function gitDiscardAllUnstaged(
worktreePath: string,
): Promise<void> {
assertRegisteredWorktree(worktreePath);

const git = simpleGit(worktreePath);
await git.checkout(["--", "."]);
}

/**
* Discard all staged changes by unstaging then discarding.
*
* Uses `git reset HEAD` followed by `git checkout -- .`.
* Does NOT affect untracked files.
*/
export async function gitDiscardAllStaged(worktreePath: string): Promise<void> {
assertRegisteredWorktree(worktreePath);

const git = simpleGit(worktreePath);
await git.reset(["HEAD"]);
await git.checkout(["--", "."]);
}

/**
* Stash all tracked changes.
*
* Uses `git stash push` to save current work-in-progress.
*/
export async function gitStash(worktreePath: string): Promise<void> {
assertRegisteredWorktree(worktreePath);

const git = simpleGit(worktreePath);
await git.stash(["push"]);
}

/**
* Stash all changes including untracked files.
*
* Uses `git stash push --include-untracked`.
*/
export async function gitStashIncludeUntracked(
worktreePath: string,
): Promise<void> {
assertRegisteredWorktree(worktreePath);

const git = simpleGit(worktreePath);
await git.stash(["push", "--include-untracked"]);
}

/**
* Pop the most recent stash.
*
* Uses `git stash pop` to apply and remove the top stash entry.
* Throws if no stash exists or if there are conflicts.
*/
export async function gitStashPop(worktreePath: string): Promise<void> {
assertRegisteredWorktree(worktreePath);

const git = simpleGit(worktreePath);
await git.stash(["pop"]);
}
5 changes: 5 additions & 0 deletions apps/desktop/src/lib/trpc/routers/changes/security/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@

export {
gitCheckoutFile,
gitDiscardAllStaged,
gitDiscardAllUnstaged,
gitStageAll,
gitStageFile,
gitStash,
gitStashIncludeUntracked,
gitStashPop,
gitSwitchBranch,
gitUnstageAll,
gitUnstageFile,
Expand Down
40 changes: 40 additions & 0 deletions apps/desktop/src/lib/trpc/routers/changes/staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { z } from "zod";
import { publicProcedure, router } from "../..";
import {
gitCheckoutFile,
gitDiscardAllStaged,
gitDiscardAllUnstaged,
gitStageAll,
gitStageFile,
gitStash,
gitStashIncludeUntracked,
gitStashPop,
gitUnstageAll,
gitUnstageFile,
secureFs,
Expand Down Expand Up @@ -72,5 +77,40 @@ export const createStagingRouter = () => {
await secureFs.delete(input.worktreePath, input.filePath);
return { success: true };
}),

discardAllUnstaged: publicProcedure
.input(z.object({ worktreePath: z.string() }))
.mutation(async ({ input }): Promise<{ success: boolean }> => {
await gitDiscardAllUnstaged(input.worktreePath);
return { success: true };
}),

discardAllStaged: publicProcedure
.input(z.object({ worktreePath: z.string() }))
.mutation(async ({ input }): Promise<{ success: boolean }> => {
await gitDiscardAllStaged(input.worktreePath);
return { success: true };
}),

stash: publicProcedure
.input(z.object({ worktreePath: z.string() }))
.mutation(async ({ input }): Promise<{ success: boolean }> => {
await gitStash(input.worktreePath);
return { success: true };
}),

stashIncludeUntracked: publicProcedure
.input(z.object({ worktreePath: z.string() }))
.mutation(async ({ input }): Promise<{ success: boolean }> => {
await gitStashIncludeUntracked(input.worktreePath);
return { success: true };
}),

stashPop: publicProcedure
.input(z.object({ worktreePath: z.string() }))
.mutation(async ({ input }): Promise<{ success: boolean }> => {
await gitStashPop(input.worktreePath);
return { success: true };
}),
});
};
Loading
Loading