Skip to content

Commit c79b3d7

Browse files
author
Stephan Dilly
authored
Fix 576 stash apply conflicts (#578)
* unittest that applying with conflicts errors out and prepare for stash apply to allow creating conflicts for later * mark conflicting items
1 parent cfd5c6a commit c79b3d7

File tree

5 files changed

+87
-4
lines changed

5 files changed

+87
-4
lines changed

asyncgit/src/sync/stash.rs

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use super::{utils::repo, CommitId};
22
use crate::error::{Error, Result};
3-
use git2::{Oid, Repository, StashFlags};
3+
use git2::{
4+
build::CheckoutBuilder, Oid, Repository, StashApplyOptions,
5+
StashFlags,
6+
};
47
use scopetime::scope_time;
58

69
///
@@ -45,14 +48,20 @@ pub fn stash_drop(repo_path: &str, stash_id: CommitId) -> Result<()> {
4548
pub fn stash_apply(
4649
repo_path: &str,
4750
stash_id: CommitId,
51+
allow_conflicts: bool,
4852
) -> Result<()> {
4953
scope_time!("stash_apply");
5054

5155
let mut repo = repo(repo_path)?;
5256

5357
let index = get_stash_index(&mut repo, stash_id.get_oid())?;
5458

55-
repo.stash_apply(index, None)?;
59+
let mut checkout = CheckoutBuilder::new();
60+
checkout.allow_conflicts(allow_conflicts);
61+
62+
let mut opt = StashApplyOptions::default();
63+
opt.checkout_options(checkout);
64+
repo.stash_apply(index, Some(&mut opt))?;
5665

5766
Ok(())
5867
}
@@ -109,7 +118,11 @@ mod tests {
109118
use super::*;
110119
use crate::sync::{
111120
commit, get_commit_files, get_commits_info, stage_add_file,
112-
tests::{debug_cmd_print, get_statuses, repo_init},
121+
staging::repo_write_file,
122+
tests::{
123+
debug_cmd_print, get_statuses, repo_init,
124+
write_commit_file,
125+
},
113126
};
114127
use std::{fs::File, io::Write, path::Path};
115128

@@ -211,4 +224,66 @@ mod tests {
211224

212225
Ok(())
213226
}
227+
228+
#[test]
229+
fn test_stash_apply_conflict() {
230+
let (_td, repo) = repo_init().unwrap();
231+
let root = repo.path().parent().unwrap();
232+
let repo_path = root.as_os_str().to_str().unwrap();
233+
234+
repo_write_file(&repo, "test.txt", "test").unwrap();
235+
236+
let id =
237+
stash_save(repo_path, Some("foo"), true, false).unwrap();
238+
239+
repo_write_file(&repo, "test.txt", "foo").unwrap();
240+
241+
let res = stash_apply(repo_path, id, false);
242+
243+
assert!(res.is_err());
244+
}
245+
246+
#[test]
247+
fn test_stash_apply_conflict2() {
248+
let (_td, repo) = repo_init().unwrap();
249+
let root = repo.path().parent().unwrap();
250+
let repo_path = root.as_os_str().to_str().unwrap();
251+
252+
write_commit_file(&repo, "test.txt", "test", "c1");
253+
254+
repo_write_file(&repo, "test.txt", "test2").unwrap();
255+
256+
let id =
257+
stash_save(repo_path, Some("foo"), true, false).unwrap();
258+
259+
repo_write_file(&repo, "test.txt", "test3").unwrap();
260+
261+
let res = stash_apply(repo_path, id, false);
262+
263+
assert!(res.is_err());
264+
}
265+
266+
#[test]
267+
fn test_stash_apply_creating_conflict() {
268+
let (_td, repo) = repo_init().unwrap();
269+
let root = repo.path().parent().unwrap();
270+
let repo_path = root.as_os_str().to_str().unwrap();
271+
272+
write_commit_file(&repo, "test.txt", "test", "c1");
273+
274+
repo_write_file(&repo, "test.txt", "test2").unwrap();
275+
276+
let id =
277+
stash_save(repo_path, Some("foo"), true, false).unwrap();
278+
279+
repo_write_file(&repo, "test.txt", "test3").unwrap();
280+
281+
let res = stash_apply(repo_path, id, false);
282+
283+
assert!(res.is_err());
284+
285+
let res = stash_apply(repo_path, id, true);
286+
287+
assert!(res.is_ok());
288+
}
214289
}

asyncgit/src/sync/status.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub enum StatusItemType {
1818
Renamed,
1919
///
2020
Typechange,
21+
///
22+
Conflicted,
2123
}
2224

2325
impl From<Status> for StatusItemType {
@@ -30,6 +32,8 @@ impl From<Status> for StatusItemType {
3032
Self::Renamed
3133
} else if s.is_index_typechange() || s.is_wt_typechange() {
3234
Self::Typechange
35+
} else if s.is_conflicted() {
36+
Self::Conflicted
3337
} else {
3438
Self::Modified
3539
}

src/components/filetree.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ impl FileTreeComponent {
142142
StatusItemType::Deleted => '-',
143143
StatusItemType::Renamed => 'R',
144144
StatusItemType::Typechange => ' ',
145+
StatusItemType::Conflicted => '!',
145146
}
146147
}
147148

src/tabs/stashlist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl StashList {
5757

5858
fn apply_stash(&mut self) {
5959
if let Some(e) = self.list.selected_entry() {
60-
match sync::stash_apply(CWD, e.id) {
60+
match sync::stash_apply(CWD, e.id, false) {
6161
Ok(_) => {
6262
self.queue
6363
.borrow_mut()

src/ui/style.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ impl Theme {
135135
StatusItemType::Renamed => {
136136
Style::default().fg(self.diff_file_moved)
137137
}
138+
StatusItemType::Conflicted => Style::default()
139+
.fg(self.diff_file_modified)
140+
.add_modifier(Modifier::BOLD),
138141
StatusItemType::Typechange => Style::default(),
139142
};
140143

0 commit comments

Comments
 (0)