Skip to content

Commit

Permalink
cli: create new wc_commit when wc_commit become immuable
Browse files Browse the repository at this point in the history
  • Loading branch information
tdaron committed May 14, 2024
1 parent b0d17ac commit 823041c
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 38 additions & 2 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = &'a CommitId>,
) -> Result<(), CommandError> {
if self.global_args.ignore_immutable {
Expand All @@ -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| {
Expand All @@ -1153,6 +1159,13 @@ impl WorkspaceCommandHelper {
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)]
fn snapshot_working_copy(&mut self, ui: &mut Ui) -> Result<(), CommandError> {
let workspace_id = self.workspace_id().to_owned();
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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)?;

Expand All @@ -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!(
Expand Down
59 changes: 59 additions & 0 deletions cli/tests/test_immutable_commits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected] 2001-02-03 08:05:11 workspace1@ f5e1b845
(empty) (no description set)
royxmykx [email protected] 2001-02-03 08:05:12 default@ c37fd624
(empty) (no description set)
kkmpptxz [email protected] 2001-02-03 08:05:12 main ada0ee19
a
zzzzzzzz root() 00000000
"###);
}

#[test]
fn test_rewrite_immutable_commands() {
let test_env = TestEnvironment::default();
Expand Down

0 comments on commit 823041c

Please sign in to comment.