Skip to main content

git_side/commands/
auto.rs

1use colored::Colorize;
2
3use crate::error::{Error, Result};
4use crate::git;
5use crate::side_repo::SideRepo;
6use crate::tracked::TrackedPaths;
7
8/// Auto-commit: sync all tracked paths using the last main repo commit message.
9///
10/// # Errors
11///
12/// Returns an error if no paths are tracked, staging fails, or commit fails.
13pub fn run() -> Result<()> {
14    let repo = SideRepo::open()?;
15
16    if !repo.is_initialized() {
17        return Err(Error::NoTrackedPaths);
18    }
19
20    // Load tracked paths
21    let tracked = TrackedPaths::load(&repo)?;
22
23    if tracked.is_empty() {
24        return Err(Error::NoTrackedPaths);
25    }
26
27    // Expand directories to files
28    let files = tracked.expand(&repo.work_tree);
29
30    // Get the raw tracked paths for staging (we stage the tracked paths, not expanded files)
31    let tracked_paths: Vec<_> = tracked.paths().iter().cloned().collect();
32
33    // Two-pass staging:
34    // Pass 1: update tracked files (modifications + deletions) — errors ignored
35    repo.stage_update(&tracked_paths);
36
37    // Pass 2: add new files
38    repo.stage_new(&tracked_paths)?;
39
40    // Stage .side-tracked file itself (self-aware versioning)
41    repo.stage_tracked_file()?;
42
43    // Get last commit message from main repo
44    let message = git::last_commit_message()?;
45
46    if message.trim().is_empty() {
47        return Err(Error::GitCommandFailed(
48            "no commit message found in main repo".to_string(),
49        ));
50    }
51
52    let prefix = "[git-side]".dimmed();
53
54    // Commit (will return NothingToCommit if nothing changed)
55    match repo.commit(&message) {
56        Ok(()) => {
57            println!(
58                "{} {} {}",
59                prefix,
60                "Auto-committed:".green().bold(),
61                message.lines().next().unwrap_or(&message)
62            );
63            println!(
64                "{} {} file(s) synced",
65                prefix,
66                files.len().to_string().cyan()
67            );
68            // Try to push — silently ignored if no remote
69            if repo.push().is_ok() {
70                println!("{} {}", prefix, "Pushed to remote.".green().bold());
71            }
72        }
73        Err(Error::NothingToCommit) => {
74            println!("{} {}", prefix, "Nothing to commit (side repo is up to date).".yellow());
75        }
76        Err(e) => return Err(e),
77    }
78
79    Ok(())
80}