Fix wrong git status for remote branches (#903)

* Fix wrong git status for remote branches

* Fix edge case where prefix may be stripped multiple times
This commit is contained in:
Alex Netsch
2025-10-02 10:08:19 +01:00
committed by GitHub
parent eaf24bcb2a
commit 5f2ff5e488
2 changed files with 62 additions and 58 deletions

View File

@@ -925,70 +925,72 @@ pub async fn get_task_attempt_branch_status(
.git() .git()
.find_branch_type(&ctx.project.git_repo_path, &task_attempt.target_branch)?; .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 (commits_ahead, commits_behind) = match target_branch_type {
let (a, b) = deployment.git().get_branch_status( BranchType::Local => {
&ctx.project.git_repo_path, let (a, b) = deployment.git().get_branch_status(
&task_attempt.branch, &ctx.project.git_repo_path,
&task_attempt.target_branch, &task_attempt.branch,
)?; &task_attempt.target_branch,
(Some(a), Some(b)) )?;
} else { (Some(a), Some(b))
(None, None) }
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 // 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 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_ahead,
commits_behind, commits_behind,
has_uncommitted_changes, has_uncommitted_changes,
head_oid, head_oid,
uncommitted_count, uncommitted_count,
untracked_count, untracked_count,
remote_commits_ahead: None, remote_commits_ahead: remote_ahead,
remote_commits_behind: None, remote_commits_behind: remote_behind,
merges, merges,
target_branch_name: task_attempt.target_branch.clone(), target_branch_name: task_attempt.target_branch,
is_rebase_in_progress, is_rebase_in_progress,
conflict_op, conflict_op,
conflicted_files, 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))) Ok(ResponseJson(ApiResponse::success(branch_status)))
} }

View File

@@ -1407,9 +1407,7 @@ impl GitService {
// If the target base is remote, update it first so CLI sees latest // If the target base is remote, update it first so CLI sees latest
if nbr.is_remote() { if nbr.is_remote() {
let github_token = github_token.ok_or(GitServiceError::TokenUnavailable)?; let github_token = github_token.ok_or(GitServiceError::TokenUnavailable)?;
let remote = self.get_remote_from_branch_ref(&main_repo, &nbr)?; self.fetch_branch_from_remote(&main_repo, &github_token, &nbr)?;
// First, fetch the latest changes from remote
self.fetch_branch_from_remote(&main_repo, &github_token, &remote, new_base_branch)?;
} }
// Ensure identity for any commits produced by rebase // 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()))?; .ok_or_else(|| GitServiceError::InvalidRepository("Remote has no URL".to_string()))?;
let https_url = self.convert_to_https_url(remote_url); let https_url = self.convert_to_https_url(remote_url);
// Create temporary HTTPS remote
let git_cli = GitCli::new(); let git_cli = GitCli::new();
if let Err(e) = if let Err(e) =
git_cli.fetch_with_token_and_refspec(repo.path(), &https_url, refspec, github_token) git_cli.fetch_with_token_and_refspec(repo.path(), &https_url, refspec, github_token)
@@ -1822,13 +1819,18 @@ impl GitService {
&self, &self,
repo: &Repository, repo: &Repository,
github_token: &str, github_token: &str,
remote: &Remote, branch: &Reference,
branch_name: &str,
) -> Result<(), GitServiceError> { ) -> Result<(), GitServiceError> {
let remote = self.get_remote_from_branch_ref(repo, branch)?;
let default_remote_name = self.default_remote_name(repo); let default_remote_name = self.default_remote_name(repo);
let remote_name = remote.name().unwrap_or(&default_remote_name); let remote_name = remote.name().unwrap_or(&default_remote_name);
let refspec = format!("+refs/heads/{branch_name}:refs/remotes/{remote_name}/{branch_name}"); let dest_ref = branch
self.fetch_from_remote(repo, github_token, remote, &refspec) .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 /// Fetch from remote repository using GitHub token authentication