fix(vcs): push to an upstream with another name

A plain git push refuses under push.default=simple when the tracked branch is named differently from the local one. Push now names the tracked ref explicitly in that case, which covers the Push dialog and Commit and Push.
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-10-31 08:56:02 +08:00
1 джерело df6ed5ccba
коміт 79e4233c56
2 files changed
+45

No files matched your search

+19
Переглянути файл
@@ -723,6 +723,8 @@ pub(crate) fn push(
})
.ok_or_else(|| IdeError::invalid("no remote configured"))?;
args.extend(["--set-upstream".into(), remote, short]);
} else if let Some((remote, target)) = renamed_upstream(r)? {
args.extend([remote, format!("HEAD:{target}")]);
}
let argv: Vec<&str> = args.iter().map(String::as_str).collect();
cli::run_streaming(r.root(), &argv, cancel, on_output)?;
@@ -814,6 +816,23 @@ pub(crate) fn update_branch(
}
/// The remote the current branch pushes to (`origin`, or the only remote, by default).
/// The remote and ref HEAD's branch tracks when the ref has another name: a plain
/// `git push` refuses that under the default `push.default=simple`.
fn renamed_upstream(r: &Repo) -> IdeResult<Option<(String, String)>> {
let repo = r.gix()?;
let Some(head) = repo.head_name().map_err(gx)? else { return Ok(None) };
let direction = gix::remote::Direction::Fetch;
let Some(Ok(merge)) = repo.branch_remote_ref_name(head.as_ref(), direction) else { return Ok(None) };
if merge.as_ref() == head.as_ref() {
return Ok(None);
}
let target = merge.as_bstr().to_str_lossy().into_owned();
let short = head.as_ref().shorten().to_str_lossy().into_owned();
Ok(repo
.branch_remote_name(short.as_str(), direction)
.map(|remote| (remote.as_bstr().to_str_lossy().into_owned(), target)))
}
fn push_remote(r: &Repo) -> IdeResult<String> {
let repo = r.gix()?;
let from_branch = repo.head_name().map_err(gx)?.and_then(|head| {
+26
Переглянути файл
@@ -467,6 +467,32 @@ fn push_with_tags() {
assert_eq!(git_in(&bare, &["rev-parse", "main"]), fx.git(&["rev-parse", "HEAD"]));
}
#[test]
fn push_goes_to_an_upstream_with_another_name() {
let fx = Fixture::new();
let bare = fx._tmp.path().join("origin.git");
git_in(fx._tmp.path(), &["init", "-q", "--bare", bare.to_str().unwrap()]);
fx.write("a.txt", "1\n");
fx.commit_all("one");
fx.git(&["remote", "add", "origin", bare.to_str().unwrap()]);
fx.git(&["push", "-q", "origin", "main:release/one"]);
fx.git(&["fetch", "-q", "origin"]);
fx.git(&["checkout", "-q", "-b", "local-name", "--track", "origin/release/one"]);
fx.write("a.txt", "2\n");
let c2 = fx.commit_all("two");
let repo = fx.repo();
let (cancel, out) = quiet();
repo.push(false, false, &cancel, &out).unwrap();
assert_eq!(git_in(&bare, &["rev-parse", "release/one"]), c2);
assert_eq!(git_in(&bare, &["branch", "--list", "local-name"]), "");
fx.write("a.txt", "3\n");
let c3 = fx.commit_all("three");
repo.push_with_tags(false, false, Some(ide_vcs::PushTags::CurrentBranch), &cancel, &out).unwrap();
assert_eq!(git_in(&bare, &["rev-parse", "release/one"]), c3);
}
#[test]
fn push_branch_and_up_to_revision() {
let fx = Fixture::new();