fix(git): make unsetting upstream idempotent

Saving sync settings with no upstream could cause a fatal Git
error when unsetting upstream on a branch that never had tracking
information. This change guards the operation by checking for an existing
merge configuration before unsetting, making it idempotent. A test was
added to verify that clearing an unconfigured upstream is a no-op.

- Add test ensuring clearing an unconfigured upstream is a no-op
This commit is contained in:
2026-08-26 00:35:30 +02:00
parent f160e48777
commit 22da397e39
2 changed files with 192 additions and 1 deletions
+26 -1
View File
@@ -836,7 +836,13 @@ pub fn set_branch_upstream(
)?;
}
None => {
run_git(&repo, ["branch", "--unset-upstream", branch.as_str()])?;
// Saving sync settings with "No upstream" must be idempotent. Git
// exits with a fatal error when --unset-upstream is used on a
// branch that never had tracking information, which is the normal
// state immediately after adding the first remote.
if git_config_value(&repo, &format!("branch.{branch}.merge")).is_some() {
run_git(&repo, ["branch", "--unset-upstream", branch.as_str()])?;
}
}
}
status_for_repo(&repo)
@@ -7661,6 +7667,25 @@ mod tests {
assert_eq!(remote.upstream, None);
}
#[test]
fn clearing_an_unconfigured_upstream_is_a_noop() {
let repo = init_temp_repo("unset_missing_upstream");
commit_initial_file(&repo.path);
let branch = git_output_test(&repo.path, ["branch", "--show-current"]);
run_git_test(&repo.path, ["remote", "add", "origin", "."]);
let status = set_branch_upstream(
repo.path.to_string_lossy().to_string(),
branch.clone(),
None,
)
.expect("saving an empty upstream should not fail");
assert_eq!(status.current_branch.as_deref(), Some(branch.as_str()));
assert_eq!(status.upstream, None);
assert!(git_config_value(&repo.path, &format!("branch.{branch}.merge")).is_none());
}
#[test]
fn commit_notes_can_be_created_updated_and_deleted_without_changing_commit() {
let repo = init_temp_repo("commit_notes_crud");