Skip to content

Commit 99598d8

Browse files
committed
Support getting/setting git config values via but config
Add key/value handling to the Config subcommand so `but config <key> [value]` can get or set configuration values (e.g.,user.name / user.email). Implement config::handle to dispatch: setting writes to the local repo config, getting uses existing lookup logic (including user.name/email helpers) and prints JSON when requested; fall back to the original show() behavior when no key is provided. Update CLI args to accept optional key and value and wire the new handler into main. This change was needed so that commands like `but config user.name <name>` actually set the name and `but config user.name` returns the effective value using the same lookup order as other config reads. It preserves existing behavior for showing all configuration and improves UX by supporting direct get/set operations.
1 parent 59b7382 commit 99598d8

File tree

3 files changed

+78
-4
lines changed

3 files changed

+78
-4
lines changed

crates/but/src/args.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,13 @@ pub enum Subcommands {
3939
#[clap(long, short = 'b')]
4040
base: bool,
4141
},
42-
/// Display configuration information about the GitButler repository.
43-
Config,
42+
/// Display or set configuration values for the repository.
43+
Config {
44+
/// Configuration key to get or set (e.g., user.name, user.email)
45+
key: Option<String>,
46+
/// Value to set (if provided, sets the key to this value)
47+
value: Option<String>,
48+
},
4449

4550
/// Show operation history (last 20 entries).
4651
Oplog {

crates/but/src/config.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,42 @@ pub struct AiProviderInfo {
4444
pub model: Option<String>,
4545
}
4646

47+
pub fn handle(current_dir: &Path, app_settings: &AppSettings, json: bool, key: Option<&str>, value: Option<&str>) -> Result<()> {
48+
match (key, value) {
49+
// Set configuration value
50+
(Some(key), Some(value)) => {
51+
set_config_value(current_dir, key, value)?;
52+
if !json {
53+
println!("Set {} = {}", key, value);
54+
}
55+
Ok(())
56+
}
57+
// Get specific configuration value
58+
(Some(key), None) => {
59+
let config_value = get_config_value(current_dir, key)?;
60+
if json {
61+
let result = serde_json::json!({
62+
"key": key,
63+
"value": config_value
64+
});
65+
println!("{}", serde_json::to_string_pretty(&result)?);
66+
} else {
67+
match config_value {
68+
Some(val) => println!("{}", val),
69+
None => println!("{} is not set", key),
70+
}
71+
}
72+
Ok(())
73+
}
74+
// Show all configuration (existing behavior)
75+
(None, None) => show(current_dir, app_settings, json),
76+
// Invalid: value without key
77+
(None, Some(_)) => {
78+
Err(anyhow::anyhow!("Cannot set a value without specifying a key"))
79+
}
80+
}
81+
}
82+
4783
pub fn show(current_dir: &Path, app_settings: &AppSettings, json: bool) -> Result<()> {
4884
let config_info = gather_config_info(current_dir, app_settings)?;
4985

@@ -219,3 +255,36 @@ fn print_ai_provider(name: &str, provider: &AiProviderInfo) {
219255
println!(" {}", model_info.trim_start());
220256
}
221257
}
258+
259+
fn set_config_value(current_dir: &Path, key: &str, value: &str) -> Result<()> {
260+
let git_repo = git2::Repository::discover(current_dir)
261+
.context("Failed to find Git repository")?;
262+
let config = Config::from(&git_repo);
263+
264+
config.set_local(key, value)
265+
.with_context(|| format!("Failed to set {} = {}", key, value))
266+
}
267+
268+
fn get_config_value(current_dir: &Path, key: &str) -> Result<Option<String>> {
269+
let git_repo = git2::Repository::discover(current_dir)
270+
.context("Failed to find Git repository")?;
271+
let config = Config::from(&git_repo);
272+
273+
// For getting values, use the same logic as the existing code
274+
// which checks the full git config hierarchy (local, global, system)
275+
match key {
276+
"user.name" => config.user_name(),
277+
"user.email" => config.user_email(),
278+
_ => {
279+
// For other keys, try to get the value from git config
280+
let git_config = git_repo.config()?;
281+
match git_config.get_string(key) {
282+
Ok(value) => Ok(Some(value)),
283+
Err(err) => match err.code() {
284+
git2::ErrorCode::NotFound => Ok(None),
285+
_ => Err(err.into()),
286+
},
287+
}
288+
}
289+
}
290+
}

crates/but/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ async fn main() -> Result<()> {
110110
metrics_if_configured(app_settings, CommandName::Status, props(start, &result)).ok();
111111
Ok(())
112112
}
113-
Subcommands::Config => {
114-
let result = config::show(&args.current_dir, &app_settings, args.json);
113+
Subcommands::Config { key, value } => {
114+
let result = config::handle(&args.current_dir, &app_settings, args.json, key.as_deref(), value.as_deref());
115115
metrics_if_configured(app_settings, CommandName::Config, props(start, &result)).ok();
116116
result
117117
}

0 commit comments

Comments
 (0)