1829 lines · 70.4 KB · Rust
RawDownload
use std::{    io::{BufReader, stdin},    path::PathBuf,    sync::{        Arc,        atomic::{AtomicBool, Ordering},    },}; use anyhow::{Context, Result, anyhow};use clap::{CommandFactory, Parser};use gitoxide_core as core;use gitoxide_core::{pack::verify, repository::PathsOrPatterns};use gix::bstr::{BString, io::BufReadExt}; use crate::{    plumbing::{        options::{            Args, Subcommands, attributes, branch, commit, commitgraph, config, credential, exclude, free, fsck, index,            mailmap, merge, odb, revision, tag, tree,        },        show_progress,    },    shared::pretty::prepare_and_run,}; #[cfg(feature = "gitoxide-core-async-client")]pub mod async_util {    use crate::shared::ProgressRange;     #[cfg(not(feature = "prodash-render-line"))]    compile_error!("BUG: Need at least a line renderer in async mode");     pub fn prepare(        verbose: bool,        trace: bool,        name: &str,        range: impl Into<Option<ProgressRange>>,    ) -> (        Option<prodash::render::line::JoinHandle>,        gix_features::progress::DoOrDiscard<prodash::tree::Item>,    ) {        use crate::shared::{self, STANDARD_RANGE};        shared::init_env_logger();         if verbose {            let progress = shared::progress_tree(trace);            let sub_progress = progress.add_child(name);            let ui_handle = shared::setup_line_renderer_range(&progress, range.into().unwrap_or(STANDARD_RANGE));            (Some(ui_handle), Some(sub_progress).into())        } else {            (None, None.into())        }    }} pub fn main() -> Result<()> {    let args: Args = Args::parse_from(gix::env::args_os());    let thread_limit = args.threads;    let verbose = args.verbose;    let format = args.format;    let cmd = args.cmd;    #[cfg_attr(not(feature = "tracing"), allow(unused_mut))]    #[cfg_attr(feature = "tracing", allow(unused_assignments))]    let mut trace = false;    #[cfg(feature = "tracing")]    {        trace = args.trace;    }    let object_hash = args.object_hash;    let config = args.config;    let repository = args.repository;    let repository_path = repository.clone();    enum Mode {        Strict,        StrictWithGitInstallConfig,        Lenient,        LenientWithGitInstallConfig,    }     let repository = {        let config = config.clone();        move |mut mode: Mode| -> Result<gix::Repository> {            let mut mapping: gix::sec::trust::Mapping<gix::open::Options> = Default::default();            if !config.is_empty() {                mode = match mode {                    Mode::Lenient => Mode::Strict,                    Mode::LenientWithGitInstallConfig => Mode::StrictWithGitInstallConfig,                    _ => mode,                };            }            let strict_toggle = matches!(mode, Mode::Strict | Mode::StrictWithGitInstallConfig) || args.strict;            mapping.full = mapping.full.strict_config(strict_toggle);            mapping.reduced = mapping.reduced.strict_config(strict_toggle);            let git_installation = matches!(                mode,                Mode::StrictWithGitInstallConfig | Mode::LenientWithGitInstallConfig            );            let to_match_settings = |mut opts: gix::open::Options| {                opts.permissions.config.git_binary = git_installation;                opts.permissions.attributes.git_binary = git_installation;                if config.is_empty() {                    opts                } else {                    opts.cli_overrides(config.clone())                }            };            mapping.full.modify(to_match_settings);            mapping.reduced.modify(to_match_settings);            let mut repo = gix::ThreadSafeRepository::discover_with_environment_overrides_opts(                repository,                Default::default(),                mapping,            )            .map(gix::Repository::from)?;            if !config.is_empty() {                repo.config_snapshot_mut()                    .append_config(config.iter(), gix::config::Source::Cli)                    .context("Unable to parse command-line configuration")?;            }            {                let mut config_mut = repo.config_snapshot_mut();                // Enable precious file parsing unless the user made a choice.                if config_mut                    .boolean(gix::config::tree::Gitoxide::PARSE_PRECIOUS)?                    .is_none()                {                    config_mut.set_raw_value(gix::config::tree::Gitoxide::PARSE_PRECIOUS, "true")?;                }            }            Ok(repo)        }    };     let progress;    let progress_keep_open;    #[cfg(feature = "prodash-render-tui")]    {        progress = args.progress;        progress_keep_open = args.progress_keep_open;    }    #[cfg(not(feature = "prodash-render-tui"))]    {        progress = false;        progress_keep_open = false;    }    let auto_verbose = !progress && !args.no_verbose;     let should_interrupt = Arc::new(AtomicBool::new(false));    #[expect(unsafe_code)]    unsafe {        // SAFETY: The closure doesn't use mutexes or memory allocation, so it should be safe to call from a signal handler.        gix::interrupt::init_handler(1, {            let should_interrupt = Arc::clone(&should_interrupt);            move || should_interrupt.store(true, Ordering::SeqCst)        })?;    }     match cmd {        Subcommands::Env => prepare_and_run(            "env",            trace,            verbose,            progress,            progress_keep_open,            None,            move |_progress, out, _err| core::env(out, format),        ),        Subcommands::Merge(merge::Platform { cmd }) => match cmd {            merge::SubCommands::File {                resolve_with,                ours,                base,                theirs,            } => prepare_and_run(                "merge-file",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, _err| {                    core::repository::merge::file(                        repository(Mode::Lenient)?,                        out,                        format,                        resolve_with.map(Into::into),                        base,                        ours,                        theirs,                    )                },            ),            merge::SubCommands::Tree {                opts:                    merge::SharedOptions {                        in_memory,                        file_favor,                        tree_favor,                        debug,                    },                message,                update_head,                ours,                base,                theirs,            } => prepare_and_run(                "merge-tree",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, err| {                    core::repository::merge::tree(                        repository(Mode::Lenient)?,                        out,                        err,                        base,                        ours,                        theirs,                        core::repository::merge::tree::Options {                            format,                            file_favor: file_favor.map(Into::into),                            in_memory,                            tree_favor: tree_favor.map(Into::into),                            debug,                            message,                            update_head,                        },                    )                },            ),            merge::SubCommands::Commit {                opts:                    merge::SharedOptions {                        in_memory,                        file_favor,                        tree_favor,                        debug,                    },                ours,                theirs,            } => prepare_and_run(                "merge-commit",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, err| {                    core::repository::merge::commit(                        repository(Mode::Lenient)?,                        out,                        err,                        ours,                        theirs,                        core::repository::merge::tree::Options {                            format,                            file_favor: file_favor.map(Into::into),                            tree_favor: tree_favor.map(Into::into),                            in_memory,                            debug,                            message: None,                            update_head: false,                        },                    )                },            ),        },        Subcommands::MergeBase(crate::plumbing::options::merge_base::Command { first, others }) => prepare_and_run(            "merge-base",            trace,            verbose,            progress,            progress_keep_open,            None,            move |_progress, out, _err| {                core::repository::merge_base(repository(Mode::Lenient)?, first, others, out, format)            },        ),        Subcommands::Diff(crate::plumbing::options::diff::Platform { cmd }) => match cmd {            crate::plumbing::options::diff::SubCommands::Tree {                old_treeish,                new_treeish,            } => prepare_and_run(                "diff-tree",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, _err| {                    core::repository::diff::tree(repository(Mode::Lenient)?, out, old_treeish, new_treeish)                },            ),            crate::plumbing::options::diff::SubCommands::File {                old_revspec,                new_revspec,            } => prepare_and_run(                "diff-file",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, _err| {                    core::repository::diff::file(repository(Mode::Lenient)?, out, old_revspec, new_revspec)                },            ),        },        Subcommands::Log(crate::plumbing::options::log::Platform { pathspec }) => prepare_and_run(            "log",            trace,            verbose,            progress,            progress_keep_open,            None,            move |_progress, out, _err| core::repository::log::log(repository(Mode::Lenient)?, out, pathspec),        ),        Subcommands::Worktree(crate::plumbing::options::worktree::Platform { cmd }) => match cmd {            crate::plumbing::options::worktree::SubCommands::List => prepare_and_run(                "worktree-list",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, _err| core::repository::worktree::list(repository(Mode::Lenient)?, out, format),            ),        },        Subcommands::IsClean | Subcommands::IsChanged => {            let mode = if matches!(cmd, Subcommands::IsClean) {                core::repository::dirty::Mode::IsClean            } else {                core::repository::dirty::Mode::IsDirty            };            prepare_and_run(                "clean",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, _err| {                    core::repository::dirty::check(repository(Mode::Lenient)?, mode, out, format)                },            )        }        #[cfg(feature = "gitoxide-core-tools-clean")]        Subcommands::Clean(crate::plumbing::options::clean::Command {            debug,            dry_run: _,            execute,            ignored,            precious,            directories,            pathspec,            repositories,            pathspec_matches_result,            skip_hidden_repositories,            find_untracked_repositories,        }) => prepare_and_run(            "clean",            trace,            verbose,            progress,            progress_keep_open,            None,            move |_progress, out, err| {                core::repository::clean(                    repository(Mode::Lenient)?,                    out,                    err,                    pathspec,                    core::repository::clean::Options {                        debug,                        format,                        execute,                        ignored,                        precious,                        directories,                        repositories,                        pathspec_matches_result,                        skip_hidden_repositories: skip_hidden_repositories.map(Into::into),                        find_untracked_repositories: find_untracked_repositories.into(),                    },                )            },        ),        Subcommands::Status(crate::plumbing::options::status::Platform {            ignored,            untracked,            format: status_format,            statistics,            submodules,            no_write,            pathspec,            index_worktree_renames,        }) => prepare_and_run(            "status",            trace,            auto_verbose,            progress,            progress_keep_open,            None,            move |progress, out, err| {                use crate::plumbing::options::status::Submodules;                core::repository::status::show(                    repository(Mode::Lenient)?,                    pathspec,                    out,                    err,                    progress,                    core::repository::status::Options {                        format: match status_format.unwrap_or_default() {                            crate::plumbing::options::status::Format::Simplified => {                                core::repository::status::Format::Simplified                            }                            crate::plumbing::options::status::Format::PorcelainV2 => {                                core::repository::status::Format::PorcelainV2                            }                        },                        ignored: ignored.map(|ignored| match ignored.unwrap_or_default() {                            crate::plumbing::options::status::Ignored::Matching => {                                core::repository::status::Ignored::Matching                            }                            crate::plumbing::options::status::Ignored::Collapsed => {                                core::repository::status::Ignored::Collapsed                            }                        }),                        untracked: untracked.map(|mode| match mode.unwrap_or_default() {                            crate::plumbing::options::status::Untracked::No => gix::status::UntrackedFiles::None,                            crate::plumbing::options::status::Untracked::Normal => {                                gix::status::UntrackedFiles::Collapsed                            }                            crate::plumbing::options::status::Untracked::All => gix::status::UntrackedFiles::Files,                        }),                        output_format: format,                        statistics,                        thread_limit: thread_limit.or(cfg!(target_os = "macos").then_some(3)), // TODO: make this a configurable when in `gix`, this seems to be optimal on MacOS, linux scales though! MacOS also scales if reading a lot of files for refresh index                        allow_write: !no_write,                        index_worktree_renames: index_worktree_renames.map(|percentage| percentage.unwrap_or(0.5)),                        submodules: submodules.map(|submodules| match submodules {                            Submodules::All => core::repository::status::Submodules::All,                            Submodules::RefChange => core::repository::status::Submodules::RefChange,                            Submodules::Modifications => core::repository::status::Submodules::Modifications,                            Submodules::None => core::repository::status::Submodules::None,                        }),                    },                )            },        ),        Subcommands::Dirwalk(crate::plumbing::options::dirwalk::Platform {            statistics,            untracked,            pathspec,        }) => prepare_and_run(            "dirwalk",            trace,            auto_verbose,            progress,            progress_keep_open,            None,            move |_progress, out, err| {                core::repository::dirwalk::walk(                    repository(Mode::Lenient)?,                    pathspec,                    out,                    err,                    core::repository::dirwalk::Options {                        output_format: format,                        statistics,                        untracked: match untracked {                            crate::plumbing::options::dirwalk::Untracked::Collapsed => {                                core::repository::dirwalk::Untracked::Collapsed                            }                            crate::plumbing::options::dirwalk::Untracked::Matching => {                                core::repository::dirwalk::Untracked::Matching                            }                        },                    },                )            },        ),        Subcommands::Submodule(platform) => match platform            .cmds            .unwrap_or(crate::plumbing::options::submodule::Subcommands::List { dirty_suffix: None })        {            crate::plumbing::options::submodule::Subcommands::List { dirty_suffix } => prepare_and_run(                "submodule-list",                trace,                verbose,                progress,                progress_keep_open,                None,                move |_progress, out, _err| {                    core::repository::submodule::list(                        repository(Mode::Lenient)?,                        out,

Lines 1–500 of 1829.