diff --git a/src/cmds/git/git.rs b/src/cmds/git/git.rs index 9936a061d8..ff164372ac 100644 --- a/src/cmds/git/git.rs +++ b/src/cmds/git/git.rs @@ -436,6 +436,43 @@ fn run_log( let mut cmd = git_cmd(global_args); cmd.arg("log"); + let (log_args, limit, user_set_limit, has_format_flag) = build_log_args(args); + cmd.args(log_args); + + let result = exec_capture(&mut cmd).context("Failed to run git log")?; + + if !result.success() { + eprintln!("{}", result.stderr); + return Ok(result.exit_code); + } + + if verbose > 0 { + eprintln!("Git log output:"); + } + + // Post-process: truncate long messages, cap lines only if RTK set the default + let filtered = filter_log_output(&result.stdout, limit, user_set_limit, has_format_flag); + let filtered = never_worse(&result.stdout, &filtered).to_string(); + println!("{}", filtered); + + timer.track( + &format!("git log {}", args.join(" ")), + &format!("rtk git log {}", args.join(" ")), + &result.stdout, + &filtered, + ); + + Ok(0) +} + +/// Build the arguments that RTK adds around the user's git log arguments. +/// +/// Merge commits are deliberately left in the history. They carry topology and +/// often identify the repository's actual integration state, so silently adding +/// --no-merges would make the compact output semantically incorrect. +fn build_log_args(args: &[String]) -> (Vec, usize, bool, bool) { + let mut log_args = Vec::new(); + // Check if user provided format flags let has_format_flag = args.iter().any(|arg| { arg.starts_with("--oneline") || arg.starts_with("--pretty") || arg.starts_with("--format") @@ -452,7 +489,7 @@ fn run_log( // Use %b (body) to preserve first line of commit body for agent context // (BREAKING CHANGE, Closes #xxx, design notes) if !has_format_flag { - cmd.args(["--pretty=format:%h %s (%ar) <%an>%n%b%n---END---"]); + log_args.push("--pretty=format:%h %s (%ar) <%an>%n%b%n---END---".to_string()); } // Determine limit: respect user's explicit -N flag, use sensible defaults otherwise @@ -462,52 +499,18 @@ fn run_log( (n, true) } else if has_format_flag { // --oneline / --pretty without -N: user wants compact output, allow more - cmd.arg("-50"); + log_args.push("-50".to_string()); (50, false) } else { // No flags at all: default to 10 - cmd.arg("-10"); + log_args.push("-10".to_string()); (10, false) }; - // Only add --no-merges if user didn't explicitly request merge commits - let wants_merges = args - .iter() - .any(|arg| arg == "--merges" || arg == "--min-parents=2" || arg == "--no-merges"); - // Don't add --no-merges if user explicitly requested merges or an exact count (-n N / --max-count) - if !wants_merges && !has_limit_flag { - cmd.arg("--no-merges"); - } - // Pass all user arguments - for arg in args { - cmd.arg(arg); - } - - let result = exec_capture(&mut cmd).context("Failed to run git log")?; - - if !result.success() { - eprintln!("{}", result.stderr); - return Ok(result.exit_code); - } - - if verbose > 0 { - eprintln!("Git log output:"); - } - - // Post-process: truncate long messages, cap lines only if RTK set the default - let filtered = filter_log_output(&result.stdout, limit, user_set_limit, has_format_flag); - let filtered = never_worse(&result.stdout, &filtered).to_string(); - println!("{}", filtered); + log_args.extend(args.iter().cloned()); - timer.track( - &format!("git log {}", args.join(" ")), - &format!("rtk git log {}", args.join(" ")), - &result.stdout, - &filtered, - ); - - Ok(0) + (log_args, limit, user_set_limit, has_format_flag) } /// Filter git log output: truncate long messages, cap lines @@ -2731,6 +2734,23 @@ A added.rs assert_eq!(parse_user_limit(&args), None); } + #[test] + fn test_build_log_args_preserves_merge_commits() { + let (args, limit, user_set_limit, user_format) = build_log_args(&[]); + + assert_eq!(limit, 10); + assert!(!user_set_limit); + assert!(!user_format); + assert!( + !args.iter().any(|arg| arg == "--no-merges"), + "default git log must not hide merge commits" + ); + assert!( + args.iter().any(|arg| arg.starts_with("--pretty=format:")), + "default git log should still use RTK's compact format" + ); + } + #[test] fn test_filter_log_output_token_savings() { fn count_tokens(text: &str) -> usize {