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