diff --git a/crates/server/src/routes/task_attempts.rs b/crates/server/src/routes/task_attempts.rs index 53d70e29..61939f54 100644 --- a/crates/server/src/routes/task_attempts.rs +++ b/crates/server/src/routes/task_attempts.rs @@ -925,70 +925,72 @@ pub async fn get_task_attempt_branch_status( .git() .find_branch_type(&ctx.project.git_repo_path, &task_attempt.target_branch)?; - let (commits_ahead, commits_behind) = if matches!(target_branch_type, BranchType::Local) { - let (a, b) = deployment.git().get_branch_status( - &ctx.project.git_repo_path, - &task_attempt.branch, - &task_attempt.target_branch, - )?; - (Some(a), Some(b)) - } else { - (None, None) + let (commits_ahead, commits_behind) = match target_branch_type { + BranchType::Local => { + let (a, b) = deployment.git().get_branch_status( + &ctx.project.git_repo_path, + &task_attempt.branch, + &task_attempt.target_branch, + )?; + (Some(a), Some(b)) + } + BranchType::Remote => { + let github_config = deployment.config().read().await.github.clone(); + let token = github_config + .token() + .ok_or(ApiError::GitHubService(GitHubServiceError::TokenInvalid))?; + let (remote_commits_ahead, remote_commits_behind) = + deployment.git().get_remote_branch_status( + &ctx.project.git_repo_path, + &task_attempt.branch, + Some(&task_attempt.target_branch), + token, + )?; + (Some(remote_commits_ahead), Some(remote_commits_behind)) + } }; // Fetch merges for this task attempt and add to branch status let merges = Merge::find_by_task_attempt_id(pool, task_attempt.id).await?; - let mut branch_status = BranchStatus { + let (remote_ahead, remote_behind) = if let Some(Merge::Pr(PrMerge { + pr_info: PullRequestInfo { + status: MergeStatus::Open, + .. + }, + .. + })) = merges.first() + { + // check remote status if the attempt has an open PR + let github_config = deployment.config().read().await.github.clone(); + let token = github_config + .token() + .ok_or(ApiError::GitHubService(GitHubServiceError::TokenInvalid))?; + let (remote_commits_ahead, remote_commits_behind) = + deployment.git().get_remote_branch_status( + &ctx.project.git_repo_path, + &task_attempt.branch, + None, + token, + )?; + (Some(remote_commits_ahead), Some(remote_commits_behind)) + } else { + (None, None) + }; + + let branch_status = BranchStatus { commits_ahead, commits_behind, has_uncommitted_changes, head_oid, uncommitted_count, untracked_count, - remote_commits_ahead: None, - remote_commits_behind: None, + remote_commits_ahead: remote_ahead, + remote_commits_behind: remote_behind, merges, - target_branch_name: task_attempt.target_branch.clone(), + target_branch_name: task_attempt.target_branch, is_rebase_in_progress, conflict_op, conflicted_files, }; - let has_open_pr = branch_status.merges.first().is_some_and(|m| { - matches!( - m, - Merge::Pr(PrMerge { - pr_info: PullRequestInfo { - status: MergeStatus::Open, - .. - }, - .. - }) - ) - }); - - // check remote status if the attempt has an open PR or the target_branch is a remote branch - if has_open_pr || target_branch_type == BranchType::Remote { - let github_config = deployment.config().read().await.github.clone(); - let token = github_config - .token() - .ok_or(ApiError::GitHubService(GitHubServiceError::TokenInvalid))?; - - // For an attempt with a remote target branch, we compare against that - // After opening a PR, the attempt has a remote branch itself, so we use that - let remote_target_branch = if target_branch_type == BranchType::Remote && !has_open_pr { - Some(task_attempt.target_branch) - } else { - None - }; - let (remote_commits_ahead, remote_commits_behind) = - deployment.git().get_remote_branch_status( - &ctx.project.git_repo_path, - &task_attempt.branch, - remote_target_branch.as_deref(), - token, - )?; - branch_status.remote_commits_ahead = Some(remote_commits_ahead); - branch_status.remote_commits_behind = Some(remote_commits_behind); - } Ok(ResponseJson(ApiResponse::success(branch_status))) } diff --git a/crates/services/src/services/git.rs b/crates/services/src/services/git.rs index 9d453058..ec6ac53f 100644 --- a/crates/services/src/services/git.rs +++ b/crates/services/src/services/git.rs @@ -1407,9 +1407,7 @@ impl GitService { // If the target base is remote, update it first so CLI sees latest if nbr.is_remote() { let github_token = github_token.ok_or(GitServiceError::TokenUnavailable)?; - let remote = self.get_remote_from_branch_ref(&main_repo, &nbr)?; - // First, fetch the latest changes from remote - self.fetch_branch_from_remote(&main_repo, &github_token, &remote, new_base_branch)?; + self.fetch_branch_from_remote(&main_repo, &github_token, &nbr)?; } // Ensure identity for any commits produced by rebase @@ -1806,7 +1804,6 @@ impl GitService { .ok_or_else(|| GitServiceError::InvalidRepository("Remote has no URL".to_string()))?; let https_url = self.convert_to_https_url(remote_url); - // Create temporary HTTPS remote let git_cli = GitCli::new(); if let Err(e) = git_cli.fetch_with_token_and_refspec(repo.path(), &https_url, refspec, github_token) @@ -1822,13 +1819,18 @@ impl GitService { &self, repo: &Repository, github_token: &str, - remote: &Remote, - branch_name: &str, + branch: &Reference, ) -> Result<(), GitServiceError> { + let remote = self.get_remote_from_branch_ref(repo, branch)?; let default_remote_name = self.default_remote_name(repo); let remote_name = remote.name().unwrap_or(&default_remote_name); - let refspec = format!("+refs/heads/{branch_name}:refs/remotes/{remote_name}/{branch_name}"); - self.fetch_from_remote(repo, github_token, remote, &refspec) + let dest_ref = branch + .name() + .ok_or_else(|| GitServiceError::InvalidRepository("Invalid branch ref".into()))?; + let remote_prefix = format!("refs/remotes/{remote_name}/"); + let src_ref = dest_ref.replacen(&remote_prefix, "refs/heads/", 1); + let refspec = format!("+{src_ref}:{dest_ref}"); + self.fetch_from_remote(repo, github_token, &remote, &refspec) } /// Fetch from remote repository using GitHub token authentication