ok/jj
1
0
Fork 0
forked from mirrors/jj

cli: add global option to not commit transaction

This patch adds a global `--no-commit-transaction` flag that prevents
publishing of most operations, including the ones created by
`snapshot_working_copy()` and `finish_transaction()`. The operations
are still created as usual.

We may want to follow up with a `jj op publish/commit/adopt` command
for publishing an operation that was not committed.

Closes #2562
This commit is contained in:
Martin von Zweigbergk 2024-09-12 13:10:27 -07:00
parent 136dcac1e1
commit 762f5e0fa1
No known key found for this signature in database
8 changed files with 153 additions and 9 deletions

View file

@ -49,6 +49,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* `jj op log` gained an option to include operation diffs.
* A new global flag `--no-commit-transaction` lets you run a command without
impacting the repo state or the working copy.
### Fixed bugs
* Fixed panic when parsing invalid conflict markers of a particular form.

View file

@ -346,6 +346,23 @@ impl CommandHelper {
)?)
}
pub fn should_commit_transaction(&self) -> bool {
!self.global_args().no_commit_transaction
}
pub fn maybe_commit_transaction(
&self,
tx: Transaction,
description: impl Into<String>,
) -> Arc<ReadonlyRepo> {
let unpublished_op = tx.write(description);
if self.should_commit_transaction() {
unpublished_op.publish()
} else {
unpublished_op.leave_unpublished()
}
}
pub fn workspace_loader(&self) -> Result<&dyn WorkspaceLoader, CommandError> {
self.data
.maybe_workspace_loader
@ -839,7 +856,11 @@ impl WorkspaceCommandHelper {
// state to it without updating working copy files.
locked_ws.locked_wc().reset(&new_git_head_commit)?;
tx.repo_mut().rebase_descendants(command.settings())?;
self.user_repo = ReadonlyUserRepo::new(tx.commit("import git head"));
self.user_repo = ReadonlyUserRepo::new(
self.env
.command
.maybe_commit_transaction(tx, "import git head"),
);
locked_ws.finish(self.user_repo.repo.op_id().clone())?;
if old_git_head.is_present() {
writeln!(
@ -1489,9 +1510,15 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
print_failed_git_export(ui, &refs)?;
}
self.user_repo = ReadonlyUserRepo::new(tx.commit("snapshot working copy"));
self.user_repo = ReadonlyUserRepo::new(
self.env
.command
.maybe_commit_transaction(tx, "snapshot working copy"),
);
}
if self.env.command.should_commit_transaction() {
locked_ws.finish(self.user_repo.repo.op_id().clone())?;
}
locked_ws.finish(self.user_repo.repo.op_id().clone())?;
Ok(())
}
@ -1606,10 +1633,11 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
print_failed_git_export(ui, &refs)?;
}
self.user_repo = ReadonlyUserRepo::new(tx.commit(description));
self.user_repo =
ReadonlyUserRepo::new(self.env.command.maybe_commit_transaction(tx, description));
self.report_repo_changes(ui, &old_repo)?;
if self.may_update_working_copy {
if self.may_update_working_copy && self.env.command.should_commit_transaction() {
if let Some(new_commit) = &maybe_new_wc_commit {
self.update_working_copy(ui, maybe_old_wc_commit.as_ref(), new_commit)?;
} else {
@ -1738,6 +1766,14 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
)?;
}
if !self.env.command.should_commit_transaction() {
writeln!(
fmt,
"Operation left uncommitted because --no-commit-transaction was requested: {}",
short_operation_hash(self.repo().op_id())
)?;
}
Ok(())
}
@ -2610,6 +2646,21 @@ pub struct GlobalArgs {
/// implies `--ignore-working-copy`.
#[arg(long, global = true)]
pub ignore_working_copy: bool,
/// Run the command as usual but don't commit any transactions
///
/// When this option is given, the operations will still be created as
/// usual but they will not be committed/promoted to the operation log. The
/// working copy will also not be updated.
///
/// The command will print the resulting operation id. You can pass that to
/// e.g. `jj --at-op` to inspect the resulting repo state, or you can pass
/// it to `jj op restore` to restore the repo to that state.
///
/// Note that this does *not* prevent side effects outside the repo. For
/// example, `jj git push --no-commit-transaction` will still perform
/// the push.
#[arg(long, global = true)]
pub no_commit_transaction: bool,
/// Allow rewriting immutable commits
///
/// By default, Jujutsu prevents rewriting commits in the configured set of

View file

@ -62,7 +62,9 @@ pub(crate) fn cmd_file_track(
if num_rebased > 0 {
writeln!(ui.status(), "Rebased {num_rebased} descendant commits")?;
}
let repo = tx.commit("track paths");
locked_ws.finish(repo.op_id().clone())?;
if command.should_commit_transaction() {
let repo = tx.commit("track paths");
locked_ws.finish(repo.op_id().clone())?;
}
Ok(())
}

View file

@ -107,7 +107,9 @@ Make sure they're ignored, then try again.",
if num_rebased > 0 {
writeln!(ui.status(), "Rebased {num_rebased} descendant commits")?;
}
let repo = tx.commit("untrack paths");
locked_ws.finish(repo.op_id().clone())?;
if command.should_commit_transaction() {
let repo = tx.commit("untrack paths");
locked_ws.finish(repo.op_id().clone())?;
}
Ok(())
}

View file

