Skip to content

Commit

Permalink
quick and dirty impl of lean command-line for index-from-pack
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Aug 2, 2020
1 parent e78386b commit 9660bbf
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 59 deletions.
6 changes: 6 additions & 0 deletions gitoxide-core/src/pack/explode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ pub enum SafetyCheck {
All,
}

impl Default for SafetyCheck {
fn default() -> Self {
SafetyCheck::All
}
}

impl SafetyCheck {
pub fn variants() -> &'static [&'static str] {
&[
Expand Down
129 changes: 73 additions & 56 deletions gitoxide-core/src/pack/index.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
use git_features::progress::Progress;
use git_odb::pack;
use std::{io, path::PathBuf};
use std::{fs, io, path::PathBuf, str::FromStr};

#[derive(PartialEq, Debug)]
pub enum IterationMode {
AsIs,
Verify,
Restore,
}

impl Default for IterationMode {
fn default() -> Self {
IterationMode::Verify
}
}

impl FromStr for IterationMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
use IterationMode::*;
let slc = s.to_ascii_lowercase();
Ok(match slc.as_str() {
"as-is" => AsIs,
"verify" => Verify,
"restore" => Restore,
_ => return Err("invalid value".into()),
})
}
}

impl From<IterationMode> for pack::data::iter::Mode {
fn from(v: IterationMode) -> Self {
use pack::data::iter::Mode::*;
Expand All @@ -19,6 +41,7 @@ impl From<IterationMode> for pack::data::iter::Mode {
}
}

#[derive(PartialEq, Debug)]
pub enum MemoryMode {
InMemory,
InMemoryDecompressed,
Expand All @@ -27,6 +50,29 @@ pub enum MemoryMode {
ResolveBasesAndDeltas,
}

impl Default for MemoryMode {
fn default() -> Self {
MemoryMode::ResolveDeltas
}
}

impl FromStr for MemoryMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
use MemoryMode::*;
let slc = s.to_ascii_lowercase();
Ok(match slc.as_str() {
"in-memory" => InMemory,
"in-memory-decompressed" => InMemoryDecompressed,
"resolve-bases" => ResolveBases,
"resolve-deltas" => ResolveDeltas,
"resolve-deltas-and-bases" => ResolveBasesAndDeltas,
_ => return Err("invalid value".into()),
})
}
}

impl From<MemoryMode> for pack::bundle::write::MemoryMode {
fn from(v: MemoryMode) -> Self {
use pack::bundle::write::MemoryMode::*;
Expand All @@ -41,10 +87,9 @@ impl From<MemoryMode> for pack::bundle::write::MemoryMode {
}

pub struct Context {
thread_limit: Option<usize>,
iteration_mode: IterationMode,
memory_mode: MemoryMode,
index_kind: pack::index::Kind,
pub thread_limit: Option<usize>,
pub iteration_mode: IterationMode,
pub memory_mode: MemoryMode,
}

impl From<Context> for pack::bundle::write::Options {
Expand All @@ -53,77 +98,49 @@ impl From<Context> for pack::bundle::write::Options {
thread_limit,
iteration_mode,
memory_mode,
index_kind,
}: Context,
) -> Self {
pack::bundle::write::Options {
thread_limit,
iteration_mode: iteration_mode.into(),
memory_mode: memory_mode.into(),
index_kind,
index_kind: pack::index::Kind::default(),
}
}
}

pub enum ReadOrSeek<R, S>
where
R: io::Read,
S: io::Seek + io::Read,
{
Read(R),
Seek(S),
}

impl<R, S> ReadOrSeek<R, S>
where
R: io::Read,
S: io::Seek + io::Read,
{
pub fn inner_stream_len(s: &mut S) -> io::Result<u64> {
use io::SeekFrom;
let old_pos = s.seek(SeekFrom::Current(0))?;
let len = s.seek(SeekFrom::End(0))?;
if old_pos != len {
s.seek(SeekFrom::Start(old_pos))?;
}
Ok(len)
}

pub fn stream_len(&mut self) -> Option<io::Result<u64>> {
match self {
ReadOrSeek::Read(_) => None,
ReadOrSeek::Seek(s) => Some(Self::inner_stream_len(s)),
}
}
}
impl<R, S> io::Read for ReadOrSeek<R, S>
where
R: io::Read,
S: io::Seek + io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
ReadOrSeek::Read(v) => v.read(buf),
ReadOrSeek::Seek(v) => v.read(buf),
}
pub fn stream_len(mut s: impl io::Seek) -> io::Result<u64> {
use io::SeekFrom;
let old_pos = s.seek(SeekFrom::Current(0))?;
let len = s.seek(SeekFrom::End(0))?;
if old_pos != len {
s.seek(SeekFrom::Start(old_pos))?;
}
Ok(len)
}

