Get default push remote from configuration (#2156)

This commit is contained in:
Christoph Rüßler
2024-04-14 12:12:09 +02:00
committed by GitHub
parent fe05f93a74
commit b08eddb45b
8 changed files with 229 additions and 29 deletions
+7 -7
View File
@@ -5,13 +5,13 @@ pub mod merge_ff;
pub mod merge_rebase;
pub mod rename;
use super::{
remotes::get_default_remote_in_repo, utils::bytes2string,
RepoPath,
};
use super::{utils::bytes2string, RepoPath};
use crate::{
error::{Error, Result},
sync::{repository::repo, utils::get_head_repo, CommitId},
sync::{
remotes::get_default_remote_for_push_in_repo,
repository::repo, utils::get_head_repo, CommitId,
},
};
use git2::{Branch, BranchType, Repository};
use scopetime::scope_time;
@@ -209,7 +209,7 @@ pub struct BranchCompare {
}
///
pub(crate) fn branch_set_upstream(
pub(crate) fn branch_set_upstream_after_push(
repo: &Repository,
branch_name: &str,
) -> Result<()> {
@@ -219,7 +219,7 @@ pub(crate) fn branch_set_upstream(
repo.find_branch(branch_name, BranchType::Local)?;
if branch.upstream().is_err() {
let remote = get_default_remote_in_repo(repo)?;
let remote = get_default_remote_for_push_in_repo(repo)?;
let upstream_name = format!("{remote}/{branch_name}");
branch.set_upstream(Some(upstream_name.as_str()))?;
}
+50 -1
View File
@@ -1,7 +1,12 @@
//! credentials git helper
use super::{
remotes::get_default_remote_in_repo, repository::repo, RepoPath,
remotes::{
get_default_remote_for_push_in_repo,
get_default_remote_in_repo,
},
repository::repo,
RepoPath,
};
use crate::error::{Error, Result};
use git2::CredentialHelper;
@@ -43,6 +48,22 @@ pub fn need_username_password(repo_path: &RepoPath) -> Result<bool> {
Ok(is_http)
}
/// know if username and password are needed for this url
pub fn need_username_password_for_push(
repo_path: &RepoPath,
) -> Result<bool> {
let repo = repo(repo_path)?;
let remote = repo
.find_remote(&get_default_remote_for_push_in_repo(&repo)?)?;
let url = remote
.pushurl()
.or_else(|| remote.url())
.ok_or(Error::UnknownRemote)?
.to_owned();
let is_http = url.starts_with("http");
Ok(is_http)
}
/// extract username and password
pub fn extract_username_password(
repo_path: &RepoPath,
@@ -71,6 +92,34 @@ pub fn extract_username_password(
})
}
/// extract username and password
pub fn extract_username_password_for_push(
repo_path: &RepoPath,
) -> Result<BasicAuthCredential> {
let repo = repo(repo_path)?;
let url = repo
.find_remote(&get_default_remote_for_push_in_repo(&repo)?)?
.url()
.ok_or(Error::UnknownRemote)?
.to_owned();
let mut helper = CredentialHelper::new(&url);
//TODO: look at Cred::credential_helper,
//if the username is in the url we need to set it here,
//I dont think `config` will pick it up
if let Ok(config) = repo.config() {
helper.config(&config);
}
Ok(match helper.execute() {
Some((username, password)) => {
BasicAuthCredential::new(Some(username), Some(password))
}
None => extract_cred_from_url(&url),
})
}
/// extract credentials from url
pub fn extract_cred_from_url(url: &str) -> BasicAuthCredential {
url::Url::parse(url).map_or_else(
+2 -2
View File
@@ -79,8 +79,8 @@ pub use merge::{
};
pub use rebase::rebase_branch;
pub use remotes::{
get_default_remote, get_remotes, push::AsyncProgress,
tags::PushTagsProgress,
get_default_remote, get_default_remote_for_push, get_remotes,
push::AsyncProgress, tags::PushTagsProgress,
};
pub(crate) use repository::repo;
pub use repository::{RepoPath, RepoPathRef};
+137 -3
View File
@@ -51,6 +51,81 @@ pub fn get_default_remote(repo_path: &RepoPath) -> Result<String> {
get_default_remote_in_repo(&repo)
}
/// Gets the current branch the user is on.
/// Returns none if they are not on a branch
/// and Err if there was a problem finding the branch
fn get_current_branch(
repo: &Repository,
) -> Result<Option<git2::Branch>> {
for b in repo.branches(None)? {
let branch = b?.0;
if branch.is_head() {
return Ok(Some(branch));
}
}
Ok(None)
}
/// Tries to find the default repo to push to based on configuration.
///
/// > remote.pushDefault
/// >
/// > The remote to push to by default. Overrides `branch.<name>.remote` for all branches, and is
/// > overridden by `branch.<name>.pushRemote` for specific branches.
///
/// > branch.<name>.remote
/// >
/// > When on branch `<name>`, it tells `git fetch` and `git push` which remote to fetch from or
/// > push to. The remote to push to may be overridden with `remote.pushDefault` (for all
/// > branches). The remote to push to, for the current branch, may be further overridden by
/// > `branch.<name>.pushRemote`. If no remote is configured, or if you are not on any branch and
/// > there is more than one remote defined in the repository, it defaults to `origin` for fetching
/// > and `remote.pushDefault` for pushing.
///
/// [git-config-remote-push-default]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-remotepushDefault
/// [git-config-branch-name-remote]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtremote
///
/// Falls back to `get_default_remote_in_repo`.
pub fn get_default_remote_for_push(
repo_path: &RepoPath,
) -> Result<String> {
let repo = repo(repo_path)?;
get_default_remote_for_push_in_repo(&repo)
}
pub(crate) fn get_default_remote_for_push_in_repo(
repo: &Repository,
) -> Result<String> {
scope_time!("get_default_remote_for_push_in_repo");
let config = repo.config()?;
let branch = get_current_branch(repo)?;
if let Some(branch) = branch {
let remote_name = bytes2string(branch.name_bytes()?)?;
let entry_name =
format!("branch.{}.pushRemote", &remote_name);
if let Ok(entry) = config.get_entry(&entry_name) {
return bytes2string(entry.value_bytes());
}
if let Ok(entry) = config.get_entry("remote.pushDefault") {
return bytes2string(entry.value_bytes());
}
let entry_name = format!("branch.{}.remote", &remote_name);
if let Ok(entry) = config.get_entry(&entry_name) {
return bytes2string(entry.value_bytes());
}
}
get_default_remote_in_repo(repo)
}
/// see `get_default_remote`
pub(crate) fn get_default_remote_in_repo(
repo: &Repository,
@@ -299,9 +374,68 @@ mod tests {
]
);
let res =
let default_remote =
get_default_remote_in_repo(&repo(repo_path).unwrap());
assert_eq!(res.is_err(), true);
assert!(matches!(res, Err(Error::NoDefaultRemoteFound)));
assert!(matches!(
default_remote,
Err(Error::NoDefaultRemoteFound)
));
}
#[test]
fn test_default_remote_for_push() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath = &repo_dir
.into_path()
.as_os_str()
.to_str()
.unwrap()
.into();
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
&format!("git remote add someremote {remote_path}")[..],
);
let mut config = repo.config().unwrap();
config
.set_str("branch.master.remote", "branchremote")
.unwrap();
let default_push_remote =
get_default_remote_for_push_in_repo(&repo);
assert!(
matches!(default_push_remote, Ok(remote_name) if remote_name == "branchremote")
);
config.set_str("remote.pushDefault", "pushdefault").unwrap();
let default_push_remote =
get_default_remote_for_push_in_repo(&repo);
assert!(
matches!(default_push_remote, Ok(remote_name) if remote_name == "pushdefault")
);
config
.set_str("branch.master.pushRemote", "branchpushremote")
.unwrap();
let default_push_remote =
get_default_remote_for_push_in_repo(&repo);
assert!(
matches!(default_push_remote, Ok(remote_name) if remote_name == "branchpushremote")
);
}
}
+2 -2
View File
@@ -2,7 +2,7 @@ use crate::{
error::{Error, Result},
progress::ProgressPercent,
sync::{
branch::branch_set_upstream,
branch::branch_set_upstream_after_push,
cred::BasicAuthCredential,
remotes::{proxy_auto, Callbacks},
repository::repo,
@@ -176,7 +176,7 @@ pub fn push_raw(
}
if !delete {
branch_set_upstream(&repo, branch)?;
branch_set_upstream_after_push(&repo, branch)?;
}
Ok(())