use std::{ffi::OsStr, path::PathBuf}; use clap::{ Arg, Command, Error, builder::{OsStringValueParser, TypedValueParser},};use gix::bstr::BString; #[derive(Debug, clap::Parser)]#[clap(name = "it", about = "internal tools to help create test cases")]pub struct Args { #[clap(subcommand)] pub cmd: Subcommands,} #[derive(Debug, clap::Subcommand)]pub enum Subcommands { /// Generate a shell script that creates a git repository containing all commits that are /// traversed when following a given file through the Git history just as `git blame` would. /// /// This command extracts the file’s history so that blame, when run on the repository created /// by the script, shows the same characteristics, in particular bugs, as the original, but in /// a way that does not resemble the original source file's content to any greater extent than /// is useful and necessary. /// /// Note that this should not be used to redact sensitive information. The obfuscation leaves /// numerous properties of the source intact, such that it may be feasible to reconstruct the /// input. /// /// This command can also be helpful in debugging the blame algorithm itself. /// /// ### Terminology /// /// A **blame history** is the set of commits that the blame algorithm, at some point, treated /// as potential suspects for any line in a file. It is a subset of all commits that ever /// changed a file in its history. /// /// With respect to branches and merge commits, the **blame history** will not necessarily be /// identical to the file's history in the source repository. This is because the blame /// algorithm will stop following a file's history for branches that only touch lines for which /// the source has already been found. The **blame history**, thus, looks likely "cleaner" and /// "simpler" than the source history. #[clap(visible_alias = "bcr")] BlameCopyRoyal { /// Don't really copy anything. #[clap(long, short = 'n')] dry_run: bool, /// The git root whose history to extract the blame-relevant parts from. worktree_dir: PathBuf, /// The directory into which to copy the files. destination_dir: PathBuf, /// The directory to place assets in. #[clap(long)] asset_dir: Option<BString>, /// The file to extract the history for. file: std::ffi::OsString, /// Do not use `copy-royal` to obfuscate the content of blobs, but copy it verbatim. /// /// Note that, for producing cases for the gitoxide test suite, we usually prefer only to /// take blobs verbatim if the source repository was purely for testing. #[clap(long)] verbatim: bool, }, /// Copy a tree so that it diffs the same but does not resemble the original files' content to /// any greater extent than is useful and necessary. /// /// The idea is that this preserves the patterns that are usually sufficient to reproduce cases /// for tests of diffs, both for making the tests work and for keeping the diffs understandable /// to developers working on the tests, while avoiding keeping large verbatim fragments of code /// based on which the test cases were created. The benefits of "reducing" the code to these /// patterns include that the original meaning and function of code will not be confused with /// the code of gitoxide itself, will not distract from the effects observed in their diffs, /// and will not inadvertently be caught up in code cleanup efforts (e.g. attempting to adjust /// style or fix bugs) that would make sense in code of gitoxide itself but that would subtly /// break data test fixtures if done on their data. /// /// Note that this should not be used to redact sensitive information. The obfuscation leaves /// numerous properties of the source intact, such that it may be feasible to reconstruct the /// input. #[clap(visible_alias = "cr")] CopyRoyal { /// Don't really copy anything. #[clap(long, short = 'n')] dry_run: bool, /// The git root whose tracked files to copy. worktree_dir: PathBuf, /// The directory into which to copy the files. destination_dir: PathBuf, /// The pathspecs to determine which paths to copy from `worktree_dir`. /// /// None will copy everything. #[clap(value_parser = AsPathSpec)] patterns: Vec<gix::pathspec::Pattern>, }, /// Serialize a git repository as linear history while degenerating content into a shell script that reproduces it. #[clap(visible_alias = "gts")] GitToSh { /// The amount of commits to copy from `committish`. /// /// If 0, all traversable commits will be copied. #[clap(long, short = 'c', default_value_t = 0)] count: usize, /// Do not use `copy-royal` to degenerate information of blobs, but take blobs verbatim. /// /// Note that, for producing cases for the gitoxide test suite, we usually prefer only to /// take blobs verbatim if the source repository was purely for testing. #[clap(long)] verbatim: bool, /// The directory into which the blobs and tree declarations will be written. #[clap(long, short = 'o', default_value = ".")] output_dir: PathBuf, /// The path to the git repository to serialize. repo_dir: PathBuf, /// The name of the directory within `output_dir` for storing blobs and trees. name: String, /// A revspec of the commit to start the iteration from, like `@`. /// /// Note that the history will be serialized, and multiple parents aren't allowed. committish: String, /// The pathspecs to determine which paths to copy from each commit's tree. /// /// None will copy everything. #[clap(value_parser = AsPathSpec)] patterns: Vec<gix::pathspec::Pattern>, }, /// Take a slider file generated with the help of [diff-slider-tools] and turn it into a series /// of baseline diffs to be used in [slider-rs]. /// /// See [make-diff-for-sliders-repo] for details. /// /// [diff-slider-tools]: https://github.com/mhagger/diff-slider-tools /// [slider-rs]: gix-diff/tests/diff/blob/slider.rs /// [make-diff-for-sliders-repo]: gix-diff/tests/fixtures/make_diff_for_sliders_repo.sh CreateDiffCases { /// Don't really copy anything. #[clap(long, short = 'n')] dry_run: bool, /// The `.sliders` file that contains a list of sliders. #[clap(long)] sliders_file: PathBuf, /// The git root to extract the diff-related parts from. #[clap(long)] worktree_dir: PathBuf, /// The directory into which to copy the files. #[clap(long)] destination_dir: PathBuf, /// The number of sliders to generate test cases for. #[clap(long, default_value_t = 10)] count: usize, /// The directory to place assets in. #[clap(long)] asset_dir: Option<BString>, }, /// Check for executable bits that disagree with shebangs. /// /// This checks committed and staged files, but not anything unstaged, to find shell scripts /// that either begin with a `#!` but not `+x` permissions, or do not begin with `#!` but do /// have `+x` permissions. Such mismatches are reported but not automatically corrected. Some /// platforms (at least Windows) do not have such permissions, but Git still represents them. /// /// This currently only checks files name with an `.sh` suffix, and only operates on the /// current repository. Its main use is checking that fixture scripts have correct modes. #[clap(visible_alias = "cm")] CheckMode {}, /// Print environment variables as `NAME=value` lines. /// /// It is useful to be able to observe environment variables that are set when running code /// with tools such as `cargo` or `cross`. Commands like `cargo run -p internal-tools -- env` /// include environment changes from `cargo` itself. With `cross`, changes are more extensive, /// due to effects of `build.env.passthrough`, container customization, and preexisting special /// cases in wrapper scripts shipped in default `cross` containers (such as to `LD_PRELOAD`). /// /// Since one use for checking environment variables is to investigate the effects of /// environments that contain variable names or values that are not valid Unicode, this avoids /// requiring that environment variables all be Unicode. Any name or value that is not Unicode /// is shown in its Rust debug representation. This is always quoted. To decrease ambiguity, /// any name or value containing a literal double quote or newline is also shown in its debug /// representation. Names and values without such content are shown literally and not quoted. #[clap(visible_alias = "e")] Env {},} #[derive(Clone)]pub struct AsPathSpec; impl TypedValueParser for AsPathSpec { type Value = gix::pathspec::Pattern; fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result<Self::Value, Error> { let pathspec_defaults = gix::pathspec::Defaults::from_environment(&mut |n| std::env::var_os(n)).unwrap_or_default(); OsStringValueParser::new() .try_map(move |arg| { let arg: &std::path::Path = arg.as_os_str().as_ref(); gix::pathspec::parse(gix::path::into_bstr(arg).as_ref(), pathspec_defaults) }) .parse_ref(cmd, arg, value) }}
use std::{ffi::OsStr, path::PathBuf};
use clap::{
Arg, Command, Error,
builder::{OsStringValueParser, TypedValueParser},
};
use gix::bstr::BString;
#[derive(Debug, clap::Parser)]
#[clap(name = "it", about = "internal tools to help create test cases")]
pub struct Args {
#[clap(subcommand)]
pub cmd: Subcommands,
}
#[derive(Debug, clap::Subcommand)]
pub enum Subcommands {
/// Generate a shell script that creates a git repository containing all commits that are
/// traversed when following a given file through the Git history just as `git blame` would.
///
/// This command extracts the file’s history so that blame, when run on the repository created
/// by the script, shows the same characteristics, in particular bugs, as the original, but in
/// a way that does not resemble the original source file's content to any greater extent than
/// is useful and necessary.
///
/// Note that this should not be used to redact sensitive information. The obfuscation leaves
/// numerous properties of the source intact, such that it may be feasible to reconstruct the
/// input.
///
/// This command can also be helpful in debugging the blame algorithm itself.
///
/// ### Terminology
///
/// A **blame history** is the set of commits that the blame algorithm, at some point, treated
/// as potential suspects for any line in a file. It is a subset of all commits that ever
/// changed a file in its history.
///
/// With respect to branches and merge commits, the **blame history** will not necessarily be
/// identical to the file's history in the source repository. This is because the blame
/// algorithm will stop following a file's history for branches that only touch lines for which
/// the source has already been found. The **blame history**, thus, looks likely "cleaner" and
/// "simpler" than the source history.
#[clap(visible_alias = "bcr")]
BlameCopyRoyal {
/// Don't really copy anything.
#[clap(long, short = 'n')]
dry_run: bool,
/// The git root whose history to extract the blame-relevant parts from.
worktree_dir: PathBuf,
/// The directory into which to copy the files.
destination_dir: PathBuf,
/// The directory to place assets in.
#[clap(long)]
asset_dir: Option<BString>,
/// The file to extract the history for.
file: std::ffi::OsString,
/// Do not use `copy-royal` to obfuscate the content of blobs, but copy it verbatim.
///
/// Note that, for producing cases for the gitoxide test suite, we usually prefer only to
/// take blobs verbatim if the source repository was purely for testing.
#[clap(long)]
verbatim: bool,
},
/// Copy a tree so that it diffs the same but does not resemble the original files' content to
/// any greater extent than is useful and necessary.
///
/// The idea is that this preserves the patterns that are usually sufficient to reproduce cases
/// for tests of diffs, both for making the tests work and for keeping the diffs understandable
/// to developers working on the tests, while avoiding keeping large verbatim fragments of code
/// based on which the test cases were created. The benefits of "reducing" the code to these
/// patterns include that the original meaning and function of code will not be confused with
/// the code of gitoxide itself, will not distract from the effects observed in their diffs,
/// and will not inadvertently be caught up in code cleanup efforts (e.g. attempting to adjust
/// style or fix bugs) that would make sense in code of gitoxide itself but that would subtly
/// break data test fixtures if done on their data.
///
/// Note that this should not be used to redact sensitive information. The obfuscation leaves
/// numerous properties of the source intact, such that it may be feasible to reconstruct the
/// input.
#[clap(visible_alias = "cr")]
CopyRoyal {
/// Don't really copy anything.
#[clap(long, short = 'n')]
dry_run: bool,
/// The git root whose tracked files to copy.
worktree_dir: PathBuf,
/// The directory into which to copy the files.
destination_dir: PathBuf,
/// The pathspecs to determine which paths to copy from `worktree_dir`.
///
/// None will copy everything.
#[clap(value_parser = AsPathSpec)]
patterns: Vec<gix::pathspec::Pattern>,
},
/// Serialize a git repository as linear history while degenerating content into a shell script that reproduces it.
#[clap(visible_alias = "gts")]
GitToSh {
/// The amount of commits to copy from `committish`.
///
/// If 0, all traversable commits will be copied.
#[clap(long, short = 'c', default_value_t = 0)]
count: usize,
/// Do not use `copy-royal` to degenerate information of blobs, but take blobs verbatim.
///
/// Note that, for producing cases for the gitoxide test suite, we usually prefer only to
/// take blobs verbatim if the source repository was purely for testing.
#[clap(long)]
verbatim: bool,
/// The directory into which the blobs and tree declarations will be written.
#[clap(long, short = 'o', default_value = ".")]
output_dir: PathBuf,
/// The path to the git repository to serialize.
repo_dir: PathBuf,
/// The name of the directory within `output_dir` for storing blobs and trees.
name: String,
/// A revspec of the commit to start the iteration from, like `@`.
///
/// Note that the history will be serialized, and multiple parents aren't allowed.
committish: String,
/// The pathspecs to determine which paths to copy from each commit's tree.
///
/// None will copy everything.
#[clap(value_parser = AsPathSpec)]
patterns: Vec<gix::pathspec::Pattern>,
},
/// Take a slider file generated with the help of [diff-slider-tools] and turn it into a series
/// of baseline diffs to be used in [slider-rs].
///
/// See [make-diff-for-sliders-repo] for details.
///
/// [diff-slider-tools]: https://github.com/mhagger/diff-slider-tools
/// [slider-rs]: gix-diff/tests/diff/blob/slider.rs
/// [make-diff-for-sliders-repo]: gix-diff/tests/fixtures/make_diff_for_sliders_repo.sh
CreateDiffCases {
/// Don't really copy anything.
#[clap(long, short = 'n')]
dry_run: bool,
/// The `.sliders` file that contains a list of sliders.
#[clap(long)]
sliders_file: PathBuf,
/// The git root to extract the diff-related parts from.
#[clap(long)]
worktree_dir: PathBuf,
/// The directory into which to copy the files.
#[clap(long)]
destination_dir: PathBuf,
/// The number of sliders to generate test cases for.
#[clap(long, default_value_t = 10)]
count: usize,
/// The directory to place assets in.
#[clap(long)]
asset_dir: Option<BString>,
},
/// Check for executable bits that disagree with shebangs.
///
/// This checks committed and staged files, but not anything unstaged, to find shell scripts
/// that either begin with a `#!` but not `+x` permissions, or do not begin with `#!` but do
/// have `+x` permissions. Such mismatches are reported but not automatically corrected. Some
/// platforms (at least Windows) do not have such permissions, but Git still represents them.
///
/// This currently only checks files name with an `.sh` suffix, and only operates on the
/// current repository. Its main use is checking that fixture scripts have correct modes.
#[clap(visible_alias = "cm")]
CheckMode {},
/// Print environment variables as `NAME=value` lines.
///
/// It is useful to be able to observe environment variables that are set when running code
/// with tools such as `cargo` or `cross`. Commands like `cargo run -p internal-tools -- env`
/// include environment changes from `cargo` itself. With `cross`, changes are more extensive,
/// due to effects of `build.env.passthrough`, container customization, and preexisting special
/// cases in wrapper scripts shipped in default `cross` containers (such as to `LD_PRELOAD`).
///
/// Since one use for checking environment variables is to investigate the effects of
/// environments that contain variable names or values that are not valid Unicode, this avoids
/// requiring that environment variables all be Unicode. Any name or value that is not Unicode
/// is shown in its Rust debug representation. This is always quoted. To decrease ambiguity,
/// any name or value containing a literal double quote or newline is also shown in its debug
/// representation. Names and values without such content are shown literally and not quoted.
#[clap(visible_alias = "e")]
Env {},
}
#[derive(Clone)]
pub struct AsPathSpec;
impl TypedValueParser for AsPathSpec {
type Value = gix::pathspec::Pattern;
fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result<Self::Value, Error> {
let pathspec_defaults =
gix::pathspec::Defaults::from_environment(&mut |n| std::env::var_os(n)).unwrap_or_default();
OsStringValueParser::new()
.try_map(move |arg| {
let arg: &std::path::Path = arg.as_os_str().as_ref();
gix::pathspec::parse(gix::path::into_bstr(arg).as_ref(), pathspec_defaults)
})
.parse_ref(cmd, arg, value)
}
}