Skip to content

Commit a92be3b

Browse files
authored
Get default fetch remote from configuration (#2204)
fixes #1093
1 parent 7651fdb commit a92be3b

File tree

6 files changed

+162
-23
lines changed

6 files changed

+162
-23
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
### Fixes
11+
* respect configuration for remote when fetching (also applies to pulling) [[@cruessler](https://github.com/cruessler)] ([#1093](https://github.com/extrawurst/gitui/issues/1093))
12+
1013
## [0.26.0+1] - 2024-04-14
1114

1215
**0.26.1**

asyncgit/src/sync/cred.rs

+51
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use super::{
44
remotes::{
5+
get_default_remote_for_fetch_in_repo,
56
get_default_remote_for_push_in_repo,
67
get_default_remote_in_repo,
78
},
@@ -49,6 +50,26 @@ pub fn need_username_password(repo_path: &RepoPath) -> Result<bool> {
4950
}
5051

5152
/// know if username and password are needed for this url
53+
/// TODO: Very similar to `need_username_password_for_fetch`. Can be refactored. See also
54+
/// `need_username_password`.
55+
pub fn need_username_password_for_fetch(
56+
repo_path: &RepoPath,
57+
) -> Result<bool> {
58+
let repo = repo(repo_path)?;
59+
let remote = repo
60+
.find_remote(&get_default_remote_for_fetch_in_repo(&repo)?)?;
61+
let url = remote
62+
.url()
63+
.or_else(|| remote.url())
64+
.ok_or(Error::UnknownRemote)?
65+
.to_owned();
66+
let is_http = url.starts_with("http");
67+
Ok(is_http)
68+
}
69+
70+
/// know if username and password are needed for this url
71+
/// TODO: Very similar to `need_username_password_for_fetch`. Can be refactored. See also
72+
/// `need_username_password`.
5273
pub fn need_username_password_for_push(
5374
repo_path: &RepoPath,
5475
) -> Result<bool> {
@@ -93,6 +114,36 @@ pub fn extract_username_password(
93114
}
94115

95116
/// extract username and password
117+
/// TODO: Very similar to `extract_username_password_for_fetch`. Can be refactored.
118+
pub fn extract_username_password_for_fetch(
119+
repo_path: &RepoPath,
120+
) -> Result<BasicAuthCredential> {
121+
let repo = repo(repo_path)?;
122+
let url = repo
123+
.find_remote(&get_default_remote_for_fetch_in_repo(&repo)?)?
124+
.url()
125+
.ok_or(Error::UnknownRemote)?
126+
.to_owned();
127+
let mut helper = CredentialHelper::new(&url);
128+
129+
//TODO: look at Cred::credential_helper,
130+
//if the username is in the url we need to set it here,
131+
//I dont think `config` will pick it up
132+
133+
if let Ok(config) = repo.config() {
134+
helper.config(&config);
135+
}
136+
137+
Ok(match helper.execute() {
138+
Some((username, password)) => {
139+
BasicAuthCredential::new(Some(username), Some(password))
140+
}
141+
None => extract_cred_from_url(&url),
142+
})
143+
}
144+
145+
/// extract username and password
146+
/// TODO: Very similar to `extract_username_password_for_fetch`. Can be refactored.
96147
pub fn extract_username_password_for_push(
97148
repo_path: &RepoPath,
98149
) -> Result<BasicAuthCredential> {

asyncgit/src/sync/mod.rs

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

asyncgit/src/sync/remotes/mod.rs

+78
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,47 @@ fn get_current_branch(
6666
Ok(None)
6767
}
6868

69+
/// Tries to find the default repo to fetch from based on configuration.
70+
///
71+
/// > branch.<name>.remote
72+
/// >
73+
/// > When on branch `<name>`, it tells `git fetch` and `git push` which remote to fetch from or
74+
/// > push to. [...] If no remote is configured, or if you are not on any branch and there is more
75+
/// > than one remote defined in the repository, it defaults to `origin` for fetching [...].
76+
///
77+
/// [git-config-branch-name-remote]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtremote
78+
///
79+
/// Falls back to `get_default_remote_in_repo`.
80+
pub fn get_default_remote_for_fetch(
81+
repo_path: &RepoPath,
82+
) -> Result<String> {
83+
let repo = repo(repo_path)?;
84+
get_default_remote_for_fetch_in_repo(&repo)
85+
}
86+
87+
// TODO: Very similar to `get_default_remote_for_push_in_repo`. Can probably be refactored.
88+
pub(crate) fn get_default_remote_for_fetch_in_repo(
89+
repo: &Repository,
90+
) -> Result<String> {
91+
scope_time!("get_default_remote_for_fetch_in_repo");
92+
93+
let config = repo.config()?;
94+
95+
let branch = get_current_branch(repo)?;
96+
97+
if let Some(branch) = branch {
98+
let remote_name = bytes2string(branch.name_bytes()?)?;
99+
100+
let entry_name = format!("branch.{}.remote", &remote_name);
101+
102+
if let Ok(entry) = config.get_entry(&entry_name) {
103+
return bytes2string(entry.value_bytes());
104+
}
105+
}
106+
107+
get_default_remote_in_repo(repo)
108+
}
109+
69110
/// Tries to find the default repo to push to based on configuration.
70111
///
71112
/// > remote.pushDefault
@@ -93,6 +134,7 @@ pub fn get_default_remote_for_push(
93134
get_default_remote_for_push_in_repo(&repo)
94135
}
95136

137+
// TODO: Very similar to `get_default_remote_for_fetch_in_repo`. Can probably be refactored.
96138
pub(crate) fn get_default_remote_for_push_in_repo(
97139
repo: &Repository,
98140
) -> Result<String> {
@@ -383,6 +425,42 @@ mod tests {
383425
));
384426
}
385427

428+
#[test]
429+
fn test_default_remote_for_fetch() {
430+
let (remote_dir, _remote) = repo_init().unwrap();
431+
let remote_path = remote_dir.path().to_str().unwrap();
432+
let (repo_dir, repo) = repo_clone(remote_path).unwrap();
433+
let repo_path: &RepoPath = &repo_dir
434+
.into_path()
435+
.as_os_str()
436+
.to_str()
437+
.unwrap()
438+
.into();
439+
440+
debug_cmd_print(
441+
repo_path,
442+
"git remote rename origin alternate",
443+
);
444+
445+
debug_cmd_print(
446+
repo_path,
447+
&format!("git remote add someremote {remote_path}")[..],
448+
);
449+
450+
let mut config = repo.config().unwrap();
451+
452+
config
453+
.set_str("branch.master.remote", "branchremote")
454+
.unwrap();
455+
456+
let default_fetch_remote =
457+
get_default_remote_for_fetch_in_repo(&repo);
458+
459+
assert!(
460+
matches!(default_fetch_remote, Ok(remote_name) if remote_name == "branchremote")
461+
);
462+
}
463+
386464
#[test]
387465
fn test_default_remote_for_push() {
388466
let (remote_dir, _remote) = repo_init().unwrap();

src/popups/pull.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ use asyncgit::{
1515
sync::{
1616
self,
1717
cred::{
18-
extract_username_password, need_username_password,
19-
BasicAuthCredential,
18+
extract_username_password_for_fetch,
19+
need_username_password_for_fetch, BasicAuthCredential,
2020
},
21-
get_default_remote, RepoPathRef,
21+
remotes::get_default_remote_for_fetch,
22+
RepoPathRef,
2223
},
2324
AsyncGitNotification, AsyncPull, FetchRequest, RemoteProgress,
2425
};
@@ -69,11 +70,11 @@ impl PullPopup {
6970
pub fn fetch(&mut self, branch: String) -> Result<()> {
7071
self.branch = branch;
7172
self.show()?;
72-
if need_username_password(&self.repo.borrow())? {
73-
let cred = extract_username_password(&self.repo.borrow())
74-
.unwrap_or_else(|_| {
75-
BasicAuthCredential::new(None, None)
76-
});
73+
if need_username_password_for_fetch(&self.repo.borrow())? {
74+
let cred = extract_username_password_for_fetch(
75+
&self.repo.borrow(),
76+
)
77+
.unwrap_or_else(|_| BasicAuthCredential::new(None, None));
7778
if cred.is_complete() {
7879
self.fetch_from_remote(Some(cred))
7980
} else {
@@ -92,7 +93,9 @@ impl PullPopup {
9293
self.pending = true;
9394
self.progress = None;
9495
self.git_fetch.request(FetchRequest {
95-
remote: get_default_remote(&self.repo.borrow())?,
96+
remote: get_default_remote_for_fetch(
97+
&self.repo.borrow(),
98+
)?,
9699
branch: self.branch.clone(),
97100
basic_credential: cred,
98101
})?;

src/tabs/status.rs

+15-12
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ enum DiffTarget {
5858
}
5959

6060
struct RemoteStatus {
61-
has_remotes: bool,
61+
has_remote_for_fetch: bool,
6262
has_remote_for_push: bool,
6363
}
6464

@@ -161,7 +161,7 @@ impl Status {
161161
queue: env.queue.clone(),
162162
visible: true,
163163
remotes: RemoteStatus {
164-
has_remotes: false,
164+
has_remote_for_fetch: false,
165165
has_remote_for_push: false,
166166
},
167167
git_state: RepoState::Clean,
@@ -415,9 +415,11 @@ impl Status {
415415
}
416416

417417
fn check_remotes(&mut self) {
418-
self.remotes.has_remotes =
419-
sync::get_default_remote(&self.repo.borrow().clone())
420-
.is_ok();
418+
self.remotes.has_remote_for_fetch =
419+
sync::get_default_remote_for_fetch(
420+
&self.repo.borrow().clone(),
421+
)
422+
.is_ok();
421423
self.remotes.has_remote_for_push =
422424
sync::get_default_remote_for_push(
423425
&self.repo.borrow().clone(),
@@ -580,7 +582,7 @@ impl Status {
580582
}
581583

582584
fn fetch(&self) {
583-
if self.can_pull() {
585+
if self.can_fetch() {
584586
self.queue.push(InternalEvent::FetchRemotes);
585587
}
586588
}
@@ -616,8 +618,9 @@ impl Status {
616618
is_ahead && self.remotes.has_remote_for_push
617619
}
618620

619-
const fn can_pull(&self) -> bool {
620-
self.remotes.has_remotes && self.git_branch_state.is_some()
621+
const fn can_fetch(&self) -> bool {
622+
self.remotes.has_remote_for_fetch
623+
&& self.git_branch_state.is_some()
621624
}
622625

623626
fn can_abort_merge(&self) -> bool {
@@ -754,12 +757,12 @@ impl Component for Status {
754757

755758
out.push(CommandInfo::new(
756759
strings::commands::status_fetch(&self.key_config),
757-
self.can_pull(),
760+
self.can_fetch(),
758761
!focus_on_diff,
759762
));
760763
out.push(CommandInfo::new(
761764
strings::commands::status_pull(&self.key_config),
762-
self.can_pull(),
765+
self.can_fetch(),
763766
!focus_on_diff,
764767
));
765768

@@ -881,13 +884,13 @@ impl Component for Status {
881884
Ok(EventState::Consumed)
882885
} else if key_match(k, self.key_config.keys.fetch)
883886
&& !self.is_focus_on_diff()
884-
&& self.can_pull()
887+
&& self.can_fetch()
885888
{
886889
self.fetch();
887890
Ok(EventState::Consumed)
888891
} else if key_match(k, self.key_config.keys.pull)
889892
&& !self.is_focus_on_diff()
890-
&& self.can_pull()
893+
&& self.can_fetch()
891894
{
892895
self.pull();
893896
Ok(EventState::Consumed)

0 commit comments

Comments
 (0)