From 823041c795b9edc0dc095b48ddc1b768a9d814e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Daron?= Date: Mon, 29 Apr 2024 22:08:35 +0200 Subject: [PATCH] cli: create new wc_commit when wc_commit become immuable --- CHANGELOG.md | 3 ++ cli/src/cli_util.rs | 40 ++++++++++++++++++- cli/tests/test_immutable_commits.rs | 59 +++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2187b67d..c6bf35317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 rebase the children of the revision being split if they had other parents (i.e. if the child was a merge). +* When the working copy commit becomes immutable, a new one is automatically created on top of it +to avoid letting the user edit the immutable one. + * The `snapshot.max-new-file-size` option can now handle raw integer literals, interpreted as a number of bytes, where previously it could only handle string literals. This means that `snapshot.max-new-file-size="1"` and diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index f7192a2d8..fb8778f8f 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -1106,8 +1106,9 @@ impl WorkspaceCommandHelper { self.commit_summary_template().format(commit, formatter) } - pub fn check_rewritable<'a>( + fn check_repo_rewritable<'a>( &self, + repo: &dyn Repo, commits: impl IntoIterator, ) -> Result<(), CommandError> { if self.global_args.ignore_immutable { @@ -1127,7 +1128,12 @@ impl WorkspaceCommandHelper { .map_err(|e| { config_error_with_message("Invalid `revset-aliases.immutable_heads()`", e) })?; - let mut expression = self.attach_revset_evaluator(immutable)?; + let mut expression = RevsetExpressionEvaluator::new( + repo, + self.revset_extensions.clone(), + self.id_prefix_context()?, + immutable, + ); expression.intersect_with(&to_rewrite_revset); let mut commit_id_iter = expression.evaluate_to_commit_ids().map_err(|e| { @@ -1153,6 +1159,13 @@ impl WorkspaceCommandHelper { Ok(()) } + pub fn check_rewritable<'a>( + &'a self, + commits: impl IntoIterator, + ) -> Result<(), CommandError> { + self.check_repo_rewritable(self.repo().as_ref(), commits) + } + #[instrument(skip_all)] fn snapshot_working_copy(&mut self, ui: &mut Ui) -> Result<(), CommandError> { let workspace_id = self.workspace_id().to_owned(); @@ -1318,6 +1331,26 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin writeln!(ui.status(), "Rebased {num_rebased} descendant commits")?; } + for (workspace_id, wc_commit_id) in + tx.mut_repo().view().wc_commit_ids().clone().iter().sorted() + //sorting otherwise non deterministic order (bad for tests) + { + if self + .check_repo_rewritable(tx.repo(), [wc_commit_id]) + .is_err() + { + let wc_commit = tx.repo().store().get_commit(wc_commit_id)?; + tx.mut_repo() + .check_out(workspace_id.clone(), &self.settings, &wc_commit)?; + writeln!( + ui.warning_default(), + "The working-copy commit in workspace '{}' became immutable, so a new commit \ + has been created on top of it.", + workspace_id.as_str() + )?; + } + } + let old_repo = tx.base_repo().clone(); let maybe_old_wc_commit = old_repo @@ -1331,6 +1364,7 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin .get_wc_commit_id(self.workspace_id()) .map(|commit_id| tx.repo().store().get_commit(commit_id)) .transpose()?; + if self.working_copy_shared_with_git { let git_repo = self.git_backend().unwrap().open_git_repo()?; if let Some(wc_commit) = &maybe_new_wc_commit { @@ -1339,6 +1373,7 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin let failed_branches = git::export_refs(tx.mut_repo())?; print_failed_git_export(ui, &failed_branches)?; } + self.user_repo = ReadonlyUserRepo::new(tx.commit(description)); self.report_repo_changes(ui, &old_repo)?; @@ -1350,6 +1385,7 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin // update it. } } + let settings = &self.settings; if settings.user_name().is_empty() || settings.user_email().is_empty() { writeln!( diff --git a/cli/tests/test_immutable_commits.rs b/cli/tests/test_immutable_commits.rs index f9ad490fc..3325c3111 100644 --- a/cli/tests/test_immutable_commits.rs +++ b/cli/tests/test_immutable_commits.rs @@ -109,6 +109,65 @@ fn test_rewrite_immutable_generic() { "###); } +#[test] +fn test_new_wc_commit_when_wc_immutable() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init"]); + test_env.jj_cmd_ok(test_env.env_root(), &["branch", "create", "main"]); + test_env.add_config(r#"revset-aliases."immutable_heads()" = "main""#); + test_env.jj_cmd_ok(test_env.env_root(), &["new", "-m=a"]); + let (_, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["branch", "set", "main"]); + insta::assert_snapshot!(stderr, @r###" +Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it. +Working copy now at: zsuskuln 87e33403 (empty) (no description set) +Parent commit : kkmpptxz 7272528e main | (empty) a + "###); +} + +#[test] +fn test_immutable_heads_set_to_working_copy() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init"]); + test_env.jj_cmd_ok(test_env.env_root(), &["branch", "create", "main"]); + test_env.add_config(r#"revset-aliases."immutable_heads()" = "@""#); + let (_, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["new", "-m=a"]); + insta::assert_snapshot!(stderr, @r###" +Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it. +Working copy now at: pmmvwywv 09dafa31 (empty) (no description set) +Parent commit : kkmpptxz 4963e243 (empty) a + "###); +} + +#[test] +fn test_new_wc_commit_when_wc_immutable_multi_workspace() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init"]); + test_env.jj_cmd_ok(test_env.env_root(), &["branch", "create", "main"]); + test_env.add_config(r#"revset-aliases."immutable_heads()" = "main""#); + test_env.jj_cmd_ok(test_env.env_root(), &["new", "-m=a"]); + test_env.jj_cmd_ok(test_env.env_root(), &["workspace", "add", "workspace1"]); + let workspace1_envroot = test_env.env_root().join("workspace1"); + test_env.jj_cmd_ok(workspace1_envroot.as_path(), &["edit", "default@"]); + let (_, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["branch", "set", "main"]); + insta::assert_snapshot!(stderr, @r###" +Warning: The working-copy commit in workspace 'default' became immutable, so a new commit has been created on top of it. +Warning: The working-copy commit in workspace 'workspace1' became immutable, so a new commit has been created on top of it. +Working copy now at: royxmykx c37fd624 (empty) (no description set) +Parent commit : kkmpptxz ada0ee19 main | a + "###); + test_env.jj_cmd_ok(workspace1_envroot.as_path(), &["workspace", "update-stale"]); + let (stdout, _) = test_env.jj_cmd_ok(workspace1_envroot.as_path(), &["log", "--no-graph"]); + insta::assert_snapshot!(stdout, @r###" +nppvrztz test.user@example.com 2001-02-03 08:05:11 workspace1@ f5e1b845 +(empty) (no description set) +royxmykx test.user@example.com 2001-02-03 08:05:12 default@ c37fd624 +(empty) (no description set) +kkmpptxz test.user@example.com 2001-02-03 08:05:12 main ada0ee19 +a +zzzzzzzz root() 00000000 + "###); +} + #[test] fn test_rewrite_immutable_commands() { let test_env = TestEnvironment::default();