diff --git a/asyncgit/src/sync/commit_files.rs b/asyncgit/src/sync/commit_files.rs index a9cae476..02e0e328 100644 --- a/asyncgit/src/sync/commit_files.rs +++ b/asyncgit/src/sync/commit_files.rs @@ -1,5 +1,5 @@ -use super::{utils::repo, CommitId}; -use crate::{error::Result, StatusItem, StatusItemType}; +use super::{stash::is_stash_commit, utils::repo, CommitId}; +use crate::{error::Result, StatusItem, StatusItemType, CWD}; use git2::{Diff, DiffDelta, DiffOptions, Repository}; use scopetime::scope_time; @@ -33,6 +33,22 @@ pub fn get_commit_files( None, )?; + // stash commits have parent commits containing untracked files and if we want to show + // these files as if they were actually in the stash commit we have to have some specific + // handling regarding these special stash commits. + // more info can be found at https://stackoverflow.com/questions/51275777/why-does-git-stash-pop-say-that-it-could-not-restore-untracked-files-from-stash/51276389#51276389 + if is_stash_commit(repo_path, &id)? { + let commit = repo.find_commit(id.into())?; + let untracked_commit = commit.parent_id(2)?; + + let mut untracked_files = get_commit_files( + repo_path, + CommitId::new(untracked_commit), + )?; + + res.append(&mut untracked_files); + } + Ok(res) } @@ -52,7 +68,7 @@ pub(crate) fn get_commit_diff( None }; - let mut opt = pathspec.map(|p| { + let mut opt = pathspec.clone().map(|p| { let mut opts = DiffOptions::new(); opts.pathspec(p); opts.show_binary(true); @@ -65,6 +81,16 @@ pub(crate) fn get_commit_diff( opt.as_mut(), )?; + if diff.deltas().len() == 0 && is_stash_commit(CWD, &id)? { + let untracked_commit = commit.parent_id(2)?; + + return get_commit_diff( + repo, + CommitId::new(untracked_commit), + (&pathspec).clone(), + ); + } + Ok(diff) } @@ -81,31 +107,31 @@ mod tests { use std::{fs::File, io::Write, path::Path}; #[test] - fn test_smoke() { + fn test_smoke() -> Result<()> { let file_path = Path::new("file1.txt"); - let (_td, repo) = repo_init().unwrap(); + let (_td, repo) = repo_init()?; let root = repo.path().parent().unwrap(); let repo_path = root.as_os_str().to_str().unwrap(); - File::create(&root.join(file_path)) - .unwrap() - .write_all(b"test file1 content") - .unwrap(); + File::create(&root.join(file_path))? + .write_all(b"test file1 content")?; - stage_add_file(repo_path, file_path).unwrap(); + stage_add_file(repo_path, file_path)?; - let id = commit(repo_path, "commit msg").unwrap(); + let id = commit(repo_path, "commit msg")?; - let diff = get_commit_files(repo_path, id).unwrap(); + let diff = get_commit_files(repo_path, id)?; assert_eq!(diff.len(), 1); assert_eq!(diff[0].status, StatusItemType::New); + + Ok(()) } #[test] fn test_stashed_untracked() -> Result<()> { let file_path = Path::new("file1.txt"); - let (_td, repo) = repo_init().unwrap(); + let (_td, repo) = repo_init()?; let root = repo.path().parent().unwrap(); let repo_path = root.as_os_str().to_str().unwrap(); @@ -117,10 +143,10 @@ mod tests { //TODO: https://github.com/extrawurst/gitui/issues/130 // `get_commit_diff` actually needs to merge the regular diff // and a third parent diff containing the untracked files - let _diff = get_commit_files(repo_path, id)?; + let diff = get_commit_files(repo_path, id)?; - // assert_eq!(diff.len(), 1); - // assert_eq!(diff[0].status, StatusItemType::New); + assert_eq!(diff.len(), 1); + assert_eq!(diff[0].status, StatusItemType::New); Ok(()) } diff --git a/asyncgit/src/sync/stash.rs b/asyncgit/src/sync/stash.rs index da67dc82..01a5c7aa 100644 --- a/asyncgit/src/sync/stash.rs +++ b/asyncgit/src/sync/stash.rs @@ -19,6 +19,15 @@ pub fn get_stashes(repo_path: &str) -> Result> { Ok(list) } +/// checks whether a given commit is a stash commit. +pub fn is_stash_commit( + repo_path: &str, + id: &CommitId, +) -> Result { + let stashes = get_stashes(repo_path)?; + Ok(stashes.contains(&id)) +} + /// pub fn stash_drop(repo_path: &str, stash_id: CommitId) -> Result<()> { scope_time!("stash_drop");