pub fn from_pack<P, R, S>(
mut pack: ReadOrSeek<R, S>,
pub fn from_pack<P>(
pack: Option<PathBuf>,
directory: Option<PathBuf>,
progress: P,
context: Context,
) -> anyhow::Result<()>
where
R: io::Read,
S: io::Seek + io::Read,
P: Progress,
<<P as Progress>::SubProgress as Progress>::SubProgress: Send,
{
use anyhow::Context;
let pack_len = pack.stream_len().transpose()?;
pack::Bundle::write_to_directory(pack, pack_len, directory, progress, context.into())
.with_context(|| "Failed to write pack and index")
.map(|_| ())
match pack {
Some(pack) => {
let pack_len = pack.metadata()?.len();
let pack_file = fs::File::open(pack)?;
pack::Bundle::write_to_directory(pack_file, Some(pack_len), directory, progress, context.into())
}
None => {
let stdin = io::stdin();
pack::Bundle::write_to_directory(stdin.lock(), None, directory, progress, context.into())
}
}
.with_context(|| "Failed to write pack and index")
.map(|_| ())
}
78 changes: 75 additions & 3 deletions src/plumbing/lean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,58 @@ mod options {
pub enum SubCommands {
PackVerify(PackVerify),
PackExplode(PackExplode),
IndexFromPack(IndexFromPack),
}
/// Create an index from a packfile.
///
/// This command can also be used to stream packs to standard input or to repair partial packs.
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "index-from-pack")]
pub struct IndexFromPack {
/// display verbose messages and progress information
#[argh(switch, short = 'v')]
pub verbose: bool,

/// specify how to iterate the pack, defaults to 'verify'
///
/// Valid values are
/// - as-is
/// * do not do anything and expect the pack file to be valid as per the trailing hash
/// - verifyhash
/// * the input ourselves and validate that it matches with the hash provided in the pack
/// - restore
/// * hash the input ourselves and ignore failing entries, instead finish the pack with the hash we computed
#[argh(option, short = 'i')]
pub iteration_mode: Option<core::pack::index::IterationMode>,

/// trade memory consumption for higher speed or lower memory consumption for reduced performance. Defaults to 'resolve-deltas'
///
/// Valid values are
/// - in-memory
/// * keep all objects in memory, compressed
/// - in-memory-decompressed
/// * as above, but decompressed, uses about twice as much memory, but does no unnecessary work
/// - resolve-bases
/// * keep compressed deltas in memory, store the pack on disk and resolve bases from there
/// - resolve-deltas
/// * as above, but keep compressed bases
/// - resolve-bases-and-deltas
/// * as above, but resolve all objects, store none in memory
/// * this option duplicates most work, but uses the least memory
#[argh(option, short = 'm')]
pub memory_mode: Option<core::pack::index::MemoryMode>,

/// path to the pack file to read (with .pack extension).
///
/// If unset, the pack file is expected on stdin.
#[argh(option, short = 'p')]
pub pack_path: Option<PathBuf>,

/// the folder into which to place the pack and the generated index file
///
/// If unset, only informational output will be provided to standard output.
#[argh(positional)]
pub directory: Option<PathBuf>,
}
/// Explode a pack into loose objects.
///
Expand Down Expand Up @@ -114,16 +166,17 @@ mod options {
}

use anyhow::Result;
use git_features::progress;
use gitoxide_core as core;
use std::io::{stderr, stdout};

#[cfg(not(any(
feature = "prodash-line-renderer-crossterm",
feature = "prodash-line-renderer-termion"
)))]
fn prepare(verbose: bool, name: &str) -> ((), Option<git_features::progress::Log>) {
fn prepare(verbose: bool, name: &str) -> ((), Option<progress::Log>) {
super::init_env_logger(verbose);
((), Some(git_features::progress::Log::new(name, Some(1))))
((), Some(progress::Log::new(name, Some(1))))
}

#[cfg(any(
Expand All @@ -148,6 +201,25 @@ pub fn main() -> Result<()> {
let cli: Args = crate::shared::from_env();
let thread_limit = cli.threads;
match cli.subcommand {
SubCommands::IndexFromPack(IndexFromPack {
verbose,
iteration_mode,
memory_mode,
pack_path,
directory,
}) => {
let (_handle, progress) = prepare(verbose, "pack-explode");
core::pack::index::from_pack(
pack_path,
directory,
progress::DoOrDiscard::from(progress),
core::pack::index::Context {
thread_limit,
iteration_mode: iteration_mode.unwrap_or_default(),
memory_mode: memory_mode.unwrap_or_default(),
},
)
}
SubCommands::PackExplode(PackExplode {
pack_path,
sink_compress,
Expand All @@ -161,7 +233,7 @@ pub fn main() -> Result<()> {
core::pack::explode::pack_or_pack_index(
pack_path,
object_path,
check.unwrap_or(core::pack::explode::SafetyCheck::All),
check.unwrap_or_default(),
progress,
core::pack::explode::Context {
thread_limit,
Expand Down

0 comments on commit 9660bbf

Please sign in to comment.