Skip to content

Commit f052fd6

Browse files
committed
Allow users to define the default remote if one isn't discernable.
1 parent 09b9770 commit f052fd6

2 files changed

Lines changed: 76 additions & 20 deletions

File tree

src/git.rs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use std::collections::HashSet;
12
use std::path::Path;
23
use std::str;
34

45
use duct::cmd;
56
use git2::{Config, Error, Repository};
6-
use log::{debug, trace};
7+
use log::{debug, trace, warn};
78
use shellexpand;
89

910
/// Update old `req.key` config format to include remote name, i.e, `req.remote_name.key`
@@ -23,6 +24,15 @@ fn slugify_domain(domain: &str) -> String {
2324
str::replace(domain, ".", "|")
2425
}
2526

27+
/// Get the remotes for the repository
28+
pub fn get_remotes() -> HashSet<String> {
29+
let repo = Repository::open_from_env().expect("Couldn't find repository");
30+
match repo.remotes() {
31+
Ok(remotes) => remotes.into_iter().filter_map(|rem| rem).map(|rem| String::from(rem)).collect(),
32+
Err(_) => HashSet::new()
33+
}
34+
}
35+
2636
/// Get the URL of the given remote
2737
pub fn get_remote_url(remote: &str) -> String {
2838
let repo = Repository::open_from_env().expect("Couldn't find repository");
@@ -47,7 +57,7 @@ pub fn get_config(field_name: &str, remote_name: &str) -> Option<String> {
4757
}
4858
}
4959

