Skip to main content

gitkraft_gui/features/commits/
commands.rs

1//! Async command helpers for commit operations.
2//!
3//! Each function spawns blocking work on a background thread via the
4//! `git_task!` macro, performs the git operation, and maps the result into a
5//! [`Message`].
6
7use std::path::PathBuf;
8
9use iced::Task;
10
11use crate::macros::StringErr;
12use crate::message::Message;
13
14/// Load just the file list (paths + statuses) for a commit — no line parsing.
15pub fn load_commit_file_list(path: PathBuf, oid: String) -> Task<Message> {
16    git_task!(
17        Message::CommitFileListLoaded,
18        (|| {
19            let repo = open_repo!(&path);
20            gitkraft_core::features::diff::get_commit_file_list(&repo, &oid).str_err()
21        })()
22    )
23}
24
25/// Load the full diff for a single file in a commit.
26pub fn load_single_file_diff(path: PathBuf, oid: String, file_path: String) -> Task<Message> {
27    git_task!(
28        Message::SingleFileDiffLoaded,
29        (|| {
30            let repo = open_repo!(&path);
31            gitkraft_core::features::diff::get_single_file_diff(&repo, &oid, &file_path).str_err()
32        })()
33    )
34}
35
36/// Search commits by query string (searches message, author, SHA).
37pub fn search_commits(path: PathBuf, query: String) -> Task<Message> {
38    git_task!(
39        Message::SearchResultsLoaded,
40        (|| {
41            let repo = open_repo!(&path);
42            gitkraft_core::features::log::search_commits(&repo, &query, 100).str_err()
43        })()
44    )
45}
46
47/// Load the file list of changes between a commit and the current working tree.
48pub fn search_diff_file_list(path: PathBuf, oid: String) -> Task<Message> {
49    git_task!(
50        Message::SearchDiffFilesLoaded,
51        (|| {
52            let repo = open_repo!(&path);
53            gitkraft_core::features::diff::file_list_commit_vs_workdir(&repo, &oid).str_err()
54        })()
55    )
56}
57
58/// Diff a single file from a specific commit against the current working tree (for search overlay).
59pub fn search_diff_file(path: PathBuf, oid: String, file_path: String) -> Task<Message> {
60    git_task!(
61        Message::SearchFileDiffLoaded,
62        (|| {
63            let repo = open_repo!(&path);
64            gitkraft_core::features::diff::diff_file_commit_vs_workdir(&repo, &oid, &file_path)
65                .str_err()
66        })()
67    )
68}
69
70/// Diff multiple files from a specific commit against the current working tree.
71pub fn search_diff_multi_files(
72    path: PathBuf,
73    oid: String,
74    file_paths: Vec<String>,
75) -> Task<Message> {
76    git_task!(
77        Message::SearchMultiDiffLoaded,
78        (|| {
79            let repo = open_repo!(&path);
80            let mut diffs = Vec::with_capacity(file_paths.len());
81            for fp in &file_paths {
82                match gitkraft_core::features::diff::diff_file_commit_vs_workdir(&repo, &oid, fp) {
83                    Ok(diff) => diffs.push(diff),
84                    Err(e) => {
85                        return Err(format!("{fp}: {e}"));
86                    }
87                }
88            }
89            Ok(diffs)
90        })()
91    )
92}
93
94/// Diff a file from a specific commit against the current working tree.
95pub fn diff_file_with_working_tree(path: PathBuf, oid: String, file_path: String) -> Task<Message> {
96    git_task!(
97        Message::DiffWithWorkingTreeLoaded,
98        (|| {
99            let repo = open_repo!(&path);
100            gitkraft_core::features::diff::diff_file_commit_vs_workdir(&repo, &oid, &file_path)
101                .str_err()
102        })()
103    )
104}
105
106/// Load diffs for multiple files in a commit and return them all together.
107pub fn load_commit_multi_diffs(
108    path: PathBuf,
109    oid: String,
110    file_paths: Vec<String>,
111) -> Task<Message> {
112    git_task!(
113        Message::CommitMultiDiffLoaded,
114        (|| {
115            let repo = open_repo!(&path);
116            let mut diffs = Vec::with_capacity(file_paths.len());
117            for fp in &file_paths {
118                match gitkraft_core::features::diff::get_single_file_diff(&repo, &oid, fp) {
119                    Ok(diff) => diffs.push(diff),
120                    Err(e) => return Err(format!("{fp}: {e}")),
121                }
122            }
123            Ok(diffs)
124        })()
125    )
126}
127
128/// Diff multiple files from a specific commit against the current working tree.
129pub fn load_multi_file_commit_vs_workdir(
130    path: PathBuf,
131    oid: String,
132    file_paths: Vec<String>,
133) -> Task<Message> {
134    git_task!(
135        Message::CommitMultiDiffLoaded,
136        (|| {
137            let repo = open_repo!(&path);
138            let mut diffs = Vec::with_capacity(file_paths.len());
139            for fp in &file_paths {
140                match gitkraft_core::features::diff::diff_file_commit_vs_workdir(&repo, &oid, fp) {
141                    Ok(diff) => diffs.push(diff),
142                    Err(e) => return Err(format!("{fp}: {e}")),
143                }
144            }
145            Ok(diffs)
146        })()
147    )
148}
149
150/// Create a new commit with the currently staged changes.
151pub fn create_commit(path: PathBuf, message: String) -> Task<Message> {
152    git_task!(
153        Message::CommitCreated,
154        (|| {
155            let repo = open_repo!(&path);
156            gitkraft_core::features::commits::create_commit(&repo, &message)
157                .map(|_| ())
158                .str_err()
159        })()
160    )
161}
162
163/// Restore a single file from a commit to the working directory.
164pub fn checkout_file_at_commit(path: PathBuf, oid: String, file_path: String) -> Task<Message> {
165    git_repo_then_reload!(path, |repo| {
166        gitkraft_core::features::diff::checkout_file_at_commit(&repo, &oid, &file_path)
167    })
168}
169
170/// Cherry-pick a sequence of commits onto the current branch.
171pub fn cherry_pick_commits(path: PathBuf, oids: Vec<String>) -> Task<Message> {
172    git_task!(
173        Message::GitOperationResult,
174        (|| {
175            for oid in &oids {
176                gitkraft_core::features::commits::cherry_pick_commit(&path, oid)
177                    .map_err(|e| format!("{oid}: {e}"))?;
178            }
179            crate::features::repo::commands::load_repo_blocking(&path)
180        })()
181    )
182}
183
184/// Revert a sequence of commits in order.
185pub fn revert_commits(path: PathBuf, oids: Vec<String>) -> Task<Message> {
186    git_task!(
187        Message::GitOperationResult,
188        (|| {
189            for oid in &oids {
190                gitkraft_core::features::repo::revert_commit(&path, oid)
191                    .map_err(|e| format!("{oid}: {e}"))?;
192            }
193            crate::features::repo::commands::load_repo_blocking(&path)
194        })()
195    )
196}
197
198/// Load the combined diff across a range of commits.
199pub fn load_commit_range_diff(
200    path: PathBuf,
201    oldest_oid: String,
202    newest_oid: String,
203) -> Task<Message> {
204    git_task!(
205        Message::CommitRangeDiffLoaded,
206        (|| {
207            let repo = open_repo!(&path);
208            gitkraft_core::features::diff::get_commit_range_diff(&repo, &oldest_oid, &newest_oid)
209                .str_err()
210        })()
211    )
212}
213
214/// Restore multiple files from a commit to the working directory.
215pub fn checkout_multi_files_at_commit(
216    path: PathBuf,
217    oid: String,
218    file_paths: Vec<String>,
219) -> Task<Message> {
220    git_task!(
221        Message::GitOperationResult,
222        (|| {
223            let repo = open_repo!(&path);
224            for fp in &file_paths {
225                gitkraft_core::features::diff::checkout_file_at_commit(&repo, &oid, fp)
226                    .map_err(|e| format!("{fp}: {e}"))?;
227            }
228            crate::features::repo::commands::load_repo_blocking(&path)
229        })()
230    )
231}