Skip to main content

gn_cli/commands/
init.rs

1use anyhow::{Context, Result};
2use std::fs;
3use std::path::Path;
4use std::process::Command;
5
6pub fn run() -> Result<()> {
7    println!("\n\x1b[1;36m🚀 Initializing git-notes in repository...\x1b[0m\n");
8
9    // 1. Verify git repo
10    let root_out = Command::new("git")
11        .args(["rev-parse", "--show-toplevel"])
12        .output()
13        .context("Failed to check git repository")?;
14
15    if !root_out.status.success() {
16        anyhow::bail!("Not inside a Git repository. Run 'git init' first.");
17    }
18
19    let repo_root = String::from_utf8_lossy(&root_out.stdout).trim().to_string();
20
21    // 2. Configure remote origin fetch refspec for refs/notes/*
22    let remote_check = Command::new("git").args(["remote"]).output()?;
23    let remotes = String::from_utf8_lossy(&remote_check.stdout);
24
25    if remotes.lines().any(|r| r.trim() == "origin") {
26        let fetch_check = Command::new("git")
27            .args(["config", "--get-all", "remote.origin.fetch"])
28            .output()?;
29        let current_fetches = String::from_utf8_lossy(&fetch_check.stdout);
30
31        if !current_fetches.contains("refs/notes/*") {
32            Command::new("git")
33                .args(["config", "--add", "remote.origin.fetch", "+refs/notes/*:refs/notes/*"])
34                .status()?;
35            println!("  \x1b[32m✔\x1b[0m Configured remote.origin.fetch for refs/notes/*");
36        } else {
37            println!("  \x1b[32m✔\x1b[0m remote.origin.fetch already configured");
38        }
39    } else {
40        println!("  \x1b[33mℹ\x1b[0m No remote 'origin' found. Skipping refspec config for now.");
41    }
42
43    // 3. Install native git hooks (post-merge & pre-push)
44    let hooks_dir = Path::new(&repo_root).join(".git").join("hooks");
45    fs::create_dir_all(&hooks_dir)?;
46
47    let post_merge_path = hooks_dir.join("post-merge");
48    let pre_push_path = hooks_dir.join("pre-push");
49
50    let post_merge_script = "#!/bin/sh\n# git-notes auto-sync hook\ngit-notes sync pull --quiet 2>/dev/null || true\n";
51    let pre_push_script = "#!/bin/sh\n# git-notes auto-sync hook\ngit-notes sync push --quiet 2>/dev/null || true\n";
52
53    fs::write(&post_merge_path, post_merge_script)?;
54    fs::write(&pre_push_path, pre_push_script)?;
55
56    #[cfg(unix)]
57    {
58        use std::os::unix::fs::PermissionsExt;
59        let mut perms = fs::metadata(&post_merge_path)?.permissions();
60        perms.set_mode(0o755);
61        fs::set_permissions(&post_merge_path, perms)?;
62
63        let mut perms2 = fs::metadata(&pre_push_path)?.permissions();
64        perms2.set_mode(0o755);
65        fs::set_permissions(&pre_push_path, perms2)?;
66    }
67
68    println!("  \x1b[32m✔\x1b[0m Installed git auto-sync hooks (.git/hooks/post-merge & pre-push)");
69
70    println!("\n\x1b[32m✨ git-notes initialized successfully!\x1b[0m");
71    println!("Try running: \x1b[36mgn l\x1b[0m or \x1b[36mgn a -f <file> -l <line> -m \"...\"\x1b[0m\n");
72
73    Ok(())
74}