* Generate diff patch for worktree against a specific commit (supports subdirectories) * Diffs against the working tree (includes uncommitted changes) * Excludes binary files and truncates large patches
(params: WorkTreeDiffPatchParams)
| 745 | * Excludes binary files and truncates large patches |
| 746 | */ |
| 747 | async function handleWorkTreeDiffPatch(params: WorkTreeDiffPatchParams): Promise<WorkTreeDiffPatchResponse> { |
| 748 | const startTime = Date.now() |
| 749 | logger.info("[Git:workTreeDiffPatch] Generating diff patch", JSON.stringify({ |
| 750 | repoDir: params.repoDir, |
| 751 | workTreeId: params.workTreeId, |
| 752 | compareToCommit: params.compareToCommit, |
| 753 | })) |
| 754 | |
| 755 | try { |
| 756 | validateRepoDir(params.repoDir) |
| 757 | validateWorkTreeId(params.workTreeId) |
| 758 | |
| 759 | // Resolve git info (handles subdirectories) |
| 760 | const { repoRoot } = await resolveGitInfo(params.repoDir) |
| 761 | |
| 762 | const worktreePath = getWorktreePath(params.workTreeId) |
| 763 | logger.info("[Git:workTreeDiffPatch] Worktree path resolved", JSON.stringify({ worktreePath, exists: fs.existsSync(worktreePath) })) |
| 764 | |
| 765 | // Validate worktree exists |
| 766 | if (!fs.existsSync(worktreePath)) { |
| 767 | throw new Error(`Worktree does not exist: ${worktreePath}`) |
| 768 | } |
| 769 | |
| 770 | // Validate worktree is in git's list using repo root |
| 771 | const listResult = await execGit(["worktree", "list", "--porcelain"], repoRoot) |
| 772 | if (listResult.success) { |
| 773 | const worktrees = parseWorktreeList(listResult.stdout) |
| 774 | const exists = worktrees.some((wt) => wt.path === worktreePath) |
| 775 | if (!exists) { |
| 776 | throw new Error(`Worktree not registered in git: ${worktreePath}`) |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | // Verify the commit exists and is accessible |
| 781 | const verifyResult = await execGit(["cat-file", "-t", params.compareToCommit], worktreePath) |
| 782 | logger.info("[Git:workTreeDiffPatch] Verified commit", JSON.stringify({ |
| 783 | commit: params.compareToCommit, |
| 784 | exists: verifyResult.success, |
| 785 | type: verifyResult.stdout.trim(), |
| 786 | stderr: verifyResult.stderr, |
| 787 | })) |
| 788 | |
| 789 | if (!verifyResult.success) { |
| 790 | throw new Error(`Commit not accessible in worktree: ${params.compareToCommit}`) |
| 791 | } |
| 792 | |
| 793 | // Generate diff patch against working tree (includes uncommitted changes) |
| 794 | // Using just "git diff <commit>" diffs the commit against the working tree |
| 795 | // By default, git diff shows "Binary files differ" for binary files without content |
| 796 | const diffArgs = ["diff", params.compareToCommit] |
| 797 | logger.info("[Git:workTreeDiffPatch] Running git diff", JSON.stringify({ args: diffArgs, cwd: worktreePath })) |
| 798 | const diffResult = await execGit(diffArgs, worktreePath) |
| 799 | |
| 800 | if (!diffResult.success) { |
| 801 | logger.error("[Git:workTreeDiffPatch] Git diff failed", JSON.stringify({ stderr: diffResult.stderr, args: diffArgs })) |
| 802 | throw new Error(`Failed to generate diff: ${diffResult.stderr}`) |
| 803 | } |
| 804 |
no test coverage detected