Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions mobile/src/utils/git/gitCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,33 @@
*
* @param diffBase - Base reference ("main", "HEAD", "--staged")
* @param includeUncommitted - Include uncommitted working directory changes
* @param pathFilter - Optional path filter (e.g., ' -- "src/foo.ts"')
* @param pathFilter - Optional path filter (e.g., 'src/foo.ts')
* @param command - "diff" (unified) or "numstat" (file stats)
*/
export function buildGitDiffCommand(
diffBase: string,
includeUncommitted: boolean,
pathFilter: string,
command: "diff" | "numstat"
): string {
const flags = command === "numstat" ? " -M --numstat" : " -M";
): string[] {
const flags = command === "numstat" ? ["-M", "--numstat"] : ["-M"];

if (diffBase === "--staged") {
// Staged changes, optionally with unstaged appended as separate diff
const base = `git diff --staged${flags}${pathFilter}`;
return includeUncommitted ? `${base} && git diff HEAD${flags}${pathFilter}` : base;
const base: string[] = ["git", "diff", "--staged", ...flags];
const withPath = pathFilter ? [...base, "--", pathFilter] : base;
if (includeUncommitted) {
const unstaged: string[] = ["git", "diff", "HEAD", ...flags];
const unstagedWithPath = pathFilter ? [...unstaged, "--", pathFilter] : unstaged;
return [...withPath, "&&", ...unstagedWithPath];
}
return withPath;
}

if (diffBase === "HEAD") {
// Uncommitted changes only (working vs HEAD)
return `git diff HEAD${flags}${pathFilter}`;
const cmd: string[] = ["git", "diff", "HEAD", ...flags];
return pathFilter ? [...cmd, "--", pathFilter] : cmd;
}

// Branch diff: use three-dot for committed only, or merge-base for committed+uncommitted
Expand All @@ -45,9 +52,12 @@ export function buildGitDiffCommand(
// This includes both committed changes on the branch AND uncommitted working changes
// Single command avoids duplicate hunks from concatenation
// Stable comparison point: merge-base doesn't change when diffBase ref moves forward
return `git diff $(git merge-base ${diffBase} HEAD)${flags}${pathFilter}`;
const mergeBaseCmd: string[] = ["git", "merge-base", diffBase, "HEAD"];
const diffCmd: string[] = ["git", "diff", ...mergeBaseCmd, ...flags];
return pathFilter ? [...diffCmd, "--", pathFilter] : diffCmd;
} else {
// Three-dot: committed changes only (merge-base to HEAD)
return `git diff ${diffBase}...HEAD${flags}${pathFilter}`;
const cmd: string[] = ["git", "diff", `${diffBase}...HEAD`, ...flags];
return pathFilter ? [...cmd, "--", pathFilter] : cmd;
}
}