cli: create new wc_commit when wc_commit become immuable

This commit is contained in:
Théo Daron 2024-04-29 22:08:35 +02:00 committed by Théo Daron
parent b0d17acb30
commit 823041c795
3 changed files with 100 additions and 2 deletions

View file

@ -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 rebase the children of the revision being split if they had other parents
(i.e. if the child was a merge). (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, * 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 interpreted as a number of bytes, where previously it could only handle string
literals. This means that `snapshot.max-new-file-size="1"` and literals. This means that `snapshot.max-new-file-size="1"` and

View file

@ -1106,8 +1106,9 @@ impl WorkspaceCommandHelper {
self.commit_summary_template().format(commit, formatter) self.commit_summary_template().format(commit, formatter)
} }
pub fn check_rewritable<'a>( fn check_repo_rewritable<'a>(
&self, &self,
repo: &dyn Repo,
commits: impl IntoIterator<Item = &'a CommitId>, commits: impl IntoIterator<Item = &'a CommitId>,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if self.global_args.ignore_immutable { if self.global_args.ignore_immutable {
@ -1127,7 +1128,12 @@ impl WorkspaceCommandHelper {
.map_err(|e| { .map_err(|e| {
config_error_with_message("Invalid `revset-aliases.immutable_heads()`", 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); expression.intersect_with(&to_rewrite_revset);
let mut commit_id_iter = expression.evaluate_to_commit_ids().map_err(|e| { let mut commit_id_iter = expression.evaluate_to_commit_ids().map_err(|e| {
@ -1153,6 +1159,13 @@ impl WorkspaceCommandHelper {
Ok(()) Ok(())
} }
pub fn check_rewritable<'a>(
&'a self,
commits: impl IntoIterator<Item = &'a CommitId>,
) -> Result<(), CommandError> {
self.check_repo_rewritable(self.repo().as_ref(), commits)
}
#[instrument(skip_all)] #[instrument(skip_all)]
fn snapshot_working_copy(&mut self, ui: &mut Ui) -> Result<(), CommandError> { fn snapshot_working_copy(&mut self, ui: &mut Ui) -> Result<(), CommandError> {
let workspace_id = self.workspace_id().to_owned(); 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")?; 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 old_repo = tx.base_repo().clone();
let maybe_old_wc_commit = old_repo 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()) .get_wc_commit_id(self.workspace_id())
.map(|commit_id| tx.repo().store().get_commit(commit_id)) .map(|commit_id| tx.repo().store().get_commit(commit_id))
.transpose()?; .transpose()?;
if self.working_copy_shared_with_git { if self.working_copy_shared_with_git {
let git_repo = self.git_backend().unwrap().open_git_repo()?; let git_repo = self.git_backend().unwrap().open_git_repo()?;
if let Some(wc_commit) = &maybe_new_wc_commit { 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())?; let failed_branches = git::export_refs(tx.mut_repo())?;
print_failed_git_export(ui, &failed_branches)?; print_failed_git_export(ui, &failed_branches)?;
} }
self.user_repo = ReadonlyUserRepo::new(tx.commit(description)); self.user_repo = ReadonlyUserRepo::new(tx.commit(description));
self.report_repo_changes(ui, &old_repo)?; 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. // update it.
} }
} }
let settings = &self.settings; let settings = &self.settings;
if settings.user_name().is_empty() || settings.user_email().is_empty() { if settings.user_name().is_empty() || settings.user_email().is_empty() {
writeln!( writeln!(

View file

@ -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] #[test]
fn test_rewrite_immutable_commands() { fn test_rewrite_immutable_commands() {
let test_env = TestEnvironment::default(); let test_env = TestEnvironment::default();