1use anyhow::{Context, Result};
2use clap::{Args, Subcommand};
3use std::fs;
4use std::path::Path;
5use std::process::Command;
6
7#[derive(Args, Debug)]
8pub struct HookArgs {
9 #[command(subcommand)]
10 pub action: HookAction,
11}
12
13#[derive(Subcommand, Debug)]
14pub enum HookAction {
15 Install,
17 Uninstall,
19}
20
21pub fn run(args: &HookArgs) -> Result<()> {
22 let root_out = Command::new("git")
23 .args(["rev-parse", "--show-toplevel"])
24 .output()
25 .context("Failed to check git repository")?;
26
27 if !root_out.status.success() {
28 anyhow::bail!("Not inside a Git repository.");
29 }
30
31 let repo_root = String::from_utf8_lossy(&root_out.stdout).trim().to_string();
32 let hooks_dir = Path::new(&repo_root).join(".git").join("hooks");
33
34 let post_merge = hooks_dir.join("post-merge");
35 let pre_push = hooks_dir.join("pre-push");
36
37 match args.action {
38 HookAction::Install => {
39 fs::create_dir_all(&hooks_dir)?;
40 let pm_script = "#!/bin/sh\n# git-notes auto-sync hook\ngit-notes sync pull --quiet 2>/dev/null || true\n";
41 let pp_script = "#!/bin/sh\n# git-notes auto-sync hook\ngit-notes sync push --quiet 2>/dev/null || true\n";
42
43 fs::write(&post_merge, pm_script)?;
44 fs::write(&pre_push, pp_script)?;
45
46 #[cfg(unix)]
47 {
48 use std::os::unix::fs::PermissionsExt;
49 let mut perms = fs::metadata(&post_merge)?.permissions();
50 perms.set_mode(0o755);
51 fs::set_permissions(&post_merge, perms)?;
52
53 let mut perms2 = fs::metadata(&pre_push)?.permissions();
54 perms2.set_mode(0o755);
55 fs::set_permissions(&pre_push, perms2)?;
56 }
57
58 println!("\x1b[32m✔\x1b[0m Successfully installed auto-sync hooks in .git/hooks/");
59 }
60 HookAction::Uninstall => {
61 if post_merge.exists() {
62 let content = fs::read_to_string(&post_merge).unwrap_or_default();
63 if content.contains("git-notes") {
64 fs::remove_file(&post_merge)?;
65 }
66 }
67 if pre_push.exists() {
68 let content = fs::read_to_string(&pre_push).unwrap_or_default();
69 if content.contains("git-notes") {
70 fs::remove_file(&pre_push)?;
71 }
72 }
73 println!("\x1b[32m✔\x1b[0m Removed git-notes hooks from .git/hooks/");
74 }
75 }
76
77 Ok(())
78}