diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 1f8248a07e5ac..c9f57e454f4d8 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -285,11 +285,17 @@ impl Git { let valid_from = self.resolve_base(from_commit, CIEnv::new())?; - let mut args = if let Some(to_commit) = to_commit { - vec!["diff", "--name-only", &valid_from, to_commit] - } else { - vec!["diff", "--name-only", &valid_from] - }; + // If a to commit is not specified for `diff-tree` it will change the comparison + // to be between the provided commit and it's parent + let to_commit = to_commit.unwrap_or("HEAD"); + let mut args = vec![ + "diff-tree", + "-r", + "--name-only", + "--no-commit-id", + &valid_from, + to_commit, + ]; if merge_base { args.push("--merge-base"); @@ -301,10 +307,17 @@ impl Git { // We only care about non-tracked files if we haven't specified both ends up the // comparison if include_uncommitted { - // Add untracked files, i.e. files that are not in git at all - let output = self - .execute_git_command(&["ls-files", "--others", "--exclude-standard"], pathspec)?; - self.add_files_from_stdout(&mut files, turbo_root, output); + // Add untracked files or unstaged changes, i.e. files that are not in git at + // all + let ls_files_output = self.execute_git_command( + &["ls-files", "--others", "--modified", "--exclude-standard"], + pathspec, + )?; + self.add_files_from_stdout(&mut files, turbo_root, ls_files_output); + // Include any files that have been staged, but not committed + let diff_output = + self.execute_git_command(&["diff", "--name-only", "--cached"], pathspec)?; + self.add_files_from_stdout(&mut files, turbo_root, diff_output); } Ok(files) @@ -514,6 +527,26 @@ mod tests { .unwrap() } + fn commit_rename(repo: &Repository, source: &Path, dest: &Path, previous_commit: Oid) -> Oid { + let mut index = repo.index().unwrap(); + index.remove_path(source).unwrap(); + index.add_path(dest).unwrap(); + let tree_oid = index.write_tree().unwrap(); + index.write().unwrap(); + let tree = repo.find_tree(tree_oid).unwrap(); + let previous_commit = repo.find_commit(previous_commit).unwrap(); + + repo.commit( + Some("HEAD"), + &repo.signature().unwrap(), + &repo.signature().unwrap(), + "Commit", + &tree, + std::slice::from_ref(&&previous_commit), + ) + .unwrap() + } + #[test] fn test_shallow_clone() -> Result<(), Error> { let tmp_dir = tempfile::tempdir()?; @@ -579,6 +612,38 @@ mod tests { Ok(()) } + #[test] + fn test_renamed_files() -> Result<(), Error> { + let (repo_root, repo) = setup_repository(None)?; + + let file = repo_root.path().join("foo.js"); + let file_path = Path::new("foo.js"); + fs::write(&file, "let z = 0;")?; + + let first_commit_oid = commit_file(&repo, file_path, None); + + fs::rename(file, repo_root.path().join("bar.js")).unwrap(); + + let new_file_path = Path::new("bar.js"); + let _second_commit_oid = commit_rename(&repo, file_path, new_file_path, first_commit_oid); + + let first_commit_sha = first_commit_oid.to_string(); + let git_root = repo_root.path().to_owned(); + let turborepo_root = repo_root.path().to_owned(); + let files = changed_files( + git_root, + turborepo_root, + Some(&first_commit_sha), + Some("HEAD"), + false, + )?; + + assert_eq!( + files, + HashSet::from(["foo.js".to_string(), "bar.js".to_string()]) + ); + Ok(()) + } #[test] fn test_merge_base() -> Result<(), Error> { let (repo_root, repo) = setup_repository(None)?; @@ -652,10 +717,43 @@ mod tests { )?; assert_eq!(files, HashSet::from(["bar.js".to_string()])); + // Test that uncommitted file in index is not marked as changed when not + // checking uncommitted + let files = changed_files( + repo_root.path().to_path_buf(), + turbo_root.to_path_buf(), + Some("HEAD"), + None, + false, + )?; + assert_eq!(files, HashSet::new()); + + // Test that uncommitted file in index is marked as changed when + // checking uncommitted + let files = changed_files( + repo_root.path().to_path_buf(), + turbo_root.to_path_buf(), + Some("HEAD"), + None, + true, + )?; + assert_eq!(files, HashSet::from(["bar.js".to_string()])); + // Add file to index index.add_path(Path::new("bar.js")).unwrap(); index.write().unwrap(); + // Test that uncommitted file in index is not marked as changed when not + // checking uncommitted + let files = changed_files( + repo_root.path().to_path_buf(), + turbo_root.to_path_buf(), + Some("HEAD"), + None, + false, + )?; + assert_eq!(files, HashSet::new()); + // Test that uncommitted file in index is still marked as changed let files = changed_files( repo_root.path().to_path_buf(),