forked from mirrors/jj
cli: run "op log" without loading repo or merging concurrent ops
When debugging behavior of badly-GCed repos, I find it's annoying that "op log" fails because the index can't be loaded. Since "op log" doesn't need a repo, I think it's better to display the exact op-heads state without merging.
This commit is contained in:
parent
831a530283
commit
543036c753
3 changed files with 47 additions and 13 deletions
|
@ -132,26 +132,45 @@ fn cmd_op_log(
|
||||||
command: &CommandHelper,
|
command: &CommandHelper,
|
||||||
args: &OperationLogArgs,
|
args: &OperationLogArgs,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
let workspace_command = command.workspace_helper(ui)?;
|
// Don't load the repo so that the operation history can be inspected even
|
||||||
let repo = workspace_command.repo();
|
// with a corrupted repo state. For example, you can find the first bad
|
||||||
let head_op = repo.operation().clone();
|
// operation id to be abandoned.
|
||||||
let head_op_id = head_op.id().clone();
|
let workspace = command.load_workspace()?;
|
||||||
|
let repo_loader = workspace.repo_loader();
|
||||||
|
let head_op_str = &command.global_args().at_operation;
|
||||||
|
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
|
||||||
|
// redundant heads whereas get_current_head_ops() won't.
|
||||||
|
let current_op = op_walk::resolve_op_for_load(repo_loader, head_op_str).ok();
|
||||||
|
if let Some(op) = current_op {
|
||||||
|
vec![op]
|
||||||
|
} else {
|
||||||
|
op_walk::get_current_head_ops(
|
||||||
|
repo_loader.op_store(),
|
||||||
|
repo_loader.op_heads_store().as_ref(),
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec![op_walk::resolve_op_for_load(repo_loader, head_op_str)?]
|
||||||
|
};
|
||||||
|
let current_op_id = match &*head_ops {
|
||||||
|
[op] => Some(op.id()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
let template_string = match &args.template {
|
let template_string = match &args.template {
|
||||||
Some(value) => value.to_owned(),
|
Some(value) => value.to_owned(),
|
||||||
None => command.settings().config().get_string("templates.op_log")?,
|
None => command.settings().config().get_string("templates.op_log")?,
|
||||||
};
|
};
|
||||||
let template = operation_templater::parse(
|
let template_aliases = command.load_template_aliases(ui)?;
|
||||||
Some(repo.op_id()),
|
let template = operation_templater::parse(current_op_id, &template_string, &template_aliases)?;
|
||||||
&template_string,
|
|
||||||
workspace_command.template_aliases_map(),
|
|
||||||
)?;
|
|
||||||
let with_content_format = LogContentFormat::new(ui, command.settings())?;
|
let with_content_format = LogContentFormat::new(ui, command.settings())?;
|
||||||
|
|
||||||
ui.request_pager();
|
ui.request_pager();
|
||||||
let mut formatter = ui.stdout_formatter();
|
let mut formatter = ui.stdout_formatter();
|
||||||
let formatter = formatter.as_mut();
|
let formatter = formatter.as_mut();
|
||||||
let iter = op_walk::walk_ancestors(&[head_op]).take(args.limit.unwrap_or(usize::MAX));
|
let iter = op_walk::walk_ancestors(&head_ops).take(args.limit.unwrap_or(usize::MAX));
|
||||||
if !args.no_graph {
|
if !args.no_graph {
|
||||||
let mut graph = get_graphlog(command.settings(), formatter.raw());
|
let mut graph = get_graphlog(command.settings(), formatter.raw());
|
||||||
let default_node_symbol = graph.default_node_symbol().to_owned();
|
let default_node_symbol = graph.default_node_symbol().to_owned();
|
||||||
|
@ -161,7 +180,7 @@ fn cmd_op_log(
|
||||||
for id in op.parent_ids() {
|
for id in op.parent_ids() {
|
||||||
edges.push(Edge::direct(id.clone()));
|
edges.push(Edge::direct(id.clone()));
|
||||||
}
|
}
|
||||||
let is_head_op = op.id() == &head_op_id;
|
let is_current_op = Some(op.id()) == current_op_id;
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
with_content_format.write_graph_text(
|
with_content_format.write_graph_text(
|
||||||
ui.new_formatter(&mut buffer).as_mut(),
|
ui.new_formatter(&mut buffer).as_mut(),
|
||||||
|
@ -173,7 +192,7 @@ fn cmd_op_log(
|
||||||
if !buffer.ends_with(b"\n") {
|
if !buffer.ends_with(b"\n") {
|
||||||
buffer.push(b'\n');
|
buffer.push(b'\n');
|
||||||
}
|
}
|
||||||
let node_symbol = if is_head_op {
|
let node_symbol = if is_current_op {
|
||||||
"@"
|
"@"
|
||||||
} else {
|
} else {
|
||||||
&default_node_symbol
|
&default_node_symbol
|
||||||
|
|
|
@ -32,6 +32,21 @@ fn test_concurrent_operation_divergence() {
|
||||||
&["describe", "-m", "message 2", "--at-op", "@-"],
|
&["describe", "-m", "message 2", "--at-op", "@-"],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// "op log" doesn't merge the concurrent operations
|
||||||
|
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
|
||||||
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
◉ 94fce7319d45 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
|
||||||
|
│ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
|
||||||
|
│ args: jj describe -m 'message 2' --at-op @-
|
||||||
|
│ ◉ 15e35d1e9190 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
|
||||||
|
├─╯ describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
|
||||||
|
│ args: jj describe -m 'message 1'
|
||||||
|
◉ 19b8089fc78b 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'
|
||||||
|
◉ f1c462c494be 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
|
||||||
|
"###);
|
||||||
|
|
||||||
// We should be informed about the concurrent modification
|
// We should be informed about the concurrent modification
|
||||||
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", "description"]);
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", "description"]);
|
||||||
insta::assert_snapshot!(stdout, @r###"
|
insta::assert_snapshot!(stdout, @r###"
|
||||||
|
|
|
@ -158,7 +158,7 @@ fn resolve_single_op_from_store(
|
||||||
|
|
||||||
/// Loads the current head operations. The returned operations may contain
|
/// Loads the current head operations. The returned operations may contain
|
||||||
/// redundant ones which are ancestors of the other heads.
|
/// redundant ones which are ancestors of the other heads.
|
||||||
fn get_current_head_ops(
|
pub fn get_current_head_ops(
|
||||||
op_store: &Arc<dyn OpStore>,
|
op_store: &Arc<dyn OpStore>,
|
||||||
op_heads_store: &dyn OpHeadsStore,
|
op_heads_store: &dyn OpHeadsStore,
|
||||||
) -> OpStoreResult<Vec<Operation>> {
|
) -> OpStoreResult<Vec<Operation>> {
|
||||||
|
|
Loading…
Reference in a new issue