@ -83,6 +83,9 @@ pub fn cmd_git_init(
command: &CommandHelper,
args: &GitInitArgs,
) -> Result<(), CommandError> {
if command.global_args().no_commit_transaction {
return Err(cli_error("--no-commit-transaction is not respected"));
}
if command.global_args().ignore_working_copy {
return Err(cli_error("--ignore-working-copy is not respected"));
}

View file

@ -159,6 +159,13 @@ To get started, see the tutorial at https://martinvonz.github.io/jj/latest/tutor
By default, Jujutsu snapshots the working copy at the beginning of every command. The working copy is also updated at the end of the command, if the command modified the working-copy commit (`@`). If you want to avoid snapshotting the working copy and instead see a possibly stale working copy commit, you can use `--ignore-working-copy`. This may be useful e.g. in a command prompt, especially if you have another process that commits the working copy.
Loading the repository at a specific operation with `--at-operation` implies `--ignore-working-copy`.
* `--no-commit-transaction` — Run the command as usual but don't commit any transactions
When this option is given, the operations will still be created as usual but they will not be committed/promoted to the operation log. The working copy will also not be updated.
The command will print the resulting operation id. You can pass that to e.g. `jj --at-op` to inspect the resulting repo state, or you can pass it to `jj op restore` to restore the repo to that state.
Note that this does *not* prevent side effects outside the repo. For example, `jj git push --no-commit-transaction` will still perform the push.
* `--ignore-immutable` — Allow rewriting immutable commits
By default, Jujutsu prevents rewriting commits in the configured set of immutable commits. This option disables that check and lets you rewrite any commit but the root commit.

View file

@ -96,6 +96,19 @@ fn test_git_init_internal() {
assert_eq!(read_git_target(&workspace_root), "git");
}
#[test]
fn test_git_init_internal_no_commit_transaction() {
let test_env = TestEnvironment::default();
let workspace_root = test_env.env_root().join("repo");
std::fs::create_dir(&workspace_root).unwrap();
let stderr =
test_env.jj_cmd_cli_error(&workspace_root, &["git", "init", "--no-commit-transaction"]);
insta::assert_snapshot!(stderr, @r###"
Error: --no-commit-transaction is not respected
"###);
}
#[test]
fn test_git_init_internal_ignore_working_copy() {
let test_env = TestEnvironment::default();

View file

@ -147,6 +147,68 @@ fn test_ignore_working_copy() {
"###);
}
#[test]
fn test_no_commit_transaction() {
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");
std::fs::write(repo_path.join("file1"), "initial").unwrap();
test_env.jj_cmd_ok(&repo_path, &["commit", "-m=initial"]);
let op_log_stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
let working_copy_stdout = test_env.jj_cmd_success(&repo_path, &["debug", "working-copy"]);
// Modify the working copy and run a mutating operation. With
// --no-commit-transaction, the working copy gets snapshotted and the operation
// gets created, but there's no new operation in the operation log, and the
// working copy state is not updated.
std::fs::write(repo_path.join("file2"), "initial").unwrap();
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["squash", "--no-commit-transaction"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"
Operation left uncommitted because --no-commit-transaction was requested: 3fa0e7ceb0e4
"###);
let first_line = stderr.split('\n').next().unwrap();
let op_id_hex = first_line[first_line.len() - 12..].to_string();
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "--ignore-working-copy"]);
assert_eq!(stdout, op_log_stdout);
let stdout = test_env.jj_cmd_success(&repo_path, &["debug", "working-copy"]);
assert_eq!(stdout, working_copy_stdout);
// We can see the resulting log and op log with --at-op
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-s", "--at-op", op_id_hex.as_str()]);
insta::assert_snapshot!(stdout, @r###"
@ mzvwutvl test.user@example.com 2001-02-03 08:05:11 e4e6953f
(empty) (no description set)
qpvuntsm test.user@example.com 2001-02-03 08:05:11 a2280cba
initial
A file1
A file2
zzzzzzzz root() 00000000
"###);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "--at-op", op_id_hex.as_str()]);
insta::assert_snapshot!(stdout, @r###"
@ 3fa0e7ceb0e4 test-username@host.example.com 2001-02-03 04:05:11.000 +07:00 - 2001-02-03 04:05:11.000 +07:00
squash commits into dc5f5c36813feca46c1e26ea80c9634ea2fdb9e6
args: jj squash --no-commit-transaction
63a798419dc4 test-username@host.example.com 2001-02-03 04:05:11.000 +07:00 - 2001-02-03 04:05:11.000 +07:00
snapshot working copy
args: jj squash --no-commit-transaction
04139fd4890a test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
commit a38d281287b19f33abe36fedb6c2df1370b90f54
args: jj commit '-m=initial'
0db616e9e09a test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
snapshot working copy
args: jj commit '-m=initial'
b51416386f26 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
9a7d829846af test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 root()
"###);
}
#[test]
fn test_repo_arg_with_init() {
let test_env = TestEnvironment::default();
@ -608,6 +670,7 @@ fn test_help() {
Global Options:
-R, --repository <REPOSITORY> Path to repository to operate on
--ignore-working-copy Don't snapshot the working copy, and don't update it
--no-commit-transaction Run the command as usual but don't commit any transactions
--ignore-immutable Allow rewriting immutable commits
--at-operation <AT_OPERATION> Operation to load the repo at [aliases: at-op]
--debug Enable debug logging