Skip to main content

ralph_workflow/git_helpers/repo/
snapshot.rs

1use std::io;
2
3use crate::git_helpers::git2_to_io_error;
4
5/// Get a snapshot of the current git status.
6///
7/// Returns status in porcelain format (similar to `git status --porcelain=v1`).
8pub fn git_snapshot() -> io::Result<String> {
9    let repo = git2::Repository::discover(".").map_err(|e| git2_to_io_error(&e))?;
10    git_snapshot_impl(&repo)
11}
12
13/// Implementation of git snapshot.
14fn git_snapshot_impl(repo: &git2::Repository) -> io::Result<String> {
15    let mut opts = git2::StatusOptions::new();
16    opts.include_untracked(true).recurse_untracked_dirs(true);
17    let statuses = repo
18        .statuses(Some(&mut opts))
19        .map_err(|e| git2_to_io_error(&e))?;
20
21    let mut result = String::new();
22    for entry in statuses.iter() {
23        let status = entry.status();
24        let path = entry.path().unwrap_or("").to_string();
25
26        // Convert git2 status to porcelain format.
27        // Untracked files are represented as "??" in porcelain v1.
28        if status.contains(git2::Status::WT_NEW) {
29            result.push('?');
30            result.push('?');
31            result.push(' ');
32            result.push_str(&path);
33            result.push('\n');
34            continue;
35        }
36
37        // Index status
38        let index_status = if status.contains(git2::Status::INDEX_NEW) {
39            'A'
40        } else if status.contains(git2::Status::INDEX_MODIFIED) {
41            'M'
42        } else if status.contains(git2::Status::INDEX_DELETED) {
43            'D'
44        } else if status.contains(git2::Status::INDEX_RENAMED) {
45            'R'
46        } else if status.contains(git2::Status::INDEX_TYPECHANGE) {
47            'T'
48        } else {
49            ' '
50        };
51
52        // Worktree status
53        let wt_status = if status.contains(git2::Status::WT_MODIFIED) {
54            'M'
55        } else if status.contains(git2::Status::WT_DELETED) {
56            'D'
57        } else if status.contains(git2::Status::WT_RENAMED) {
58            'R'
59        } else if status.contains(git2::Status::WT_TYPECHANGE) {
60            'T'
61        } else {
62            ' '
63        };
64
65        result.push(index_status);
66        result.push(wt_status);
67        result.push(' ');
68        result.push_str(&path);
69        result.push('\n');
70    }
71
72    Ok(result)
73}