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:
parent
136dcac1e1
commit
762f5e0fa1
8 changed files with 153 additions and 9 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue