From 655a20a19ff401ca55fe3560a86d34241d954fc5 Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Tue, 24 Jun 2025 17:39:51 +0100 Subject: [PATCH] Task attempt 66878c93-02e8-433f-bbe4-b4ed21264f9a - Final changes --- backend/src/models/task_attempt.rs | 14 ++++ frontend/src/pages/task-attempt-compare.tsx | 90 +++++++++++++++++---- shared/types.ts | 2 +- 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/backend/src/models/task_attempt.rs b/backend/src/models/task_attempt.rs index 62690558..96527dd6 100644 --- a/backend/src/models/task_attempt.rs +++ b/backend/src/models/task_attempt.rs @@ -126,6 +126,7 @@ pub struct BranchStatus { pub commits_ahead: usize, pub up_to_date: bool, pub merged: bool, + pub has_uncommitted_changes: bool, } impl TaskAttempt { @@ -1229,6 +1230,17 @@ impl TaskAttempt { let worktree_head = worktree_repo.head()?.peel_to_commit()?; let worktree_oid = worktree_head.id(); + // Check for uncommitted changes in the worktree + let has_uncommitted_changes = { + let statuses = worktree_repo.statuses(None)?; + statuses.iter().any(|entry| { + let status = entry.status(); + // Check for any unstaged or staged changes + status.is_wt_modified() || status.is_wt_new() || status.is_wt_deleted() || + status.is_index_modified() || status.is_index_new() || status.is_index_deleted() + }) + }; + if main_oid == worktree_oid { // Branches are at the same commit return Ok(BranchStatus { @@ -1237,6 +1249,7 @@ impl TaskAttempt { commits_ahead: 0, up_to_date: true, merged: attempt.merge_commit.is_some(), + has_uncommitted_changes, }); } @@ -1260,6 +1273,7 @@ impl TaskAttempt { commits_ahead, up_to_date: commits_behind == 0 && commits_ahead == 0, merged: attempt.merge_commit.is_some(), + has_uncommitted_changes, }) } diff --git a/frontend/src/pages/task-attempt-compare.tsx b/frontend/src/pages/task-attempt-compare.tsx index fd5cf4e9..d17d004a 100644 --- a/frontend/src/pages/task-attempt-compare.tsx +++ b/frontend/src/pages/task-attempt-compare.tsx @@ -58,6 +58,7 @@ export function TaskAttemptComparePage() { const [showAllUnchanged, setShowAllUnchanged] = useState(false); const [deletingFiles, setDeletingFiles] = useState>(new Set()); const [fileToDelete, setFileToDelete] = useState(null); + const [showUncommittedWarning, setShowUncommittedWarning] = useState(false); useEffect(() => { if (projectId && taskId && attemptId) { @@ -125,6 +126,18 @@ export function TaskAttemptComparePage() { const handleMergeClick = async () => { if (!projectId || !taskId || !attemptId) return; + // Check for uncommitted changes and show warning dialog + if (branchStatus?.has_uncommitted_changes) { + setShowUncommittedWarning(true); + return; + } + + await performMerge(); + }; + + const performMerge = async () => { + if (!projectId || !taskId || !attemptId) return; + try { setMerging(true); const response = await makeRequest( @@ -154,6 +167,15 @@ export function TaskAttemptComparePage() { } }; + const handleConfirmMergeWithUncommitted = async () => { + setShowUncommittedWarning(false); + await performMerge(); + }; + + const handleCancelMergeWithUncommitted = () => { + setShowUncommittedWarning(false); + }; + const handleRebaseClick = async () => { if (!projectId || !taskId || !attemptId) return; @@ -465,20 +487,28 @@ export function TaskAttemptComparePage() {
{/* Branch Status */} {!branchStatusLoading && branchStatus && ( -
- - {branchStatus.up_to_date ? ( - Up to date - ) : branchStatus.is_behind === true ? ( - - {branchStatus.commits_behind} commit - {branchStatus.commits_behind !== 1 ? "s" : ""} behind main - - ) : ( - - {branchStatus.commits_ahead} commit - {branchStatus.commits_ahead !== 1 ? "s" : ""} ahead of main - +
+
+ + {branchStatus.up_to_date ? ( + Up to date + ) : branchStatus.is_behind === true ? ( + + {branchStatus.commits_behind} commit + {branchStatus.commits_behind !== 1 ? "s" : ""} behind main + + ) : ( + + {branchStatus.commits_ahead} commit + {branchStatus.commits_ahead !== 1 ? "s" : ""} ahead of main + + )} +
+ {branchStatus.has_uncommitted_changes && ( +
+ + Uncommitted changes +
)}
)} @@ -718,6 +748,38 @@ export function TaskAttemptComparePage() { + + {/* Uncommitted Changes Warning Dialog */} + handleCancelMergeWithUncommitted()}> + + + Uncommitted Changes Detected + + There are uncommitted changes in the worktree that will be included in the merge. + + +
+
+

+ Warning: The worktree contains uncommitted changes (modified, added, or deleted files) + that have not been committed to git. These changes will be permanently merged into the main branch. +

+
+
+ + + + +
+
); } diff --git a/shared/types.ts b/shared/types.ts index 086e9423..26ca899a 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -70,7 +70,7 @@ export type FileDiff = { path: string, chunks: Array, }; export type WorktreeDiff = { files: Array, }; -export type BranchStatus = { is_behind: boolean, commits_behind: number, commits_ahead: number, up_to_date: boolean, merged: boolean, }; +export type BranchStatus = { is_behind: boolean, commits_behind: number, commits_ahead: number, up_to_date: boolean, merged: boolean, has_uncommitted_changes: boolean, }; export type ExecutionProcess = { id: string, task_attempt_id: string, process_type: ExecutionProcessType, executor_type: string | null, status: ExecutionProcessStatus, command: string, args: string | null, working_directory: string, stdout: string | null, stderr: string | null, exit_code: bigint | null, started_at: string, completed_at: string | null, created_at: string, updated_at: string, };