Skip to main content

lattice_vcs/
stash.rs

1use crate::{Repository, Result, VcsError};
2
3/// Stash operations — list, apply, pop, drop, create.
4///
5/// Uses the git CLI.
6pub struct Stash;
7
8/// A single stash entry.
9#[derive(Debug, Clone)]
10pub struct StashEntry {
11    /// Zero-based index in the stash list (0 is the most recent).
12    pub index: usize,
13    /// The stash message (first line of the stash commit message).
14    pub message: String,
15}
16
17impl Stash {
18    /// List all stash entries, newest first.
19    ///
20    /// Equivalent to `git stash list`.
21    pub fn list(repo: &Repository) -> Result<Vec<StashEntry>> {
22        let lines = repo.run_git_str(["stash", "list"])?;
23        let mut entries = Vec::new();
24        for (i, line) in lines.lines().enumerate() {
25            if line.trim().is_empty() {
26                continue;
27            }
28            entries.push(StashEntry {
29                index: i,
30                message: line.trim().to_string(),
31            });
32        }
33        Ok(entries)
34    }
35
36    /// Apply a stash entry without removing it from the stash list.
37    ///
38    /// Equivalent to `git stash apply stash@{<index>}`.
39    pub fn apply(repo: &Repository, index: usize) -> Result<()> {
40        let refspec = format!("stash@{{{}}}", index);
41        repo.run_git(["stash", "apply", &refspec])
42            .map(|_| ())
43            .map_err(|e| VcsError::Stash(format!("stash apply {}: {}", index, e)))
44    }
45
46    /// Apply a stash entry and remove it from the stash list.
47    ///
48    /// Equivalent to `git stash pop stash@{<index>}`.
49    pub fn pop(repo: &Repository, index: usize) -> Result<()> {
50        let refspec = format!("stash@{{{}}}", index);
51        repo.run_git(["stash", "pop", &refspec])
52            .map(|_| ())
53            .map_err(|e| VcsError::Stash(format!("stash pop {}: {}", index, e)))
54    }
55
56    /// Drop a stash entry without applying it.
57    ///
58    /// Equivalent to `git stash drop stash@{<index>}`.
59    pub fn drop(repo: &Repository, index: usize) -> Result<()> {
60        let refspec = format!("stash@{{{}}}", index);
61        repo.run_git(["stash", "drop", &refspec])
62            .map(|_| ())
63            .map_err(|e| VcsError::Stash(format!("stash drop {}: {}", index, e)))
64    }
65
66    /// Create a new stash from the current working tree state.
67    ///
68    /// If `include_untracked` is `true`, untracked files are also stashed.
69    /// If `message` is provided, it is used as the stash message.
70    ///
71    /// Equivalent to `git stash push [-u] [-m <message>]`.
72    pub fn create(repo: &Repository, message: Option<&str>, include_untracked: bool) -> Result<()> {
73        let mut args: Vec<String> = vec!["stash".into(), "push".into()];
74        if include_untracked {
75            args.push("--include-untracked".into());
76        }
77        if let Some(msg) = message {
78            args.push("-m".into());
79            args.push(msg.into());
80        }
81        let refs: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
82        repo.run_git(refs)
83            .map(|_| ())
84            .map_err(|e| VcsError::Stash(format!("stash create: {}", e)))
85    }
86}