From 3711979a8f4daed42fd9a5ef8af9be2022be7403 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Wed, 15 Jan 2025 14:17:51 +0900 Subject: [PATCH] absorb: show warning if source commit contains file deletion --- cli/tests/test_absorb_command.rs | 19 +++++++++++++++++++ lib/src/absorb.rs | 11 ++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cli/tests/test_absorb_command.rs b/cli/tests/test_absorb_command.rs index 01fa10e64..eb6ec390b 100644 --- a/cli/tests/test_absorb_command.rs +++ b/cli/tests/test_absorb_command.rs @@ -438,6 +438,25 @@ fn test_absorb_conflict() { "); } +#[test] +fn test_absorb_deleted_file() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + let repo_path = test_env.env_root().join("repo"); + + test_env.jj_cmd_ok(&repo_path, &["describe", "-m1"]); + std::fs::write(repo_path.join("file1"), "1a\n").unwrap(); + + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::remove_file(repo_path.join("file1")).unwrap(); + + let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["absorb"]); + insta::assert_snapshot!(stderr, @r" + Warning: Skipping file1: Deleted file + Nothing changed. + "); +} + #[test] fn test_absorb_file_mode() { let test_env = TestEnvironment::default(); diff --git a/lib/src/absorb.rs b/lib/src/absorb.rs index d28dc0a8f..37d14ef5b 100644 --- a/lib/src/absorb.rs +++ b/lib/src/absorb.rs @@ -110,6 +110,7 @@ pub async fn split_hunks_to_trees( let (left_value, right_value) = entry.values?; let (left_text, executable) = match to_file_value(left_value) { Ok(Some(mut value)) => (value.read(left_path)?, value.executable), + // New file should have no destinations Ok(None) => continue, Err(reason) => { selected_trees @@ -120,7 +121,15 @@ pub async fn split_hunks_to_trees( }; let right_text = match to_file_value(right_value) { Ok(Some(mut value)) => value.read(right_path)?, - Ok(None) => continue, + // Deleted file could be absorbed, but that would require special + // handling to propagate deletion of the tree entry + Ok(None) => { + let reason = "Deleted file".to_owned(); + selected_trees + .skipped_paths + .push((right_path.to_owned(), reason)); + continue; + } Err(reason) => { selected_trees .skipped_paths