diff --git a/gitoxide-core/src/lib.rs b/gitoxide-core/src/lib.rs index 6c3f066b4a2..88065e5fe10 100644 --- a/gitoxide-core/src/lib.rs +++ b/gitoxide-core/src/lib.rs @@ -33,5 +33,6 @@ impl FromStr for OutputFormat { } } +pub mod pack; pub mod repository; pub mod verify; diff --git a/gitoxide-core/src/pack.rs b/gitoxide-core/src/pack.rs new file mode 100644 index 00000000000..1ea015611b8 --- /dev/null +++ b/gitoxide-core/src/pack.rs @@ -0,0 +1,12 @@ +pub mod explode { + use anyhow::Result; + use git_features::progress::Progress; + use std::path::Path; + + pub fn pack_or_pack_index

(_path: impl AsRef, _progress: Option

, _delete_pack: bool) -> Result<()> + where + P: Progress, + { + Ok(()) + } +} diff --git a/src/plumbing/lean.rs b/src/plumbing/lean.rs index ab766b9e59a..3e50d18c9bf 100644 --- a/src/plumbing/lean.rs +++ b/src/plumbing/lean.rs @@ -25,9 +25,29 @@ mod options { #[argh(subcommand)] pub enum SubCommands { PackVerify(PackVerify), + PackExplode(PackExplode), } + /// Explode a pack into loose objects. + /// + /// This can be useful in case of partially invalidated packs to extract as much information as possible, + /// or because working with loose objects is easier with custom tooling. + #[derive(FromArgs, PartialEq, Debug)] + #[argh(subcommand, name = "pack-explode")] + pub struct PackExplode { + /// delete the pack and index file after the operation is successful + #[argh(switch)] + pub delete_pack: bool, - /// Initialize the repository in the current directory. + /// display verbose messages and progress information + #[argh(switch, short = 'v')] + pub verbose: bool, + + /// the '.pack' or '.idx' file to explode into loose objects + #[argh(positional)] + pub path: PathBuf, + } + + /// Verify a pack #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "pack-verify")] pub struct PackVerify { @@ -56,10 +76,10 @@ mod options { /// output statistical information about the pack #[argh(switch, short = 's')] pub statistics: bool, - /// verbose progress messages are printed line by line + /// display verbose messages and progress information #[argh(switch, short = 'v')] pub verbose: bool, - /// the '.pack' or '.idx' data whose checksum to validate. + /// the '.pack' or '.idx' file whose checksum to validate. #[argh(positional)] pub path: PathBuf, } @@ -100,6 +120,14 @@ pub fn main() -> Result<()> { let cli: Args = crate::shared::from_env(); let thread_limit = cli.threads; match cli.subcommand { + SubCommands::PackExplode(PackExplode { + path, + verbose, + delete_pack, + }) => { + let (_handle, progress) = prepare(verbose, "pack-explode"); + core::pack::explode::pack_or_pack_index(path, progress, delete_pack) + } SubCommands::PackVerify(PackVerify { path, verbose, diff --git a/src/plumbing/pretty.rs b/src/plumbing/pretty.rs index 6da3e81fd39..f8ac49e8c0c 100644 --- a/src/plumbing/pretty.rs +++ b/src/plumbing/pretty.rs @@ -26,6 +26,31 @@ mod options { #[derive(Debug, StructOpt)] pub enum Subcommands { + /// Verify the integrity of a pack or index file + #[structopt(setting = AppSettings::ColoredHelp)] + PackExplode { + /// Delete the pack and index file after the operation is successful + #[structopt(long)] + delete_pack: bool, + + /// Display verbose messages and progress information + #[structopt(long, short = "v")] + verbose: bool, + + /// Bring up a terminal user interface displaying progress visually + #[structopt(long, conflicts_with("verbose"))] + progress: bool, + + /// The progress TUI will stay up even though the work is already completed. + /// + /// Use this to be able to read progress messages or additional information visible in the TUI log pane. + #[structopt(long, conflicts_with("verbose"), requires("progress"))] + progress_keep_open: bool, + + /// The '.pack' or '.idx' file to explode into loose objects + #[structopt(parse(from_os_str))] + path: PathBuf, + }, /// Verify the integrity of a pack or index file #[structopt(setting = AppSettings::ColoredHelp)] PackVerify { @@ -50,23 +75,23 @@ mod options { )] algorithm: core::verify::Algorithm, - /// verbose progress messages are printed line by line + /// Display verbose messages and progress information #[structopt(long, short = "v")] verbose: bool, - /// bring up a terminal user interface displaying progress visually + /// Bring up a terminal user interface displaying progress visually #[structopt(long, conflicts_with("verbose"))] progress: bool, #[structopt(long, conflicts_with("re-encode"))] - /// decode and parse tags, commits and trees to validate their correctness beyond hashing correctly. + /// Decode and parse tags, commits and trees to validate their correctness beyond hashing correctly. /// /// Malformed objects should not usually occur, but could be injected on purpose or accident. /// This will reduce overall performance. decode: bool, #[structopt(long)] - /// decode and parse tags, commits and trees to validate their correctness, and re-encode them. + /// Decode and parse tags, commits and trees to validate their correctness, and re-encode them. /// /// This flag is primarily to test the implementation of encoding, and requires to decode the object first. /// Encoding an object after decoding it should yield exactly the same bytes. @@ -74,13 +99,13 @@ mod options { /// owned objects, causing plenty of allocation to occour. re_encode: bool, - /// the progress TUI will stay up even though the work is already completed. + /// The progress TUI will stay up even though the work is already completed. /// /// Use this to be able to read progress messages or additional information visible in the TUI log pane. #[structopt(long, conflicts_with("verbose"), requires("progress"))] progress_keep_open: bool, - /// The '.pack' or '.idx' data whose checksum to validate. + /// The '.pack' or '.idx' file whose checksum to validate. #[structopt(parse(from_os_str))] path: PathBuf, }, @@ -180,6 +205,19 @@ pub fn main() -> Result<()> { let args = Args::from_args(); let thread_limit = args.threads; match args.cmd { + Subcommands::PackExplode { + verbose, + progress, + progress_keep_open, + delete_pack, + path, + } => prepare_and_run( + "pack-explode", + verbose, + progress, + progress_keep_open, + move |progress, _out, _err| core::pack::explode::pack_or_pack_index(path, progress, delete_pack), + ), Subcommands::PackVerify { path, algorithm, diff --git a/tasks.md b/tasks.md index a4db75f4f64..e651ce05c65 100644 --- a/tasks.md +++ b/tasks.md @@ -33,10 +33,11 @@ * [x] Deflate stream * [x] disk - with decent errors * [x] size as u64 (properly) - * [ ] generalize pack reading algorithm - * [ ] write loose object to memory * **cli** + * [ ] generalize pack reading algorithm * [ ] write objects from pack (multi-threaded comes for free) + * [ ] to sink + * [ ] to disk * [ ] progress * [ ] statistics diff --git a/tests/snapshots/plumbing-pack-explode-to-sink-delete-pack-success b/tests/snapshots/plumbing-pack-explode-to-sink-delete-pack-success new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/snapshots/plumbing-pack-explode-to-sink-success b/tests/snapshots/plumbing-pack-explode-to-sink-success new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/stateless-journey.sh b/tests/stateless-journey.sh index 3e838086c03..342240f44a4 100755 --- a/tests/stateless-journey.sh +++ b/tests/stateless-journey.sh @@ -40,6 +40,42 @@ title "CLI ${kind}" ) ) +(when "running 'plumbing pack-explode" + PACK_FILE="$fixtures/packs/pack-11fdfa9e156ab73caae3b6da867192221f2089c2" + (with "no directory specified" + it "explodes the pack successfully and with desired output" && { + WITH_SNAPSHOT="$snapshot/plumbing-pack-explode-to-sink-success" \ + expect_run $SUCCESSFULLY "$exe_plumbing" pack-explode "${PACK_FILE}.idx" + } + + (when "using the --delete-pack flag" + (sandbox + cp ${PACK_FILE}.idx ${PACK_FILE}.pack . + PACK_FILE="${PACK_FILE##*/}" + (with "a valid pack" + it "explodes the pack successfully and deletes the original pack and index" && { + WITH_SNAPSHOT="$snapshot/plumbing-pack-explode-to-sink-delete-pack-success" \ + expect_run $SUCCESSFULLY "$exe_plumbing" pack-explode --delete-pack "${PACK_FILE}.pack" + } + it "removes the original files" && { + expect_run $WITH_FAILURE ls ${PACK_FILE}.pack + expect_run $WITH_FAILURE ls ${PACK_FILE}.idx + } + ) + (with "TODO(how to write into the middle of a file in bash): an invalid pack" + + ) + ) + ) + ) + (with "a non-existing directory specified" + + ) + (with "an existing directory specified" + + ) +) + (when "running 'plumbing pack-verify" (with "a valid pack file" PACK_FILE="$fixtures/packs/pack-11fdfa9e156ab73caae3b6da867192221f2089c2.pack"