git_quick_add/git/
stage.rs

1use crate::models::path_items::PathItems;
2use git2::Repository;
3use std::path::Path;
4
5/// Stages the selected files in the git repository.
6/// If staging fails, the program exits.
7/// # Arguments
8/// * `repo` - A reference to the git repository.
9/// * `paths` - A vector of file paths to stage.
10pub fn git_add_selected(repo: &Repository, paths: &Vec<PathItems>) -> Result<(), git2::Error> {
11    let mut index = repo.index()?;
12
13    println!("{}", console::style("Changes Made:").bold());
14
15    let mut logs = vec![];
16
17    for item in paths {
18        // if the item is staged and not selected, we need to unstage it
19        if item.is_staged && !item.is_selected {
20            let target = repo.head()?.peel(git2::ObjectType::Commit)?;
21            repo.reset_default(Some(&target), &[&item.path])?;
22
23            logs.push(format!(
24                " - {} {}",
25                console::style("Unstaged:").yellow(),
26                item.path.clone()
27            ));
28        // if the item is not staged and selected, we need to stage it
29        } else if !item.is_staged && item.is_selected {
30            let p = Path::new(&item.path);
31
32            index.add_path(p).unwrap_or_else(|error| {
33                eprintln!(
34                    "{} {} - {}",
35                    console::style("Failed to add path for").red(),
36                    console::style(&item.path).yellow(),
37                    error
38                );
39            });
40
41            index.write().unwrap_or_else(|error| {
42                eprintln!(
43                    "{} {} - {}",
44                    console::style("Failed to write index for").red(),
45                    console::style(&item.path).yellow(),
46                    error
47                );
48            });
49
50            logs.push(format!(
51                " - {} {}",
52                console::style("Staged:").green(),
53                item.path
54            ));
55        } else {
56            if item.is_staged {
57                logs.push(format!(
58                    " - {} {}",
59                    console::style("Staged:").green(),
60                    item.path.clone()
61                ));
62            } else {
63                logs.push(format!(
64                    " - {} {}",
65                    console::style("Unstaged:").yellow(),
66                    item.path.clone()
67                ));
68            }
69        }
70    }
71
72    println!("{}", logs.join("\n"));
73
74    Ok(())
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use std::fs::File;
81    use std::io::Write;
82    use tempfile::TempDir;
83
84    /// Helper to initialize a new git repository in a temp dir
85    fn init_repo() -> (TempDir, Repository) {
86        let tmp_dir = TempDir::new().expect("create temp dir");
87        let repo = Repository::init(tmp_dir.path()).expect("init repo");
88        (tmp_dir, repo)
89    }
90
91    #[test]
92    fn test_git_add_selected() {
93        let (_tmp, repo) = init_repo();
94
95        // Create a test file
96        let file_path = "test.txt";
97        let file_full_path = repo.workdir().unwrap().join(file_path);
98        let mut file = File::create(&file_full_path).unwrap();
99        writeln!(file, "test content").unwrap();
100
101        let paths = vec![PathItems {
102            path: String::from(file_path),
103            is_staged: false,
104            is_selected: true,
105        }];
106
107        git_add_selected(&repo, &paths).unwrap();
108
109        // Verify the file is staged
110        let statuses = repo.statuses(None).unwrap();
111        for status in statuses.iter() {
112            assert!(status.status().is_index_new());
113        }
114    }
115}