Skip to content

Commit 0972057

Browse files
cruesslerIndianBoy42
authored andcommitted
Get default push remote from configuration (gitui-org#2156)
1 parent 3efdbb0 commit 0972057

File tree

8 files changed

+229
-29
lines changed

8 files changed

+229
-29
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
* support `core.commitChar` filtering [[@concelare](https://github.com/concelare)] ([#2136](https://github.com/extrawurst/gitui/issues/2136))
1515
* allow reset in branch popup ([#2170](https://github.com/extrawurst/gitui/issues/2170))
1616
* support ssh commit signing (when `user.signingKey` and `gpg.format = ssh` of gitconfig are set; ssh-agent isn't yet supported) [[@yanganto](https://github.com/yanganto)] ([#1149](https://github.com/extrawurst/gitui/issues/1149))
17+
* respect configuration for remote when pushing [[@cruessler](https://github.com/cruessler)]
1718

1819
### Changed
1920
* Make info and error message popups scrollable [[@MichaelAug](https://github.com/MichaelAug)] ([#1138](https://github.com/extrawurst/gitui/issues/1138))

asyncgit/src/sync/branch/mod.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ pub mod merge_ff;
55
pub mod merge_rebase;
66
pub mod rename;
77

8-
use super::{
9-
remotes::get_default_remote_in_repo, utils::bytes2string,
10-
RepoPath,
11-
};
8+
use super::{utils::bytes2string, RepoPath};
129
use crate::{
1310
error::{Error, Result},
14-
sync::{repository::repo, utils::get_head_repo, CommitId},
11+
sync::{
12+
remotes::get_default_remote_for_push_in_repo,
13+
repository::repo, utils::get_head_repo, CommitId,
14+
},
1515
};
1616
use git2::{Branch, BranchType, Repository};
1717
use scopetime::scope_time;
@@ -209,7 +209,7 @@ pub struct BranchCompare {
209209
}
210210

211211
///
212-
pub(crate) fn branch_set_upstream(
212+
pub(crate) fn branch_set_upstream_after_push(
213213
repo: &Repository,
214214
branch_name: &str,
215215
) -> Result<()> {
@@ -219,7 +219,7 @@ pub(crate) fn branch_set_upstream(
219219
repo.find_branch(branch_name, BranchType::Local)?;
220220

221221
if branch.upstream().is_err() {
222-
let remote = get_default_remote_in_repo(repo)?;
222+
let remote = get_default_remote_for_push_in_repo(repo)?;
223223
let upstream_name = format!("{remote}/{branch_name}");
224224
branch.set_upstream(Some(upstream_name.as_str()))?;
225225
}

asyncgit/src/sync/cred.rs

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
//! credentials git helper
22
33
use super::{
4-
remotes::get_default_remote_in_repo, repository::repo, RepoPath,
4+
remotes::{
5+
get_default_remote_for_push_in_repo,
6+
get_default_remote_in_repo,
7+
},
8+
repository::repo,
9+
RepoPath,
510
};
611
use crate::error::{Error, Result};
712
use git2::CredentialHelper;
@@ -43,6 +48,22 @@ pub fn need_username_password(repo_path: &RepoPath) -> Result<bool> {
4348
Ok(is_http)
4449
}
4550

51+
/// know if username and password are needed for this url
52+
pub fn need_username_password_for_push(
53+
repo_path: &RepoPath,
54+
) -> Result<bool> {
55+
let repo = repo(repo_path)?;
56+
let remote = repo
57+
.find_remote(&get_default_remote_for_push_in_repo(&repo)?)?;
58+
let url = remote
59+
.pushurl()
60+
.or_else(|| remote.url())
61+
.ok_or(Error::UnknownRemote)?
62+
.to_owned();
63+
let is_http = url.starts_with("http");
64+
Ok(is_http)
65+
}
66+
4667
/// extract username and password
4768
pub fn extract_username_password(
4869
repo_path: &RepoPath,
@@ -71,6 +92,34 @@ pub fn extract_username_password(
7192
})
7293
}
7394

95+
/// extract username and password
96+
pub fn extract_username_password_for_push(
97+
repo_path: &RepoPath,
98+
) -> Result<BasicAuthCredential> {
99+
let repo = repo(repo_path)?;
100+
let url = repo
101+
.find_remote(&get_default_remote_for_push_in_repo(&repo)?)?
102+
.url()
103+
.ok_or(Error::UnknownRemote)?
104+
.to_owned();
105+
let mut helper = CredentialHelper::new(&url);
106+
107+
//TODO: look at Cred::credential_helper,
108+
//if the username is in the url we need to set it here,
109+
//I dont think `config` will pick it up
110+
111+
if let Ok(config) = repo.config() {
112+
helper.config(&config);
113+
}
114+
115+
Ok(match helper.execute() {
116+
Some((username, password)) => {
117+
BasicAuthCredential::new(Some(username), Some(password))
118+
}
119+
None => extract_cred_from_url(&url),
120+
})
121+
}
122+
74123
/// extract credentials from url
75124
pub fn extract_cred_from_url(url: &str) -> BasicAuthCredential {
76125
url::Url::parse(url).map_or_else(

asyncgit/src/sync/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ pub use merge::{
7979
};
8080
pub use rebase::rebase_branch;
8181
pub use remotes::{
82-
get_default_remote, get_remotes, push::AsyncProgress,
83-
tags::PushTagsProgress,
82+
get_default_remote, get_default_remote_for_push, get_remotes,
83+
push::AsyncProgress, tags::PushTagsProgress,
8484
};
8585
pub(crate) use repository::repo;
8686
pub use repository::{RepoPath, RepoPathRef};

asyncgit/src/sync/remotes/mod.rs

+137-3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,81 @@ pub fn get_default_remote(repo_path: &RepoPath) -> Result<String> {
5151
get_default_remote_in_repo(&repo)
5252
}
5353

54+
/// Gets the current branch the user is on.
55+
/// Returns none if they are not on a branch
56+
/// and Err if there was a problem finding the branch
57+
fn get_current_branch(
58+
repo: &Repository,
59+
) -> Result<Option<git2::Branch>> {
60+
for b in repo.branches(None)? {
61+
let branch = b?.0;
62+
if branch.is_head() {
63+
return Ok(Some(branch));
64+
}
65+
}
66+
Ok(None)
67+
}
68+
69+
/// Tries to find the default repo to push to based on configuration.
70+
///
71+
/// > remote.pushDefault
72+
/// >
73+
/// > The remote to push to by default. Overrides `branch.<name>.remote` for all branches, and is
74+
/// > overridden by `branch.<name>.pushRemote` for specific branches.
75+
///
76+
/// > branch.<name>.remote
77+
/// >
78+
/// > When on branch `<name>`, it tells `git fetch` and `git push` which remote to fetch from or
79+
/// > push to. The remote to push to may be overridden with `remote.pushDefault` (for all
80+
/// > branches). The remote to push to, for the current branch, may be further overridden by
81+
/// > `branch.<name>.pushRemote`. If no remote is configured, or if you are not on any branch and
82+
/// > there is more than one remote defined in the repository, it defaults to `origin` for fetching
83+
/// > and `remote.pushDefault` for pushing.
84+
///
85+
/// [git-config-remote-push-default]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-remotepushDefault
86+
/// [git-config-branch-name-remote]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtremote
87+
///
88+
/// Falls back to `get_default_remote_in_repo`.
89+
pub fn get_default_remote_for_push(
90+
repo_path: &RepoPath,
91+
) -> Result<String> {
92+
let repo = repo(repo_path)?;
93+
get_default_remote_for_push_in_repo(&repo)
94+
}
95+
96+
pub(crate) fn get_default_remote_for_push_in_repo(
97+
repo: &Repository,
98+
) -> Result<String> {
99+
scope_time!("get_default_remote_for_push_in_repo");
100+
101+
let config = repo.config()?;
102+
103+
let branch = get_current_branch(repo)?;
104+
105+
if let Some(branch) = branch {
106+
let remote_name = bytes2string(branch.name_bytes()?)?;
107+
108+
let entry_name =
109+
format!("branch.{}.pushRemote", &remote_name);
110+
111+
if let Ok(entry) = config.get_entry(&entry_name) {
112+
return bytes2string(entry.value_bytes());
113+
}
114+
115+
if let Ok(entry) = config.get_entry("remote.pushDefault") {
116+
return bytes2string(entry.value_bytes());
117+
}
118+
119+
let entry_name = format!("branch.{}.remote", &remote_name);
120+
121+
if let Ok(entry) = config.get_entry(&entry_name) {
122+
return bytes2string(entry.value_bytes());
123+
}
124+
}
125+
126+
get_default_remote_in_repo(repo)
127+
}
128+
54129
/// see `get_default_remote`
55130
pub(crate) fn get_default_remote_in_repo(
56131
repo: &Repository,
@@ -299,9 +374,68 @@ mod tests {
299374
]
300375
);
301376

302-
let res =
377+
let default_remote =
303378
get_default_remote_in_repo(&repo(repo_path).unwrap());
304-
assert_eq!(res.is_err(), true);
305-
assert!(matches!(res, Err(Error::NoDefaultRemoteFound)));
379+
380+
assert!(matches!(
381+
default_remote,
382+
Err(Error::NoDefaultRemoteFound)
383+
));
384+
}
385+
386+
#[test]
387+
fn test_default_remote_for_push() {
388+
let (remote_dir, _remote) = repo_init().unwrap();
389+
let remote_path = remote_dir.path().to_str().unwrap();
390+
let (repo_dir, repo) = repo_clone(remote_path).unwrap();
391+
let repo_path: &RepoPath = &repo_dir
392+
.into_path()
393+
.as_os_str()
394+
.to_str()
395+
.unwrap()
396+
.into();
397+
398+
debug_cmd_print(
399+
repo_path,
400+
"git remote rename origin alternate",
401+
);
402+
403+
debug_cmd_print(
404+
repo_path,
405+
&format!("git remote add someremote {remote_path}")[..],
406+
);
407+
408+
let mut config = repo.config().unwrap();
409+
410+
config
411+
.set_str("branch.master.remote", "branchremote")
412+
.unwrap();
413+
414+
let default_push_remote =
415+
get_default_remote_for_push_in_repo(&repo);
416+
417+
assert!(
418+
matches!(default_push_remote, Ok(remote_name) if remote_name == "branchremote")
419+
);
420+
421+
config.set_str("remote.pushDefault", "pushdefault").unwrap();
422+
423+
let default_push_remote =
424+
get_default_remote_for_push_in_repo(&repo);
425+
426+
assert!(
427+
matches!(default_push_remote, Ok(remote_name) if remote_name == "pushdefault")
428+
);
429+
430+
config
431+
.set_str("branch.master.pushRemote", "branchpushremote")
432+
.unwrap();
433+
434+
let default_push_remote =
435+
get_default_remote_for_push_in_repo(&repo);
436+
437+
assert!(
438+
matches!(default_push_remote, Ok(remote_name) if remote_name == "branchpushremote")
439+
);
306440
}
307441
}

asyncgit/src/sync/remotes/push.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
error::{Error, Result},
33
progress::ProgressPercent,
44
sync::{
5-
branch::branch_set_upstream,
5+
branch::branch_set_upstream_after_push,
66
cred::BasicAuthCredential,
77
remotes::{proxy_auto, Callbacks},
88
repository::repo,
@@ -176,7 +176,7 @@ pub fn push_raw(
176176
}
177177

178178
if !delete {
179-
branch_set_upstream(&repo, branch)?;
179+
branch_set_upstream_after_push(&repo, branch)?;
180180
}
181181

182182
Ok(())

src/popups/push.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ use anyhow::Result;
1313
use asyncgit::{
1414
sync::{
1515
cred::{
16-
extract_username_password, need_username_password,
17-
BasicAuthCredential,
16+
extract_username_password_for_push,
17+
need_username_password_for_push, BasicAuthCredential,
1818
},
19-
get_branch_remote, get_default_remote, RepoPathRef,
19+
get_branch_remote,
20+
remotes::get_default_remote_for_push,
21+
RepoPathRef,
2022
},
2123
AsyncGitNotification, AsyncPush, PushRequest, PushType,
2224
RemoteProgress, RemoteProgressState,
@@ -104,11 +106,11 @@ impl PushPopup {
104106

105107
self.show()?;
106108

107-
if need_username_password(&self.repo.borrow())? {
108-
let cred = extract_username_password(&self.repo.borrow())
109-
.unwrap_or_else(|_| {
110-
BasicAuthCredential::new(None, None)
111-
});
109+
if need_username_password_for_push(&self.repo.borrow())? {
110+
let cred = extract_username_password_for_push(
111+
&self.repo.borrow(),
112+
)
113+
.unwrap_or_else(|_| BasicAuthCredential::new(None, None));
112114
if cred.is_complete() {
113115
self.push_to_remote(Some(cred), force)
114116
} else {
@@ -132,7 +134,8 @@ impl PushPopup {
132134
remote
133135
} else {
134136
log::info!("push: branch '{}' has no upstream - looking up default remote",self.branch);
135-
let remote = get_default_remote(&self.repo.borrow())?;
137+
let remote =
138+
get_default_remote_for_push(&self.repo.borrow())?;
136139
log::info!(
137140
"push: branch '{}' to remote '{}'",
138141
self.branch,

0 commit comments

Comments
 (0)