// Copyright 2022 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use std::collections::{HashMap, HashSet, VecDeque};use std::env::{self, ArgsOs, VarError};use std::ffi::{OsStr, OsString};use std::fmt::Debug;use std::io::Write as _;use std::ops::Deref;use std::path::{Path, PathBuf};use std::process::ExitCode;use std::rc::Rc;use std::str::FromStr;use std::sync::Arc;use std::time::SystemTime;use std::{iter, str}; use clap::builder::{NonEmptyStringValueParser, TypedValueParser, ValueParserFactory};use clap::{Arg, ArgAction, ArgMatches, Command, FromArgMatches};use indexmap::IndexSet;use itertools::Itertools;use jj_lib::backend::{BackendError, ChangeId, CommitId, MergedTreeId, ObjectId};use jj_lib::commit::Commit;use jj_lib::git::{ FailedRefExport, FailedRefExportReason, GitConfigParseError, GitExportError, GitImportError, GitImportStats, GitRemoteManagementError,};use jj_lib::git_backend::GitBackend;use jj_lib::gitignore::GitIgnoreFile;use jj_lib::hex_util::to_reverse_hex;use jj_lib::id_prefix::IdPrefixContext;use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher};use jj_lib::merged_tree::MergedTree;use jj_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};use jj_lib::op_store::{OpStore, OpStoreError, OperationId, WorkspaceId};use jj_lib::operation::Operation;use jj_lib::repo::{ CheckOutCommitError, EditCommitError, MutableRepo, ReadonlyRepo, Repo, RepoLoader, RepoLoaderError, RewriteRootCommit, StoreFactories, StoreLoadError,};use jj_lib::repo_path::{FsPathParseError, RepoPath};use jj_lib::revset::{ DefaultSymbolResolver, Revset, RevsetAliasesMap, RevsetCommitRef, RevsetEvaluationError, RevsetExpression, RevsetIteratorExt, RevsetParseContext, RevsetParseError, RevsetParseErrorKind, RevsetResolutionError, RevsetWorkspaceContext,};use jj_lib::rewrite::restore_tree;use jj_lib::settings::{ConfigResultExt as _, UserSettings};use jj_lib::str_util::{StringPattern, StringPatternParseError};use jj_lib::transaction::Transaction;use jj_lib::tree::TreeMergeError;use jj_lib::working_copy::{ CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, SnapshotOptions, WorkingCopy, WorkingCopyStateError,};use jj_lib::workspace::{ default_working_copy_factories, LockedWorkspace, WorkingCopyFactory, Workspace, WorkspaceInitError, WorkspaceLoadError, WorkspaceLoader,};use jj_lib::{dag_walk, file_util, git, revset};use once_cell::unsync::OnceCell;use toml_edit;use tracing::instrument;use tracing_chrome::ChromeLayerBuilder;use tracing_subscriber::prelude::*; use crate::config::{ new_config_path, AnnotatedValue, CommandNameAndArgs, ConfigSource, LayeredConfigs,};use crate::formatter::{FormatRecorder, Formatter, PlainTextFormatter};use crate::merge_tools::{ConflictResolveError, DiffEditError, DiffGenerateError};use crate::template_parser::{TemplateAliasesMap, TemplateParseError};use crate::templater::Template;use crate::ui::{ColorChoice, Ui};use crate::{commit_templater, text_util}; #[derive(Clone, Debug)]pub enum CommandError { UserError { message: String, hint: Option<String>, }, ConfigError(String), /// Invalid command line CliError(String), /// Invalid command line detected by clap ClapCliError(Arc<clap::Error>), BrokenPipe, InternalError(String),} pub fn user_error(message: impl Into<String>) -> CommandError { CommandError::UserError { message: message.into(), hint: None, }}pub fn user_error_with_hint(message: impl Into<String>, hint: impl Into<String>) -> CommandError { CommandError::UserError { message: message.into(), hint: Some(hint.into()), }} fn format_similarity_hint<S: AsRef<str>>(candidates: &[S]) -> Option<String> { match candidates { [] => None, names => { let quoted_names = names .iter() .map(|s| format!(r#""{}""#, s.as_ref())) .join(", "); Some(format!("Did you mean {quoted_names}?")) } }} impl From<std::io::Error> for CommandError { fn from(err: std::io::Error) -> Self { if err.kind() == std::io::ErrorKind::BrokenPipe { CommandError::BrokenPipe } else { // TODO: Record the error as a chained cause CommandError::InternalError(format!("I/O error: {err}")) } }} impl From<config::ConfigError> for CommandError { fn from(err: config::ConfigError) -> Self { CommandError::ConfigError(err.to_string()) }} impl From<crate::config::ConfigError> for CommandError { fn from(err: crate::config::ConfigError) -> Self { CommandError::ConfigError(err.to_string()) }} impl From<RewriteRootCommit> for CommandError { fn from(err: RewriteRootCommit) -> Self { CommandError::InternalError(format!("Attempted to rewrite the root commit: {err}")) }} impl From<EditCommitError> for CommandError { fn from(err: EditCommitError) -> Self { CommandError::InternalError(format!("Failed to edit a commit: {err}")) }} impl From<CheckOutCommitError> for CommandError { fn from(err: CheckOutCommitError) -> Self { CommandError::InternalError(format!("Failed to check out a commit: {err}")) }} impl From<BackendError> for CommandError { fn from(err: BackendError) -> Self { user_error(format!("Unexpected error from backend: {err}")) }} impl From<WorkspaceInitError> for CommandError { fn from(err: WorkspaceInitError) -> Self { match err { WorkspaceInitError::DestinationExists(_) => { user_error("The target repo already exists") } WorkspaceInitError::NonUnicodePath => { user_error("The target repo path contains non-unicode characters") } WorkspaceInitError::CheckOutCommit(err) => CommandError::InternalError(format!( "Failed to check out the initial commit: {err}" )), WorkspaceInitError::Path(err) => { CommandError::InternalError(format!("Failed to access the repository: {err}")) } WorkspaceInitError::Backend(err) => { user_error(format!("Failed to access the repository: {err}")) } WorkspaceInitError::WorkingCopyState(err) => { CommandError::InternalError(format!("Failed to access the repository: {err}")) } } }} impl From<OpHeadResolutionError<CommandError>> for CommandError { fn from(err: OpHeadResolutionError<CommandError>) -> Self { match err { OpHeadResolutionError::NoHeads => CommandError::InternalError( "Corrupt repository: there are no operations".to_string(), ), OpHeadResolutionError::OpStore(err) => err.into(), OpHeadResolutionError::Err(e) => e, } }} impl From<SnapshotError> for CommandError { fn from(err: SnapshotError) -> Self { match err { SnapshotError::NewFileTooLarge { .. } => user_error_with_hint( format!("Failed to snapshot the working copy: {err}"), r#"Increase the value of the `snapshot.max-new-file-size` config option if youwant this file to be snapshotted. Otherwise add it to your `.gitignore` file."#, ), err => { CommandError::InternalError(format!("Failed to snapshot the working copy: {err}")) } } }} impl From<TreeMergeError> for CommandError { fn from(err: TreeMergeError) -> Self { CommandError::InternalError(format!("Merge failed: {err}")) }} impl From<OpStoreError> for CommandError { fn from(err: OpStoreError) -> Self { CommandError::InternalError(format!("Failed to load an operation: {err}")) }} impl From<RepoLoaderError> for CommandError { fn from(err: RepoLoaderError) -> Self { CommandError::InternalError(format!("Failed to load the repo: {err}")) }} impl From<ResetError> for CommandError { fn from(_: ResetError) -> Self { CommandError::InternalError("Failed to reset the working copy".to_string()) }} impl From<DiffEditError> for CommandError { fn from(err: DiffEditError) -> Self { user_error(format!("Failed to edit diff: {err}")) }} impl From<DiffGenerateError> for CommandError { fn from(err: DiffGenerateError) -> Self { user_error(format!("Failed to generate diff: {err}")) }} impl From<ConflictResolveError> for CommandError { fn from(err: ConflictResolveError) -> Self { user_error(format!("Failed to resolve conflicts: {err}")) }} impl From<git2::Error> for CommandError { fn from(err: git2::Error) -> Self { user_error(format!("Git operation failed: {err}")) }} impl From<GitImportError> for CommandError { fn from(err: GitImportError) -> Self { let message = format!("Failed to import refs from underlying Git repo: {err}"); let hint = match &err { GitImportError::MissingHeadTarget { .. } | GitImportError::MissingRefAncestor { .. } => Some( "\Is this Git repository a shallow or partial clone (cloned with the --depth or --filter \ argument)?jj currently does not support shallow/partial clones. To use jj with this \ repository, tryunshallowing the repository (https://stackoverflow.com/q/6802145) or re-cloning with the fullrepository contents." .to_string(), ), GitImportError::RemoteReservedForLocalGitRepo => { Some("Run `jj git remote rename` to give different name.".to_string()) } GitImportError::InternalGitError(_) => None, GitImportError::UnexpectedBackend => None, }; CommandError::UserError { message, hint } }} impl From<GitExportError> for CommandError { fn from(err: GitExportError) -> Self { CommandError::InternalError(format!( "Failed to export refs to underlying Git repo: {err}" )) }} impl From<GitRemoteManagementError> for CommandError { fn from(err: GitRemoteManagementError) -> Self { user_error(format!("{err}")) }} impl From<RevsetEvaluationError> for CommandError { fn from(err: RevsetEvaluationError) -> Self { user_error(format!("{err}")) }} impl From<RevsetParseError> for CommandError { fn from(err: RevsetParseError) -> Self { let message = iter::successors(Some(&err), |e| e.origin()).join("\n"); // Only for the top-level error as we can't attach hint to inner errors let hint = match err.kind() { RevsetParseErrorKind::NotPostfixOperator { op: _, similar_op, description, } | RevsetParseErrorKind::NotInfixOperator { op: _, similar_op, description, } => Some(format!("Did you mean '{similar_op}' for {description}?")), RevsetParseErrorKind::NoSuchFunction { name: _, candidates, } => format_similarity_hint(candidates), _ => None, }; CommandError::UserError { message: format!("Failed to parse revset: {message}"), hint, } }} impl From<RevsetResolutionError> for CommandError { fn from(err: RevsetResolutionError) -> Self { let hint = match &err { RevsetResolutionError::NoSuchRevision { name: _, candidates, } => format_similarity_hint(candidates), RevsetResolutionError::EmptyString | RevsetResolutionError::WorkspaceMissingWorkingCopy { .. } | RevsetResolutionError::AmbiguousCommitIdPrefix(_) | RevsetResolutionError::AmbiguousChangeIdPrefix(_) | RevsetResolutionError::StoreError(_) => None, }; CommandError::UserError { message: format!("{err}"), hint, } }} impl From<TemplateParseError> for CommandError { fn from(err: TemplateParseError) -> Self { let message = iter::successors(Some(&err), |e| e.origin()).join("\n"); user_error(format!("Failed to parse template: {message}")) }} impl From<FsPathParseError> for CommandError { fn from(err: FsPathParseError) -> Self { user_error(format!("{err}")) }} impl From<clap::Error> for CommandError { fn from(err: clap::Error) -> Self { CommandError::ClapCliError(Arc::new(err)) }} impl From<GitConfigParseError> for CommandError { fn from(err: GitConfigParseError) -> Self { CommandError::InternalError(format!("Failed to parse Git config: {err} ")) }} impl From<WorkingCopyStateError> for CommandError { fn from(err: WorkingCopyStateError) -> Self { CommandError::InternalError(format!("Failed to access working copy state: {err}")) }} #[derive(Clone)]struct ChromeTracingFlushGuard { _inner: Option<Rc<tracing_chrome::FlushGuard>>,} impl Debug for ChromeTracingFlushGuard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { _inner } = self; f.debug_struct("ChromeTracingFlushGuard") .finish_non_exhaustive() }} /// Handle to initialize or change tracing subscription.#[derive(Clone, Debug)]pub struct TracingSubscription { reload_log_filter: tracing_subscriber::reload::Handle< tracing_subscriber::EnvFilter, tracing_subscriber::Registry, >, _chrome_tracing_flush_guard: ChromeTracingFlushGuard,} impl TracingSubscription { /// Initializes tracing with the default configuration. This should be /// called as early as possible. pub fn init() -> Self { let filter = tracing_subscriber::EnvFilter::builder() .with_default_directive(tracing::metadata::LevelFilter::ERROR.into()) .from_env_lossy(); let (filter, reload_log_filter) = tracing_subscriber::reload::Layer::new(filter); let (chrome_tracing_layer, chrome_tracing_flush_guard) = match std::env::var("JJ_TRACE") { Ok(filename) => { let filename = if filename.is_empty() { format!( "jj-trace-{}.json", SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), ) } else { filename }; let include_args = std::env::var("JJ_TRACE_INCLUDE_ARGS").is_ok(); let (layer, guard) = ChromeLayerBuilder::new() .file(filename) .include_args(include_args) .build(); ( Some(layer), ChromeTracingFlushGuard { _inner: Some(Rc::new(guard)), }, ) } Err(_) => (None, ChromeTracingFlushGuard { _inner: None }), }; tracing_subscriber::registry() .with( tracing_subscriber::fmt::Layer::default() .with_writer(std::io::stderr) .with_filter(filter), ) .with(chrome_tracing_layer) .init(); TracingSubscription { reload_log_filter, _chrome_tracing_flush_guard: chrome_tracing_flush_guard, } } pub fn enable_verbose_logging(&self) -> Result<(), CommandError> { self.reload_log_filter .modify(|filter| { *filter = tracing_subscriber::EnvFilter::builder() .with_default_directive(tracing::metadata::LevelFilter::DEBUG.into()) .from_env_lossy() }) .map_err(|err| { CommandError::InternalError(format!("failed to enable verbose logging: {err}")) })?; tracing::info!("verbose logging enabled"); Ok(()) }} pub struct CommandHelper { app: Command, cwd: PathBuf, string_args: Vec<String>, matches: ArgMatches, global_args: GlobalArgs, settings: UserSettings, layered_configs: LayeredConfigs, maybe_workspace_loader: Result<WorkspaceLoader, CommandError>, store_factories: StoreFactories, working_copy_factories: HashMap<String, WorkingCopyFactory>,Lines 1–500 of 2890. Load more
// Copyright 2022 The Jujutsu Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::collections::{HashMap, HashSet, VecDeque}; use std::env::{self, ArgsOs, VarError}; use std::ffi::{OsStr, OsString}; use std::fmt::Debug; use std::io::Write as _; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::ExitCode; use std::rc::Rc; use std::str::FromStr; use std::sync::Arc; use std::time::SystemTime; use std::{iter, str}; use clap::builder::{NonEmptyStringValueParser, TypedValueParser, ValueParserFactory}; use clap::{Arg, ArgAction, ArgMatches, Command, FromArgMatches}; use indexmap::IndexSet; use itertools::Itertools; use jj_lib::backend::{BackendError, ChangeId, CommitId, MergedTreeId, ObjectId}; use jj_lib::commit::Commit; use jj_lib::git::{ FailedRefExport, FailedRefExportReason, GitConfigParseError, GitExportError, GitImportError, GitImportStats, GitRemoteManagementError, }; use jj_lib::git_backend::GitBackend; use jj_lib::gitignore::GitIgnoreFile; use jj_lib::hex_util::to_reverse_hex; use jj_lib::id_prefix::IdPrefixContext; use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher}; use jj_lib::merged_tree::MergedTree; use jj_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore}; use jj_lib::op_store::{OpStore, OpStoreError, OperationId, WorkspaceId}; use jj_lib::operation::Operation; use jj_lib::repo::{ CheckOutCommitError, EditCommitError, MutableRepo, ReadonlyRepo, Repo, RepoLoader, RepoLoaderError, RewriteRootCommit, StoreFactories, StoreLoadError, }; use jj_lib::repo_path::{FsPathParseError, RepoPath}; use jj_lib::revset::{ DefaultSymbolResolver, Revset, RevsetAliasesMap, RevsetCommitRef, RevsetEvaluationError, RevsetExpression, RevsetIteratorExt, RevsetParseContext, RevsetParseError, RevsetParseErrorKind, RevsetResolutionError, RevsetWorkspaceContext, }; use jj_lib::rewrite::restore_tree; use jj_lib::settings::{ConfigResultExt as _, UserSettings}; use jj_lib::str_util::{StringPattern, StringPatternParseError}; use jj_lib::transaction::Transaction; use jj_lib::tree::TreeMergeError; use jj_lib::working_copy::{ CheckoutStats, LockedWorkingCopy, ResetError, SnapshotError, SnapshotOptions, WorkingCopy, WorkingCopyStateError, }; use jj_lib::workspace::{ default_working_copy_factories, LockedWorkspace, WorkingCopyFactory, Workspace, WorkspaceInitError, WorkspaceLoadError, WorkspaceLoader, }; use jj_lib::{dag_walk, file_util, git, revset}; use once_cell::unsync::OnceCell; use toml_edit; use tracing::instrument; use tracing_chrome::ChromeLayerBuilder; use tracing_subscriber::prelude::*; use crate::config::{ new_config_path, AnnotatedValue, CommandNameAndArgs, ConfigSource, LayeredConfigs, }; use crate::formatter::{FormatRecorder, Formatter, PlainTextFormatter}; use crate::merge_tools::{ConflictResolveError, DiffEditError, DiffGenerateError}; use crate::template_parser::{TemplateAliasesMap, TemplateParseError}; use crate::templater::Template; use crate::ui::{ColorChoice, Ui}; use crate::{commit_templater, text_util}; #[derive(Clone, Debug)] pub enum CommandError { UserError { message: String, hint: Option<String>, }, ConfigError(String), /// Invalid command line CliError(String), /// Invalid command line detected by clap ClapCliError(Arc<clap::Error>), BrokenPipe, InternalError(String), } pub fn user_error(message: impl Into<String>) -> CommandError { CommandError::UserError { message: message.into(), hint: None, } } pub fn user_error_with_hint(message: impl Into<String>, hint: impl Into<String>) -> CommandError { CommandError::UserError { message: message.into(), hint: Some(hint.into()), } } fn format_similarity_hint<S: AsRef<str>>(candidates: &[S]) -> Option<String> { match candidates { [] => None, names => { let quoted_names = names .iter() .map(|s| format!(r#""{}""#, s.as_ref())) .join(", "); Some(format!("Did you mean {quoted_names}?")) } } } impl From<std::io::Error> for CommandError { fn from(err: std::io::Error) -> Self { if err.kind() == std::io::ErrorKind::BrokenPipe { CommandError::BrokenPipe } else { // TODO: Record the error as a chained cause CommandError::InternalError(format!("I/O error: {err}")) } } } impl From<config::ConfigError> for CommandError { fn from(err: config::ConfigError) -> Self { CommandError::ConfigError(err.to_string()) } } impl From<crate::config::ConfigError> for CommandError { fn from(err: crate::config::ConfigError) -> Self { CommandError::ConfigError(err.to_string()) } } impl From<RewriteRootCommit> for CommandError { fn from(err: RewriteRootCommit) -> Self { CommandError::InternalError(format!("Attempted to rewrite the root commit: {err}")) } } impl From<EditCommitError> for CommandError { fn from(err: EditCommitError) -> Self { CommandError::InternalError(format!("Failed to edit a commit: {err}")) } } impl From<CheckOutCommitError> for CommandError { fn from(err: CheckOutCommitError) -> Self { CommandError::InternalError(format!("Failed to check out a commit: {err}")) } } impl From<BackendError> for CommandError { fn from(err: BackendError) -> Self { user_error(format!("Unexpected error from backend: {err}")) } } impl From<WorkspaceInitError> for CommandError { fn from(err: WorkspaceInitError) -> Self { match err { WorkspaceInitError::DestinationExists(_) => { user_error("The target repo already exists") } WorkspaceInitError::NonUnicodePath => { user_error("The target repo path contains non-unicode characters") } WorkspaceInitError::CheckOutCommit(err) => CommandError::InternalError(format!( "Failed to check out the initial commit: {err}" )), WorkspaceInitError::Path(err) => { CommandError::InternalError(format!("Failed to access the repository: {err}")) } WorkspaceInitError::Backend(err) => { user_error(format!("Failed to access the repository: {err}")) } WorkspaceInitError::WorkingCopyState(err) => { CommandError::InternalError(format!("Failed to access the repository: {err}")) } } } } impl From<OpHeadResolutionError<CommandError>> for CommandError { fn from(err: OpHeadResolutionError<CommandError>) -> Self { match err { OpHeadResolutionError::NoHeads => CommandError::InternalError( "Corrupt repository: there are no operations".to_string(), ), OpHeadResolutionError::OpStore(err) => err.into(), OpHeadResolutionError::Err(e) => e, } } } impl From<SnapshotError> for CommandError { fn from(err: SnapshotError) -> Self { match err { SnapshotError::NewFileTooLarge { .. } => user_error_with_hint( format!("Failed to snapshot the working copy: {err}"), r#"Increase the value of the `snapshot.max-new-file-size` config option if you want this file to be snapshotted. Otherwise add it to your `.gitignore` file."#, ), err => { CommandError::InternalError(format!("Failed to snapshot the working copy: {err}")) } } } } impl From<TreeMergeError> for CommandError { fn from(err: TreeMergeError) -> Self { CommandError::InternalError(format!("Merge failed: {err}")) } } impl From<OpStoreError> for CommandError { fn from(err: OpStoreError) -> Self { CommandError::InternalError(format!("Failed to load an operation: {err}")) } } impl From<RepoLoaderError> for CommandError { fn from(err: RepoLoaderError) -> Self { CommandError::InternalError(format!("Failed to load the repo: {err}")) } } impl From<ResetError> for CommandError { fn from(_: ResetError) -> Self { CommandError::InternalError("Failed to reset the working copy".to_string()) } } impl From<DiffEditError> for CommandError { fn from(err: DiffEditError) -> Self { user_error(format!("Failed to edit diff: {err}")) } } impl From<DiffGenerateError> for CommandError { fn from(err: DiffGenerateError) -> Self { user_error(format!("Failed to generate diff: {err}")) } } impl From<ConflictResolveError> for CommandError { fn from(err: ConflictResolveError) -> Self { user_error(format!("Failed to resolve conflicts: {err}")) } } impl From<git2::Error> for CommandError { fn from(err: git2::Error) -> Self { user_error(format!("Git operation failed: {err}")) } } impl From<GitImportError> for CommandError { fn from(err: GitImportError) -> Self { let message = format!("Failed to import refs from underlying Git repo: {err}"); let hint = match &err { GitImportError::MissingHeadTarget { .. } | GitImportError::MissingRefAncestor { .. } => Some( "\ Is this Git repository a shallow or partial clone (cloned with the --depth or --filter \ argument)? jj currently does not support shallow/partial clones. To use jj with this \ repository, try unshallowing the repository (https://stackoverflow.com/q/6802145) or re-cloning with the full repository contents." .to_string(), ), GitImportError::RemoteReservedForLocalGitRepo => { Some("Run `jj git remote rename` to give different name.".to_string()) } GitImportError::InternalGitError(_) => None, GitImportError::UnexpectedBackend => None, }; CommandError::UserError { message, hint } } } impl From<GitExportError> for CommandError { fn from(err: GitExportError) -> Self { CommandError::InternalError(format!( "Failed to export refs to underlying Git repo: {err}" )) } } impl From<GitRemoteManagementError> for CommandError { fn from(err: GitRemoteManagementError) -> Self { user_error(format!("{err}")) } } impl From<RevsetEvaluationError> for CommandError { fn from(err: RevsetEvaluationError) -> Self { user_error(format!("{err}")) } } impl From<RevsetParseError> for CommandError { fn from(err: RevsetParseError) -> Self { let message = iter::successors(Some(&err), |e| e.origin()).join("\n"); // Only for the top-level error as we can't attach hint to inner errors let hint = match err.kind() { RevsetParseErrorKind::NotPostfixOperator { op: _, similar_op, description, } | RevsetParseErrorKind::NotInfixOperator { op: _, similar_op, description, } => Some(format!("Did you mean '{similar_op}' for {description}?")), RevsetParseErrorKind::NoSuchFunction { name: _, candidates, } => format_similarity_hint(candidates), _ => None, }; CommandError::UserError { message: format!("Failed to parse revset: {message}"), hint, } } } impl From<RevsetResolutionError> for CommandError { fn from(err: RevsetResolutionError) -> Self { let hint = match &err { RevsetResolutionError::NoSuchRevision { name: _, candidates, } => format_similarity_hint(candidates), RevsetResolutionError::EmptyString | RevsetResolutionError::WorkspaceMissingWorkingCopy { .. } | RevsetResolutionError::AmbiguousCommitIdPrefix(_) | RevsetResolutionError::AmbiguousChangeIdPrefix(_) | RevsetResolutionError::StoreError(_) => None, }; CommandError::UserError { message: format!("{err}"), hint, } } } impl From<TemplateParseError> for CommandError { fn from(err: TemplateParseError) -> Self { let message = iter::successors(Some(&err), |e| e.origin()).join("\n"); user_error(format!("Failed to parse template: {message}")) } } impl From<FsPathParseError> for CommandError { fn from(err: FsPathParseError) -> Self { user_error(format!("{err}")) } } impl From<clap::Error> for CommandError { fn from(err: clap::Error) -> Self { CommandError::ClapCliError(Arc::new(err)) } } impl From<GitConfigParseError> for CommandError { fn from(err: GitConfigParseError) -> Self { CommandError::InternalError(format!("Failed to parse Git config: {err} ")) } } impl From<WorkingCopyStateError> for CommandError { fn from(err: WorkingCopyStateError) -> Self { CommandError::InternalError(format!("Failed to access working copy state: {err}")) } } #[derive(Clone)] struct ChromeTracingFlushGuard { _inner: Option<Rc<tracing_chrome::FlushGuard>>, } impl Debug for ChromeTracingFlushGuard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { _inner } = self; f.debug_struct("ChromeTracingFlushGuard") .finish_non_exhaustive() } } /// Handle to initialize or change tracing subscription. #[derive(Clone, Debug)] pub struct TracingSubscription { reload_log_filter: tracing_subscriber::reload::Handle< tracing_subscriber::EnvFilter, tracing_subscriber::Registry, >, _chrome_tracing_flush_guard: ChromeTracingFlushGuard, } impl TracingSubscription { /// Initializes tracing with the default configuration. This should be /// called as early as possible. pub fn init() -> Self { let filter = tracing_subscriber::EnvFilter::builder() .with_default_directive(tracing::metadata::LevelFilter::ERROR.into()) .from_env_lossy(); let (filter, reload_log_filter) = tracing_subscriber::reload::Layer::new(filter); let (chrome_tracing_layer, chrome_tracing_flush_guard) = match std::env::var("JJ_TRACE") { Ok(filename) => { let filename = if filename.is_empty() { format!( "jj-trace-{}.json", SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), ) } else { filename }; let include_args = std::env::var("JJ_TRACE_INCLUDE_ARGS").is_ok(); let (layer, guard) = ChromeLayerBuilder::new() .file(filename) .include_args(include_args) .build(); ( Some(layer), ChromeTracingFlushGuard { _inner: Some(Rc::new(guard)), }, ) } Err(_) => (None, ChromeTracingFlushGuard { _inner: None }), }; tracing_subscriber::registry() .with( tracing_subscriber::fmt::Layer::default() .with_writer(std::io::stderr) .with_filter(filter), ) .with(chrome_tracing_layer) .init(); TracingSubscription { reload_log_filter, _chrome_tracing_flush_guard: chrome_tracing_flush_guard, } } pub fn enable_verbose_logging(&self) -> Result<(), CommandError> { self.reload_log_filter .modify(|filter| { *filter = tracing_subscriber::EnvFilter::builder() .with_default_directive(tracing::metadata::LevelFilter::DEBUG.into()) .from_env_lossy() }) .map_err(|err| { CommandError::InternalError(format!("failed to enable verbose logging: {err}")) })?; tracing::info!("verbose logging enabled"); Ok(()) } } pub struct CommandHelper { app: Command, cwd: PathBuf, string_args: Vec<String>, matches: ArgMatches, global_args: GlobalArgs, settings: UserSettings, layered_configs: LayeredConfigs, maybe_workspace_loader: Result<WorkspaceLoader, CommandError>, store_factories: StoreFactories, working_copy_factories: HashMap<String, WorkingCopyFactory>,