mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
cli: do not attempt to merge op heads if --at-op=@ is specified
The idea is that --at-op specifies a certain operation, so --at-op=@ can be interpreted as the option to select _the_ known head operation. This helps eliminate special cases from "op log" which doesn't snapshot nor merge concurrent ops.
This commit is contained in:
parent
b290af8e29
commit
2008991749
13 changed files with 53 additions and 27 deletions
|
@ -36,6 +36,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
preserved. If a checked-out named branch gets deleted locally or remotely, the
|
||||
corresponding commits will be abandoned.
|
||||
|
||||
* `jj --at-op=@` no longer merges concurrent operations if explicitly specified.
|
||||
|
||||
### Deprecations
|
||||
|
||||
* The original configuration syntax for `jj fix` is now deprecated in favor of
|
||||
|
|
|
@ -301,8 +301,7 @@ impl CommandHelper {
|
|||
let workspace = self.load_workspace()?;
|
||||
let op_head = self.resolve_operation(ui, workspace.repo_loader())?;
|
||||
let repo = workspace.repo_loader().load_at(&op_head)?;
|
||||
let loaded_at_head = self.global_args.at_operation == "@";
|
||||
WorkspaceCommandHelper::new(ui, self, workspace, repo, loaded_at_head)
|
||||
WorkspaceCommandHelper::new(ui, self, workspace, repo, self.is_at_head_operation())
|
||||
}
|
||||
|
||||
pub fn get_working_copy_factory(&self) -> Result<&dyn WorkingCopyFactory, CommandError> {
|
||||
|
@ -329,13 +328,26 @@ impl CommandHelper {
|
|||
.map_err(|err| map_workspace_load_error(err, self.global_args.repository.as_deref()))
|
||||
}
|
||||
|
||||
/// Returns true if the current operation is considered to be the head.
|
||||
pub fn is_at_head_operation(&self) -> bool {
|
||||
// TODO: should we accept --at-op=<head_id> as the head op? or should we
|
||||
// make --at-op=@ imply --ignore-workign-copy (i.e. not at the head.)
|
||||
matches!(self.global_args.at_operation.as_deref(), None | Some("@"))
|
||||
}
|
||||
|
||||
/// Resolves the current operation from the command-line argument.
|
||||
///
|
||||
/// If no `--at-operation` is specified, the head operations will be
|
||||
/// loaded. If there are multiple heads, they'll be merged.
|
||||
#[instrument(skip_all)]
|
||||
pub fn resolve_operation(
|
||||
&self,
|
||||
ui: &mut Ui,
|
||||
repo_loader: &RepoLoader,
|
||||
) -> Result<Operation, CommandError> {
|
||||
if self.global_args.at_operation == "@" {
|
||||
if let Some(op_str) = &self.global_args.at_operation {
|
||||
Ok(op_walk::resolve_op_for_load(repo_loader, op_str)?)
|
||||
} else {
|
||||
op_heads_store::resolve_op_heads(
|
||||
repo_loader.op_heads_store().as_ref(),
|
||||
repo_loader.op_store(),
|
||||
|
@ -366,10 +378,6 @@ impl CommandHelper {
|
|||
.clone())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let operation =
|
||||
op_walk::resolve_op_for_load(repo_loader, &self.global_args.at_operation)?;
|
||||
Ok(operation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2429,10 +2437,14 @@ pub struct GlobalArgs {
|
|||
/// Operation to load the repo at
|
||||
///
|
||||
/// Operation to load the repo at. By default, Jujutsu loads the repo at the
|
||||
/// most recent operation. You can use `--at-op=<operation ID>` to see what
|
||||
/// the repo looked like at an earlier operation. For example `jj
|
||||
/// --at-op=<operation ID> st` will show you what `jj st` would have
|
||||
/// shown you when the given operation had just finished.
|
||||
/// most recent operation, or at the merge of the concurrent operations if
|
||||
/// any.
|
||||
///
|
||||
/// You can use `--at-op=<operation ID>` to see what the repo looked like at
|
||||
/// an earlier operation. For example `jj --at-op=<operation ID> st` will
|
||||
/// show you what `jj st` would have shown you when the given operation had
|
||||
/// just finished. `--at-op=@` is pretty much the same as the default except
|
||||
/// that concurrent operations will never be merged.
|
||||
///
|
||||
/// Use `jj op log` to find the operation ID you want. Any unambiguous
|
||||
/// prefix of the operation ID is enough.
|
||||
|
@ -2444,8 +2456,8 @@ pub struct GlobalArgs {
|
|||
/// earlier operation. Doing that is equivalent to having run concurrent
|
||||
/// commands starting at the earlier operation. There's rarely a reason to
|
||||
/// do that, but it is possible.
|
||||
#[arg(long, visible_alias = "at-op", global = true, default_value = "@")]
|
||||
pub at_operation: String,
|
||||
#[arg(long, visible_alias = "at-op", global = true)]
|
||||
pub at_operation: Option<String>,
|
||||
/// Enable debug logging
|
||||
#[arg(long, global = true)]
|
||||
pub debug: bool,
|
||||
|
|
|
@ -35,7 +35,10 @@ pub fn cmd_debug_index(
|
|||
// merge concurrent operations and update the index.
|
||||
let workspace = command.load_workspace()?;
|
||||
let repo_loader = workspace.repo_loader();
|
||||
let op = op_walk::resolve_op_for_load(repo_loader, &command.global_args().at_operation)?;
|
||||
let op = op_walk::resolve_op_for_load(
|
||||
repo_loader,
|
||||
command.global_args().at_operation.as_deref().unwrap_or("@"),
|
||||
)?;
|
||||
let index_store = repo_loader.index_store();
|
||||
let index = index_store
|
||||
.get_index_at_op(&op, repo_loader.store())
|
||||
|
|
|
@ -35,7 +35,10 @@ pub fn cmd_debug_reindex(
|
|||
// be rebuilt while loading the repo.
|
||||
let workspace = command.load_workspace()?;
|
||||
let repo_loader = workspace.repo_loader();
|
||||
let op = op_walk::resolve_op_for_load(repo_loader, &command.global_args().at_operation)?;
|
||||
let op = op_walk::resolve_op_for_load(
|
||||
repo_loader,
|
||||
command.global_args().at_operation.as_deref().unwrap_or("@"),
|
||||
)?;
|
||||
let index_store = repo_loader.index_store();
|
||||
if let Some(default_index_store) = index_store.as_any().downcast_ref::<DefaultIndexStore>() {
|
||||
default_index_store.reinit().map_err(internal_error)?;
|
||||
|
|
|
@ -85,7 +85,7 @@ pub fn cmd_git_clone(
|
|||
command: &CommandHelper,
|
||||
args: &GitCloneArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
if command.global_args().at_operation != "@" {
|
||||
if command.global_args().at_operation.is_some() {
|
||||
return Err(cli_error("--at-op is not respected"));
|
||||
}
|
||||
let remote_name = "origin";
|
||||
|
|
|
@ -76,7 +76,7 @@ pub fn cmd_git_init(
|
|||
if command.global_args().ignore_working_copy {
|
||||
return Err(cli_error("--ignore-working-copy is not respected"));
|
||||
}
|
||||
if command.global_args().at_operation != "@" {
|
||||
if command.global_args().at_operation.is_some() {
|
||||
return Err(cli_error("--at-op is not respected"));
|
||||
}
|
||||
let cwd = command.cwd();
|
||||
|
|
|
@ -55,7 +55,7 @@ pub(crate) fn cmd_init(
|
|||
if command.global_args().ignore_working_copy {
|
||||
return Err(cli_error("--ignore-working-copy is not respected"));
|
||||
}
|
||||
if command.global_args().at_operation != "@" {
|
||||
if command.global_args().at_operation.is_some() {
|
||||
return Err(cli_error("--at-op is not respected"));
|
||||
}
|
||||
let cwd = command.cwd();
|
||||
|
|
|
@ -53,11 +53,10 @@ pub fn cmd_op_abandon(
|
|||
let op_store = repo_loader.op_store();
|
||||
// It doesn't make sense to create concurrent operations that will be merged
|
||||
// with the current head.
|
||||
let head_op_str = &command.global_args().at_operation;
|
||||
if head_op_str != "@" {
|
||||
if command.global_args().at_operation.is_some() {
|
||||
return Err(cli_error("--at-op is not respected"));
|
||||
}
|
||||
let current_head_op = op_walk::resolve_op_for_load(repo_loader, head_op_str)?;
|
||||
let current_head_op = op_walk::resolve_op_for_load(repo_loader, "@")?;
|
||||
let resolve_op = |op_str| op_walk::resolve_op_at(op_store, ¤t_head_op, op_str);
|
||||
let (abandon_root_op, abandon_head_op) =
|
||||
if let Some((root_op_str, head_op_str)) = args.operation.split_once("..") {
|
||||
|
|
|
@ -54,7 +54,7 @@ pub fn cmd_op_log(
|
|||
// operation id to be abandoned.
|
||||
let workspace = command.load_workspace()?;
|
||||
let repo_loader = workspace.repo_loader();
|
||||
let head_op_str = &command.global_args().at_operation;
|
||||
let head_op_str = command.global_args().at_operation.as_deref().unwrap_or("@");
|
||||
let head_ops = if head_op_str == "@" {
|
||||
// If multiple head ops can't be resolved without merging, let the
|
||||
// current op be empty. Beware that resolve_op_for_load() will eliminate
|
||||
|
|
|
@ -173,7 +173,7 @@ fn cmd_util_gc(
|
|||
command: &CommandHelper,
|
||||
args: &UtilGcArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
if command.global_args().at_operation != "@" {
|
||||
if !command.is_at_head_operation() {
|
||||
return Err(user_error(
|
||||
"Cannot garbage collect from a non-head operation",
|
||||
));
|
||||
|
|
|
@ -166,15 +166,15 @@ To get started, see the tutorial at https://github.com/martinvonz/jj/blob/main/d
|
|||
This option only affects the check. It does not affect the `immutable_heads()` revset or the `immutable` template keyword.
|
||||
* `--at-operation <AT_OPERATION>` — Operation to load the repo at
|
||||
|
||||
Operation to load the repo at. By default, Jujutsu loads the repo at the most recent operation. You can use `--at-op=<operation ID>` to see what the repo looked like at an earlier operation. For example `jj --at-op=<operation ID> st` will show you what `jj st` would have shown you when the given operation had just finished.
|
||||
Operation to load the repo at. By default, Jujutsu loads the repo at the most recent operation, or at the merge of the concurrent operations if any.
|
||||
|
||||
You can use `--at-op=<operation ID>` to see what the repo looked like at an earlier operation. For example `jj --at-op=<operation ID> st` will show you what `jj st` would have shown you when the given operation had just finished. `--at-op=@` is pretty much the same as the default except that concurrent operations will never be merged.
|
||||
|
||||
Use `jj op log` to find the operation ID you want. Any unambiguous prefix of the operation ID is enough.
|
||||
|
||||
When loading the repo at an earlier operation, the working copy will be ignored, as if `--ignore-working-copy` had been specified.
|
||||
|
||||
It is possible to run mutating commands when loading the repo at an earlier operation. Doing that is equivalent to having run concurrent commands starting at the earlier operation. There's rarely a reason to do that, but it is possible.
|
||||
|
||||
Default value: `@`
|
||||
* `--debug` — Enable debug logging
|
||||
* `--color <WHEN>` — When to colorize output (always, never, debug, auto)
|
||||
* `--quiet` — Silence non-primary command output
|
||||
|
|
|
@ -30,6 +30,13 @@ fn test_concurrent_operation_divergence() {
|
|||
&["describe", "-m", "message 2", "--at-op", "@-"],
|
||||
);
|
||||
|
||||
// "--at-op=@" disables op heads merging
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "--at-op=@"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: The "@" expression resolved to more than one operation
|
||||
Hint: Try specifying one of the operations by ID: e31015019d90, 48f4a48f3f70
|
||||
"###);
|
||||
|
||||
// "op log" doesn't merge the concurrent operations
|
||||
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
|
|
|
@ -607,7 +607,7 @@ fn test_help() {
|
|||
-R, --repository <REPOSITORY> Path to repository to operate on
|
||||
--ignore-working-copy Don't snapshot the working copy, and don't update it
|
||||
--ignore-immutable Allow rewriting immutable commits
|
||||
--at-operation <AT_OPERATION> Operation to load the repo at [default: @] [aliases: at-op]
|
||||
--at-operation <AT_OPERATION> Operation to load the repo at [aliases: at-op]
|
||||
--debug Enable debug logging
|
||||
--color <WHEN> When to colorize output (always, never, debug, auto)
|
||||
--quiet Silence non-primary command output
|
||||
|
|
Loading…
Reference in a new issue