From 42b576d2850c346ea4283db2d78acbde10c1f1e7 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 29 Apr 2026 00:06:48 +0000 Subject: [PATCH] huskies: merge 817 --- server/src/chat/commands/loc.rs | 71 ++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/server/src/chat/commands/loc.rs b/server/src/chat/commands/loc.rs index 59fe7000..502f7959 100644 --- a/server/src/chat/commands/loc.rs +++ b/server/src/chat/commands/loc.rs @@ -17,8 +17,8 @@ const SKIP_DIRS: &[&str] = &[ "test-results", ]; -/// Path components that indicate a worktree path that should be skipped. -const SKIP_PATH_COMPONENTS: &[&str] = &[".huskies/worktrees"]; +/// Path components that indicate a worktree or merge-workspace path that should be skipped. +const SKIP_PATH_COMPONENTS: &[&str] = &[".huskies/worktrees", ".huskies/merge_workspace"]; /// Known-huge or machine-generated files that are excluded from the loc count /// even when they have a recognised source extension (e.g. `.json`, `.yaml`). @@ -37,6 +37,7 @@ const EXCLUDED_FILENAMES: &[&str] = &[ "flake.lock", ]; +/// Handle the `/loc` chat command, returning a lines-of-code report for the project. pub(super) fn handle_loc(ctx: &CommandContext) -> Option { let args = ctx.args.trim(); @@ -380,6 +381,72 @@ mod tests { ); } + #[test] + fn loc_skips_merge_workspace_directory() { + let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap_or(std::path::Path::new(".")); + let services = + crate::services::Services::new_test(repo_root.to_path_buf(), "Timmy".to_string()); + let ctx = make_ctx(&services, repo_root, ""); + let output = handle_loc(&ctx).unwrap(); + assert!( + !output.contains(".huskies/merge_workspace"), + "output must not include paths inside merge_workspace: {output}" + ); + } + + #[test] + fn loc_counts_canonical_file_once_when_also_in_worktree_copy() { + use std::io::Write as _; + let dir = tempfile::tempdir().expect("tempdir"); + + // Canonical source file at the project root. + let canonical = dir.path().join("lib.rs"); + { + let mut f = std::fs::File::create(&canonical).unwrap(); + for i in 0..30 { + writeln!(f, "fn canonical_{i}() {{}}").unwrap(); + } + } + + // Duplicate of the same file inside a simulated worktree path. + let worktree_dir = dir.path().join(".huskies").join("worktrees").join("42"); + std::fs::create_dir_all(&worktree_dir).unwrap(); + let worktree_copy = worktree_dir.join("lib.rs"); + std::fs::copy(&canonical, &worktree_copy).unwrap(); + + // Duplicate inside merge_workspace. + let merge_dir = dir + .path() + .join(".huskies") + .join("merge_workspace") + .join("42"); + std::fs::create_dir_all(&merge_dir).unwrap(); + let merge_copy = merge_dir.join("lib.rs"); + std::fs::copy(&canonical, &merge_copy).unwrap(); + + let services = + crate::services::Services::new_test(dir.path().to_path_buf(), "Timmy".to_string()); + let ctx = make_ctx(&services, dir.path(), "50"); + let output = handle_loc(&ctx).unwrap(); + + // lib.rs should appear exactly once (from the canonical path). + let occurrences = output.matches("lib.rs").count(); + assert_eq!( + occurrences, 1, + "lib.rs must be counted exactly once, not duplicated from worktree/merge copies: {output}" + ); + assert!( + !output.contains("worktrees"), + "output must not include paths from .huskies/worktrees: {output}" + ); + assert!( + !output.contains("merge_workspace"), + "output must not include paths from .huskies/merge_workspace: {output}" + ); + } + #[test] fn loc_skips_target_directory() { use std::io::Write as _;