@@ -35,12 +35,10 @@ pub fn run() -> Result<()> {
3535 options. extend ( saved_builds. iter ( ) . map ( |b| format ! ( "Use build: {b}" ) ) ) ;
3636
3737 let choice = Select :: new ( "Saved builds available" , options)
38- . with_help_message ( "↑↓ move enter confirm" )
39- . prompt_skippable ( ) ?
40- . unwrap_or_default ( ) ;
38+ . with_help_message ( "↑↓ move enter confirm esc start fresh" )
39+ . prompt_skippable ( ) ?;
4140
42- if choice != "Start fresh configuration" {
43- let build_name = choice. strip_prefix ( "Use build: " ) . unwrap_or ( & choice) ;
41+ if let Some ( build_name) = choice. as_deref ( ) . and_then ( |c| c. strip_prefix ( "Use build: " ) ) {
4442 println ! ( ) ;
4543 let build = builds:: load_build ( build_name) ?;
4644 builds:: apply_build ( & build) ?;
@@ -97,10 +95,17 @@ pub fn run() -> Result<()> {
9795
9896 for item in & hook_selections {
9997 if item == "Add custom hook..." {
100- let hook_name = Select :: new ( "Hook type" , hooks:: valid_hook_names ( ) . to_vec ( ) )
101- . prompt ( )
102- . map_err ( |e| anyhow:: anyhow!( "Hook selection cancelled: {}" , e) ) ?;
103- let command = Text :: new ( " Command to run" ) . prompt ( ) ?;
98+ let Some ( hook_name) = Select :: new ( "Hook type" , hooks:: valid_hook_names ( ) . to_vec ( ) )
99+ . prompt_skippable ( ) ?
100+ else {
101+ continue ;
102+ } ;
103+ let command = Text :: new ( " Command to run" )
104+ . prompt_skippable ( ) ?
105+ . unwrap_or_default ( ) ;
106+ if command. trim ( ) . is_empty ( ) {
107+ continue ;
108+ }
104109 custom_hooks. push ( ( hook_name. to_string ( ) , command) ) ;
105110 } else if let Some ( idx) = hook_items. iter ( ) . position ( |i| i == item) {
106111 if idx < builtins. len ( ) {
@@ -170,7 +175,7 @@ pub fn run() -> Result<()> {
170175 let defaults: Vec < usize > = config_options
171176 . iter ( )
172177 . enumerate ( )
173- . filter ( |( _, o) | o. recommended )
178+ . filter ( |( _, o) | o. recommended || configured_keys . contains ( o . key ) )
174179 . map ( |( i, _) | i)
175180 . collect ( ) ;
176181
@@ -247,6 +252,16 @@ pub fn run() -> Result<()> {
247252 }
248253 for hook in & hooks_to_remove {
249254 if let Some ( builtin) = hooks:: available_builtins ( ) . iter ( ) . find ( |b| b. name == * hook) {
255+ // Several built-ins can share a hook file (e.g. pre-commit); don't
256+ // delete the file if a freshly installed selection now owns it.
257+ let file_reused = selected_builtins. iter ( ) . any ( |sel| {
258+ hooks:: available_builtins ( )
259+ . iter ( )
260+ . any ( |b| b. name == * sel && b. hook == builtin. hook )
261+ } ) ;
262+ if file_reused {
263+ continue ;
264+ }
250265 if hooks:: remove_hook ( builtin. hook , true ) . is_ok ( ) {
251266 println ! ( " ◇ hook '{hook}' removed ✓" ) ;
252267 }
@@ -269,20 +284,19 @@ pub fn run() -> Result<()> {
269284 ) ?;
270285 println ! ( " ◇ git config applied ✓" ) ;
271286 }
287+ // Only touch the repo's local config; a global value affects every repo,
288+ // so it is never removed from here.
272289 for key in & configs_to_remove {
273- let removed = config:: remove_config_key ( key, config:: ConfigScope :: Local ) . is_ok ( )
274- || config:: remove_config_key ( key, config:: ConfigScope :: Global ) . is_ok ( ) ;
275- if removed {
290+ if config:: remove_config_key ( key, config:: ConfigScope :: Local ) . is_ok ( ) {
276291 println ! ( " ◇ git config '{key}' removed ✓" ) ;
292+ } else {
293+ println ! (
294+ " ◇ git config '{key}' is set globally — left untouched (git config --global --unset {key})"
295+ ) ;
277296 }
278297 }
279298
280299 // ── Save as build ─────────────────────────────────────────────────────
281- if nothing {
282- println ! ( "\n Nothing selected — exiting." ) ;
283- return Ok ( ( ) ) ;
284- }
285-
286300 println ! ( ) ;
287301 let save_build = inquire:: Confirm :: new ( "Save this configuration as a reusable build?" )
288302 . with_default ( false )
@@ -298,19 +312,9 @@ pub fn run() -> Result<()> {
298312 } else {
299313 Some ( description. as_str ( ) )
300314 } ;
301- builds:: capture_current_config ( & name, desc_ref)
302- . and_then ( |b| {
303- let dir = builds:: builds_dir ( ) ?;
304- fs:: create_dir_all ( & dir) ?;
305- let path = dir. join ( format ! ( "{name}.toml" ) ) ;
306- let content = toml:: to_string_pretty ( & b) ?;
307- fs:: write ( & path, content) ?;
308- println ! ( " ✓ Build '{name}' saved" ) ;
309- Ok ( ( ) )
310- } )
311- . unwrap_or_else ( |e| {
312- println ! ( " ⚠ Failed to save build: {e}" ) ;
313- } ) ;
315+ if let Err ( e) = builds:: save ( & name, desc_ref) {
316+ println ! ( " ⚠ Failed to save build: {e}" ) ;
317+ }
314318 }
315319
316320 println ! ( "\n Done\n " ) ;
@@ -331,16 +335,7 @@ fn get_installed_hooks() -> HashSet<String> {
331335 let name = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
332336 if !name. ends_with ( ".bak" ) && !name. ends_with ( ".sample" ) {
333337 let content = fs:: read_to_string ( entry. path ( ) ) . unwrap_or_default ( ) ;
334- let builtin_match = hooks:: available_builtins ( ) . iter ( ) . find ( |b| {
335- b. hook == name
336- && b. script
337- . chars ( )
338- . take ( 80 )
339- . collect :: < String > ( )
340- . chars ( )
341- . all ( |c| content. contains ( c) )
342- } ) ;
343- if let Some ( b) = builtin_match {
338+ if let Some ( b) = hooks:: detect_builtin ( & name, & content) {
344339 installed. insert ( b. name . to_string ( ) ) ;
345340 }
346341 }
@@ -416,10 +411,11 @@ mod tests {
416411 use super :: * ;
417412
418413 #[ test]
419- fn get_configured_keys_works ( ) {
414+ fn get_configured_keys_only_returns_known_option_keys ( ) {
420415 let configured = get_configured_keys ( ) ;
421- // Just verify it doesn't panic and returns a valid HashSet
422- assert ! ( configured. len( ) >= 0 ) ;
416+ for key in & configured {
417+ assert ! ( config:: CONFIG_OPTIONS . iter( ) . any( |o| o. key == key) ) ;
418+ }
423419 }
424420
425421 #[ test]
0 commit comments