Installs pre-commit hooks for OCaml projects that run dune fmt automatically
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-precommit: read global git config for user email filtering

When checking for AI attribution, precommit now reads ~/.gitconfig
as a fallback when the local repo doesn't have user.email configured.
This ensures only the current user's commits are flagged, not commits
from other verse members.

Renamed parameters for clarity: ~cwd for working directory, ~fs for
filesystem root (needed for absolute paths like ~/.gitconfig).

+68 -41
+19 -21
bin/main.ml
··· 231 231 232 232 (* Shared: find AI commits across dirs and display a unified table. 233 233 Returns [(affected_dirs, total_commits, repos_with_issues)]. *) 234 - let find_and_display_ai_commits ~fs dirs = 234 + let find_and_display_ai_commits ~cwd ~fs dirs = 235 235 let term_width = get_terminal_width () in 236 236 let subject_max = max 20 (term_width - 35) in 237 237 let total_commits = ref 0 in ··· 240 240 let affected_dirs = ref [] in 241 241 List.iter 242 242 (fun d -> 243 - let commits = Precommit.check_ai_attribution ~fs d in 243 + let commits = Precommit.check_ai_attribution ~cwd ~fs d in 244 244 if commits <> [] then begin 245 245 incr repos_with_issues; 246 246 total_commits := !total_commits + List.length commits; ··· 283 283 end; 284 284 (List.rev !affected_dirs, !total_commits, !repos_with_issues) 285 285 286 - let check_impl ~fs dirs = 287 - let dirs = collect_dirs ~fs dirs in 286 + let check_impl ~cwd ~fs dirs = 287 + let dirs = collect_dirs ~fs:cwd dirs in 288 288 let _affected, total_commits, repos_with_issues = 289 - find_and_display_ai_commits ~fs dirs 289 + find_and_display_ai_commits ~cwd ~fs dirs 290 290 in 291 291 if total_commits > 0 then begin 292 292 error "%d commit%s with AI attribution in %d repo%s" total_commits ··· 299 299 300 300 let check chdir dirs = 301 301 Eio_main.run @@ fun env -> 302 - let fs = 303 - match chdir with 304 - | None -> Eio.Stdenv.cwd env 305 - | Some d -> Eio.Path.(Eio.Stdenv.fs env / d) 302 + let fs = Eio.Stdenv.fs env in 303 + let cwd = 304 + match chdir with None -> Eio.Stdenv.cwd env | Some d -> Eio.Path.(fs / d) 306 305 in 307 - check_impl ~fs dirs 306 + check_impl ~cwd ~fs dirs 308 307 309 308 let check_cmd = 310 309 let doc = "Check git history for commits with AI attribution." in ··· 349 348 let answer = String.trim line in 350 349 answer = "y" || answer = "Y" 351 350 352 - let fix_impl ~fs dry_run yes dirs = 353 - let dirs = collect_dirs ~fs dirs in 351 + let fix_impl ~cwd ~fs dry_run yes dirs = 352 + let dirs = collect_dirs ~fs:cwd dirs in 354 353 let affected, total_commits, repos_with_issues = 355 - find_and_display_ai_commits ~fs dirs 354 + find_and_display_ai_commits ~cwd ~fs dirs 356 355 in 357 356 if total_commits = 0 then success "No AI attribution found in commit history" 358 357 else if dry_run then begin ··· 362 361 (if repos_with_issues = 1 then "" else "s"); 363 362 List.iter 364 363 (fun d -> 365 - let branch = Precommit.current_branch ~fs d in 364 + let branch = Precommit.current_branch ~fs:cwd d in 366 365 let name = Option.value ~default:"HEAD" branch in 367 366 info "Would backup %s:%s before rewriting" d name) 368 367 affected ··· 378 377 Eio.Fiber.all 379 378 (List.map 380 379 (fun d () -> 381 - let backup = Precommit.backup_branch ~fs d in 380 + let backup = Precommit.backup_branch ~fs:cwd d in 382 381 success "%s: backed up to %s" d backup; 383 - match Precommit.rewrite_ai_attribution ~fs d with 382 + match Precommit.rewrite_ai_attribution ~cwd ~fs d with 384 383 | Ok _ -> 385 384 Atomic.incr fixed; 386 385 success "%s: attribution removed" d ··· 407 406 408 407 let fix chdir dry_run yes dirs = 409 408 Eio_main.run @@ fun env -> 410 - let fs = 411 - match chdir with 412 - | None -> Eio.Stdenv.cwd env 413 - | Some d -> Eio.Path.(Eio.Stdenv.fs env / d) 409 + let fs = Eio.Stdenv.fs env in 410 + let cwd = 411 + match chdir with None -> Eio.Stdenv.cwd env | Some d -> Eio.Path.(fs / d) 414 412 in 415 - fix_impl ~fs dry_run yes dirs 413 + fix_impl ~cwd ~fs dry_run yes dirs 416 414 417 415 let fix_cmd = 418 416 let doc = "Remove AI attribution from commit history." in
+33 -13
lib/precommit.ml
··· 232 232 let filter_message msg = Re.replace_string ai_pattern ~by:"" msg 233 233 let message_has_ai_attribution msg = Re.execp ai_pattern msg 234 234 235 - (* Get current user's email from git config *) 236 - let get_user_email repo = 237 - match Git.Repository.read_config repo with 238 - | Some config -> (Git.Config.get_user config).email 239 - | None -> None 235 + (* Get current user's email from git config (local or global) *) 236 + let get_user_email ~fs repo = 237 + (* First check local repo config *) 238 + let local_email = 239 + match Git.Repository.read_config repo with 240 + | Some config -> (Git.Config.get_user config).email 241 + | None -> None 242 + in 243 + match local_email with 244 + | Some _ -> local_email 245 + | None -> ( 246 + (* Fall back to global config ~/.gitconfig *) 247 + let home = 248 + try Sys.getenv "HOME" 249 + with Not_found -> ( 250 + try Sys.getenv "USERPROFILE" with Not_found -> "") 251 + in 252 + if home = "" then None 253 + else 254 + let global_config_path = Filename.concat home ".gitconfig" in 255 + try 256 + let content = Eio.Path.load Eio.Path.(fs / global_config_path) in 257 + let config = Git.Config.of_string content in 258 + (Git.Config.get_user config).email 259 + with _ -> None) 240 260 241 261 (* Check if commit was made by the current user *) 242 262 let is_my_commit ~user_email commit = ··· 246 266 let committer_email = Git.User.email (Git.Commit.committer commit) in 247 267 String.equal email committer_email 248 268 249 - let check_ai_attribution ~fs dir = 250 - if not (file_exists ~fs (Filename.concat dir ".git")) then [] 269 + let check_ai_attribution ~cwd ~fs dir = 270 + if not (file_exists ~fs:cwd (Filename.concat dir ".git")) then [] 251 271 else 252 - let repo = Git.Repository.open_repo ~fs (Fpath.v dir) in 253 - let user_email = get_user_email repo in 272 + let repo = Git.Repository.open_repo ~fs:cwd (Fpath.v dir) in 273 + let user_email = get_user_email ~fs repo in 254 274 match Git.Repository.head repo with 255 275 | None -> [] 256 276 | Some head_hash -> ··· 304 324 backup_name 305 325 | None -> backup_name 306 326 307 - let rewrite_ai_attribution ~fs dir = 308 - if not (file_exists ~fs (Filename.concat dir ".git")) then 327 + let rewrite_ai_attribution ~cwd ~fs dir = 328 + if not (file_exists ~fs:cwd (Filename.concat dir ".git")) then 309 329 Error (Printf.sprintf "%s: No .git directory found" dir) 310 330 else 311 - let repo = Git.Repository.open_repo ~fs (Fpath.v dir) in 312 - let user_email = get_user_email repo in 331 + let repo = Git.Repository.open_repo ~fs:cwd (Fpath.v dir) in 332 + let user_email = get_user_email ~fs repo in 313 333 match Git.Repository.head repo with 314 334 | None -> Ok 0 315 335 | Some head_hash ->
+16 -7
lib/precommit.mli
··· 101 101 (** A commit with AI attribution. *) 102 102 103 103 val check_ai_attribution : 104 - fs:Eio.Fs.dir_ty Eio.Path.t -> string -> ai_commit list 105 - (** [check_ai_attribution ~fs dir] uses ocaml-git to find commits that contain 106 - AI attribution patterns in the commit message. *) 104 + cwd:Eio.Fs.dir_ty Eio.Path.t -> 105 + fs:Eio.Fs.dir_ty Eio.Path.t -> 106 + string -> 107 + ai_commit list 108 + (** [check_ai_attribution ~cwd ~fs dir] uses ocaml-git to find commits that 109 + contain AI attribution patterns in the commit message. Only checks commits 110 + authored by the current user (determined from git config). [fs] is used to 111 + read the global git config if the local repo doesn't have user settings. *) 107 112 108 113 (** {1 History Rewriting} *) 109 114 ··· 117 122 ocaml-git. *) 118 123 119 124 val rewrite_ai_attribution : 120 - fs:Eio.Fs.dir_ty Eio.Path.t -> string -> (int, string) result 121 - (** [rewrite_ai_attribution ~fs dir] uses ocaml-git to rewrite commits and 122 - remove [Co-Authored-By:.*claude] lines from commit messages. Returns [Ok n] 123 - where [n] is the number of commits rewritten, or [Error msg] on failure. *) 125 + cwd:Eio.Fs.dir_ty Eio.Path.t -> 126 + fs:Eio.Fs.dir_ty Eio.Path.t -> 127 + string -> 128 + (int, string) result 129 + (** [rewrite_ai_attribution ~cwd ~fs dir] uses ocaml-git to rewrite commits and 130 + remove [Co-Authored-By:.*claude] lines from commit messages. Only rewrites 131 + commits authored by the current user. Returns [Ok n] where [n] is the number 132 + of commits rewritten, or [Error msg] on failure. *)