use std::io;
use std::io::Write as _;
use jj_lib::repo_path::RepoPathUiConverter;
use jj_lib::working_copy::SnapshotStats;
use jj_lib::working_copy::UntrackedReason;
use tracing::instrument;
use crate::cli_util::CommandHelper;
use crate::cli_util::print_large_file_hint;
use crate::cli_util::print_untracked_files;
use crate::command_error::CommandError;
use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct FileTrackArgs {
#[arg(required = true, value_name = "FILESETS", value_hint = clap::ValueHint::AnyPath)]
paths: Vec<String>,
#[arg(long)]
include_ignored: bool,
}
#[instrument(skip_all)]
pub(crate) async fn cmd_file_track(
ui: &mut Ui,
command: &CommandHelper,
args: &FileTrackArgs,
) -> Result<(), CommandError> {
let (mut workspace_command, auto_stats) = command.workspace_helper_with_stats(ui)?;
let matcher = workspace_command
.parse_file_patterns(ui, &args.paths)?
.to_matcher();
let mut options = workspace_command.snapshot_options_with_start_tracking_matcher(&matcher)?;
if args.include_ignored {
options.force_tracking_matcher = &matcher;
}
let mut tx = workspace_command.start_transaction().into_inner();
let (mut locked_ws, _wc_commit) = workspace_command.start_working_copy_mutation().await?;
let (_tree, track_stats) = locked_ws.locked_wc().snapshot(&options).await?;
let num_rebased = tx.repo_mut().rebase_descendants().await?;
if num_rebased > 0 {
writeln!(ui.status(), "Rebased {num_rebased} descendant commits")?;
}
let repo = tx.commit("track paths").await?;
locked_ws.finish(repo.op_id().clone()).await?;
print_track_snapshot_stats(
ui,
auto_stats,
track_stats,
workspace_command.env().path_converter(),
)?;
Ok(())
}
pub fn print_track_snapshot_stats(
ui: &Ui,
mut auto_stats: SnapshotStats,
track_stats: SnapshotStats,
path_converter: &RepoPathUiConverter,
) -> io::Result<()> {
let mut untracked_paths = track_stats.untracked_paths;
for (path, reason) in &mut untracked_paths {
if !matches!(reason, UntrackedReason::FileNotAutoTracked) {
continue;
}
if let Some(old_reason) = auto_stats.untracked_paths.remove(path) {
*reason = old_reason;
}
}
print_untracked_files(ui, &untracked_paths, path_converter)?;
let (large_files, sizes): (Vec<_>, Vec<_>) = untracked_paths
.iter()
.filter_map(|(path, reason)| match reason {
UntrackedReason::FileTooLarge { size, .. } => Some((path, *size)),
UntrackedReason::FileNotAutoTracked => None,
})
.unzip();
if let Some(size) = sizes.iter().max() {
let large_files_list: Vec<_> = large_files
.iter()
.map(|path| path_converter.format_file_path(path))
.collect();
print_large_file_hint(ui, *size, Some(&large_files_list))?;
}
Ok(())
}