use itertools::Itertools;
use jj_lib::matchers::EverythingMatcher;
use jj_lib::repo::Repo;
use jj_lib::rewrite::merge_commit_trees;
use tracing::instrument;
use super::resolve;
use crate::cli_util::{CommandError, CommandHelper};
use crate::diff_util;
use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)]
#[command(visible_alias = "st")]
pub(crate) struct StatusArgs {}
#[instrument(skip_all)]
pub(crate) fn cmd_status(
ui: &mut Ui,
command: &CommandHelper,
_args: &StatusArgs,
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo();
let maybe_wc_commit = workspace_command
.get_wc_commit_id()
.map(|id| repo.store().get_commit(id))
.transpose()?;
ui.request_pager();
let mut formatter = ui.stdout_formatter();
let formatter = formatter.as_mut();
if let Some(wc_commit) = &maybe_wc_commit {
let parent_tree = merge_commit_trees(repo.as_ref(), &wc_commit.parents())?;
let tree = wc_commit.tree()?;
if tree.id() == parent_tree.id() {
formatter.write_str("The working copy is clean\n")?;
} else {
formatter.write_str("Working copy changes:\n")?;
diff_util::show_diff_summary(
formatter,
&workspace_command,
parent_tree.diff_stream(&tree, &EverythingMatcher),
)?;
}
let conflicts = wc_commit.tree()?.conflicts().collect_vec();
if !conflicts.is_empty() {
writeln!(
formatter.labeled("conflict"),
"There are unresolved conflicts at these paths:"
)?;
resolve::print_conflicted_paths(&conflicts, formatter, &workspace_command)?
}
formatter.write_str("Working copy : ")?;
formatter.with_label("working_copy", |fmt| {
workspace_command.write_commit_summary(fmt, wc_commit)
})?;
formatter.write_str("\n")?;
for parent in wc_commit.parents() {
formatter.write_str("Parent commit: ")?;
workspace_command.write_commit_summary(formatter, &parent)?;
formatter.write_str("\n")?;
}
} else {
formatter.write_str("No working copy\n")?;
}
let conflicted_local_branches = repo
.view()
.local_branches()
.filter(|(_, target)| target.has_conflict())
.map(|(branch_name, _)| branch_name)
.collect_vec();
let conflicted_remote_branches = repo
.view()
.all_remote_branches()
.filter(|(_, remote_ref)| remote_ref.target.has_conflict())
.map(|(full_name, _)| full_name)
.collect_vec();
if !conflicted_local_branches.is_empty() {
writeln!(
formatter.labeled("conflict"),
"These branches have conflicts:"
)?;
for branch_name in conflicted_local_branches {
write!(formatter, " ")?;
write!(formatter.labeled("branch"), "{branch_name}")?;
writeln!(formatter)?;
}
writeln!(
formatter,
" Use `jj branch list` to see details. Use `jj branch set <name> -r <rev>` to \
resolve."
)?;
}
if !conflicted_remote_branches.is_empty() {
writeln!(
formatter.labeled("conflict"),
"These remote branches have conflicts:"
)?;
for (branch_name, remote_name) in conflicted_remote_branches {
write!(formatter, " ")?;
write!(formatter.labeled("branch"), "{branch_name}@{remote_name}")?;
writeln!(formatter)?;
}
writeln!(
formatter,
" Use `jj branch list` to see details. Use `jj git fetch` to resolve."
)?;
}
Ok(())
}