50-
/// Set a value for the project-local git-req configuration
60+
/// Set a value for the project remote-local git-req configuration
5161
pub fn set_config(field_name: &str, remote_name: &str, value: &str) {
5262
migrate_legacy(field_name, "origin");
5363
let repo = Repository::open_from_env().expect("Couldn't find repository");
@@ -65,6 +75,24 @@ pub fn delete_config(field_name: &str, remote_name: &str) {
6575
.unwrap();
6676
}
6777

78+
/// Get a value for the given project-local git-req config
79+
pub fn get_project_config(field_name: &str) -> Option<String> {
80+
let key = format!("req.{}", field_name);
81+
match get_repo_info(&key) {
82+
Ok(val) => Some(val),
83+
Err(_) => None,
84+
}
85+
}
86+
87+
/// Set a value for the project-local git-req configuration. Consider using `set_config` unless
88+
/// absolutely necessary.
89+
pub fn set_project_config(field_name: &str, value: &str) {
90+
let repo = Repository::open_from_env().expect("Couldn't find repository");
91+
let mut cfg = repo.config().unwrap();
92+
cfg.set_str(&format!("req.{}", field_name), value)
93+
.unwrap();
94+
}
95+
6896
/// Get a value for the given global git-req config
6997
pub fn get_req_config(domain: &str, field: &str) -> Option<String> {
7098
let slug = slugify_domain(domain);
@@ -99,9 +127,9 @@ pub fn delete_req_config(domain: &str, field: &str) -> Result<(), Error> {
99127
cfg.remove(&format!("req.{}.{}", slug, field))
100128
}
101129

102-
/// Get the name of the default remote
130+
/// Guess the name of the default remote
103131
#[allow(clippy::match_wild_err_arm)]
104-
pub fn get_default_remote_name() -> Result<String, String> {
132+
pub fn guess_default_remote_name() -> Result<String, String> {
105133
let repo = Repository::open_from_env().expect("Couldn't find repository");
106134
let remotes = repo.remotes().expect("Couldn't fetch the list of remotes");
107135
match remotes.len() {
@@ -122,8 +150,8 @@ pub fn checkout_branch(
122150
is_virtual_remote_branch: bool,
123151
) -> Result<bool, String> {
124152
let repo = Repository::open_from_env().expect("Couldn't find repository");
125-
let local_branch_name = match get_default_remote_name() {
126-
Ok(default_remote_name) => {
153+
let local_branch_name = match get_project_config("defaultremote") {
154+
Some(default_remote_name) => {
127155
if remote_name != default_remote_name {
128156
trace!("Non-default remote name requested: {}", remote_name);
129157
format!("{}/{}", remote_name, local_branch_name)
@@ -132,9 +160,9 @@ pub fn checkout_branch(
132160
String::from(local_branch_name)
133161
}
134162
}
135-
Err(_) => {
136-
trace!(
137-
"Multiple remotes found, but none named origin: {}",
163+
None => {
164+
warn!(
165+
"No default remote found. Using {}",
138166
remote_name
139167
);
140168
format!("{}/{}", remote_name, local_branch_name)
@@ -172,6 +200,7 @@ pub fn checkout_branch(
172200
} else {
173201
&origin_with_remote
174202
};
203+
trace!("Checking '{}' as '{}'", remote_ref, local_branch_name);
175204
match cmd!("git", "checkout", "-b", &local_branch_name, &remote_ref).run() {
176205
Ok(_) => Ok(true),
177206
Err(err) => Err(format!("Could not check out local branch: {}", err)),

src/main.rs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
mod git;
33
mod remotes;
44

5-
use clap::{crate_authors, crate_version, load_yaml, App, AppSettings};
5+
use clap::{crate_authors, crate_version, load_yaml, App, AppSettings, ArgMatches};
66
use colored::*;
77
use git2::ErrorCode;
88
use log::{debug, error, info, trace};
9-
use std::io::{self, Cursor, Write};
9+
use std::io::{self, stdin, stdout, Cursor, Write};
1010
use std::{env, process};
1111
use tabwriter::TabWriter;
1212

@@ -164,6 +164,36 @@ fn generate_completion(app: &mut App, shell_name: &str) {
164164
print!("{}", &output);
165165
}
166166

167+
fn get_remote_name(matches: &ArgMatches) -> String {
168+
let default_remote_name = match git::get_project_config("defaultremote") {
169+
Some(remote_name) => remote_name,
170+
None => {
171+
let new_remote_name = match git::guess_default_remote_name() {
172+
Ok(guessed) => guessed,
173+
Err(_) => {
174+
let mut new_remote_name = String::new();
175+
println!("Multiple remotes detected. Enter the name of the default one.");
176+
for remote in git::get_remotes() {
177+
println!(" * {}", remote);
178+
}
179+
print!("Remote name: ");
180+
let _ = stdout().flush();
181+
stdin().read_line(&mut new_remote_name).expect("Did not input a name");
182+
trace!("New remote: {}", &new_remote_name);
183+
if !git::get_remotes().contains(new_remote_name.trim()) {
184+
panic!("Invalid remote name provided")
185+
}
186+
new_remote_name
187+
}
188+
};
189+
git::set_project_config("defaultremote", &new_remote_name);
190+
new_remote_name
191+
}
192+
};
193+
// Not using Clap's default_value because of https://github.com/clap-rs/clap/issues/1140
194+
String::from(matches.value_of("REMOTE_NAME").unwrap_or(&default_remote_name))
195+
}
196+
167197
fn build_cli(cfg: &yaml_rust::Yaml) -> App {
168198
App::from_yaml(&cfg)
169199
.version(crate_version!())
@@ -183,25 +213,22 @@ fn main() {
183213
let app = build_cli(&cfg);
184214
let matches = app.get_matches();
185215

186-
// Not using Clap's default_value because of https://github.com/clap-rs/clap/issues/1140
187-
let remote_name = matches.value_of("REMOTE_NAME").unwrap_or("origin");
188-
189216
if let Some(project_id) = matches.value_of("NEW_PROJECT_ID") {
190-
set_project_id(remote_name, project_id);
217+
set_project_id(&get_remote_name(&matches), project_id);
191218
} else if matches.is_present("CLEAR_PROJECT_ID") {
192-
clear_project_id(remote_name);
219+
clear_project_id(&get_remote_name(&matches));
193220
} else if matches.is_present("LIST_MR") {
194-
list_open_requests(remote_name);
221+
list_open_requests(&get_remote_name(&matches));
195222
} else if matches.is_present("CLEAR_DOMAIN_KEY") {
196-
clear_domain_key(remote_name);
223+
clear_domain_key(&get_remote_name(&matches));
197224
} else if let Some(domain_key) = matches.value_of("NEW_DOMAIN_KEY") {
198-
set_domain_key(remote_name, domain_key);
225+
set_domain_key(&get_remote_name(&matches), domain_key);
199226
} else if let Some(shell_name) = matches.value_of("GENERATE_COMPLETIONS") {
200227
let mut app = build_cli(&cfg);
201228
generate_completion(&mut app, &shell_name);
202229
} else {
203230
checkout_mr(
204-
remote_name,
231+
&get_remote_name(&matches),
205232
matches.value_of("REQUEST_ID").unwrap().parse().unwrap(),
206233
);
207234
}

0 commit comments

Comments
 (0)