Unified · Split
| @@ -1210,6 +1210,7 @@ dependencies = [ · 1209 unmodified lines | |||
| 1210 | "rayon", | 1210 | "rayon", |
| 1211 | "serde", | 1211 | "serde", |
| 1212 | "serde_json", | 1212 | "serde_json", |
| 1213 | "tempfile", | ||
| 1213 | ] | 1214 | ] |
| 1214 | 1215 | ||
| 1215 | [[package]] | 1216 | [[package]] |
| @@ -22,6 +22,8 @@ pub struct FromEntriesIter<I, W> { · 21 unmodified lines | |||
| 22 | pub input: I, | 22 | pub input: I, |
| 23 | /// A way of writing encoded bytes. | 23 | /// A way of writing encoded bytes. |
| 24 | output: hash::Write<W>, | 24 | output: hash::Write<W>, |
| 25 | /// Our trailing hash when done writing all input entries | ||
| 26 | trailer: Option<git_hash::ObjectId>, | ||
| 25 | /// The amount of objects in the iteration and the version of the packfile to be written. | 27 | /// The amount of objects in the iteration and the version of the packfile to be written. |
| 26 | /// Will be `None` to signal the header was written already. | 28 | /// Will be `None` to signal the header was written already. |
| 27 | header_info: Option<(crate::data::Version, u32)>, | 29 | header_info: Option<(crate::data::Version, u32)>, |
| @@ -62,6 +64,7 @@ where · 34 unmodified lines | |||
| 62 | FromEntriesIter { | 64 | FromEntriesIter { |
| 63 | input, | 65 | input, |
| 64 | output: hash::Write::new(output, hash_kind), | 66 | output: hash::Write::new(output, hash_kind), |
| 67 | trailer: None, | ||
| 65 | entry_version: version, | 68 | entry_version: version, |
| 66 | header_info: Some((version, num_entries)), | 69 | header_info: Some((version, num_entries)), |
| 67 | is_done: false, | 70 | is_done: false, |
| @@ -75,6 +78,12 @@ where · 7 unmodified lines | |||
| 75 | self.output.inner | 78 | self.output.inner |
| 76 | } | 79 | } |
| 77 | 80 | ||
| 81 | /// Returns the trailing hash over all written entries once done. | ||
| 82 | /// It's `None` if we are not yet done writing. | ||
| 83 | pub fn digest(&self) -> Option<git_hash::ObjectId> { | ||
| 84 | self.trailer.clone() | ||
| 85 | } | ||
| 86 | |||
| 78 | fn next_inner(&mut self) -> Result<u64, Error<E>> { | 87 | fn next_inner(&mut self) -> Result<u64, Error<E>> { |
| 79 | let mut written = 0u64; | 88 | let mut written = 0u64; |
| 80 | if let Some((version, num_entries)) = self.header_info.take() { | 89 | if let Some((version, num_entries)) = self.header_info.take() { |
| @@ -98,6 +107,7 @@ where · 17 unmodified lines | |||
| 98 | written += digest.len() as u64; | 107 | written += digest.len() as u64; |
| 99 | self.output.flush()?; | 108 | self.output.flush()?; |
| 100 | self.is_done = true; | 109 | self.is_done = true; |
| 110 | self.trailer = Some(git_hash::ObjectId::from(digest)); | ||
| 101 | } | 111 | } |
| 102 | }; | 112 | }; |
| 103 | Ok(written) | 113 | Ok(written) |
| @@ -36,6 +36,7 @@ anyhow = "1.0.31" · 35 unmodified lines | |||
| 36 | quick-error = "2.0.0" | 36 | quick-error = "2.0.0" |
| 37 | bytesize = "1.0.1" | 37 | bytesize = "1.0.1" |
| 38 | serde_json = { version = "1.0.56", optional = true } | 38 | serde_json = { version = "1.0.56", optional = true } |
| 39 | tempfile = "3.1.0" | ||
| 39 | 40 | ||
| 40 | # for async-client | 41 | # for async-client |
| 41 | async-trait = { version = "0.1.50", optional = true } | 42 | async-trait = { version = "0.1.50", optional = true } |
| @@ -74,7 +74,7 @@ pub fn create( · 73 unmodified lines | |||
| 74 | repository: impl AsRef<Path>, | 74 | repository: impl AsRef<Path>, |
| 75 | tips: impl IntoIterator<Item = impl AsRef<OsStr>>, | 75 | tips: impl IntoIterator<Item = impl AsRef<OsStr>>, |
| 76 | input: Option<impl io::BufRead + Send + 'static>, | 76 | input: Option<impl io::BufRead + Send + 'static>, |
| 77 | out: impl io::Write, | 77 | output_directory: impl AsRef<Path>, |
| 78 | mut progress: impl Progress, | 78 | mut progress: impl Progress, |
| 79 | ctx: Context, | 79 | ctx: Context, |
| 80 | ) -> anyhow::Result<()> { | 80 | ) -> anyhow::Result<()> { |
| @@ -154,25 +154,27 @@ pub fn create( · 73 unmodified lines | |||
| 154 | write_progress.init(None, progress::bytes()); | 154 | write_progress.init(None, progress::bytes()); |
| 155 | let start = Instant::now(); | 155 | let start = Instant::now(); |
| 156 | 156 | ||
| 157 | let mut pack_file = tempfile::NamedTempFile::new_in(&output_directory)?; | ||
| 157 | let mut output_iter = pack::data::output::bytes::FromEntriesIter::new( | 158 | let mut output_iter = pack::data::output::bytes::FromEntriesIter::new( |
| 158 | entries.inspect(|e| { | 159 | entries.inspect(|e| { |
| 159 | if let Ok(entries) = e { | 160 | if let Ok(entries) = e { |
| 160 | entries_progress.inc_by(entries.len()) | 161 | entries_progress.inc_by(entries.len()) |
| 161 | } | 162 | } |
| 162 | }), | 163 | }), |
| 163 | out, | 164 | &mut pack_file, |
| 164 | num_objects as u32, | 165 | num_objects as u32, |
| 165 | pack::data::Version::default(), | 166 | pack::data::Version::default(), |
| 166 | hash::Kind::default(), | 167 | hash::Kind::default(), |
| 167 | ); | 168 | ); |
| 168 | while let Some(io_res) = output_iter.next() { | 169 | for io_res in output_iter.by_ref() { |
| 169 | if interrupt::is_triggered() { | 170 | if interrupt::is_triggered() { |
| 170 | bail!("Cancelled by user") | 171 | bail!("Cancelled by user") |
| 171 | } | 172 | } |
| 172 | let written = io_res?; | 173 | let written = io_res?; |
| 173 | write_progress.inc_by(written as usize); | 174 | write_progress.inc_by(written as usize); |
| 174 | } | 175 | } |
| 175 | output_iter.into_write().flush()?; | 176 | let hash = output_iter.digest().expect("iteration is done"); |
| 177 | pack_file.persist(output_directory.as_ref().join(format!("{}.pack", hash)))?; | ||
| 176 | 178 | ||
| 177 | write_progress.show_throughput(start); | 179 | write_progress.show_throughput(start); |
| 178 | entries_progress.show_throughput(start); | 180 | entries_progress.show_throughput(start); |
| @@ -22,15 +22,10 @@ pub fn main() -> Result<()> { · 21 unmodified lines | |||
| 22 | repository, | 22 | repository, |
| 23 | expansion, | 23 | expansion, |
| 24 | tips, | 24 | tips, |
| 25 | output_directory, | ||
| 25 | }) => { | 26 | }) => { |
| 26 | let (_handle, progress) = prepare(verbose, "pack-create", Some(core::pack::create::PROGRESS_RANGE)); | 27 | let (_handle, progress) = prepare(verbose, "pack-create", Some(core::pack::create::PROGRESS_RANGE)); |
| 27 | let has_tips = !tips.is_empty(); | 28 | let has_tips = !tips.is_empty(); |
| 28 | let stdout = stdout(); | ||
| 29 | let stdout_lock = stdout.lock(); | ||
| 30 | #[cfg(feature = "atty")] | ||
| 31 | if atty::is(atty::Stream::Stdout) { | ||
| 32 | anyhow::bail!("Refusing to output pack data stream to stdout.") | ||
| 33 | } | ||
| 34 | let input = if has_tips { | 29 | let input = if has_tips { |
| 35 | None | 30 | None |
| 36 | } else { | 31 | } else { |
| @@ -51,7 +46,7 @@ pub fn main() -> Result<()> { · 14 unmodified lines | |||
| 51 | repository.unwrap_or_else(|| PathBuf::from(".")), | 46 | repository.unwrap_or_else(|| PathBuf::from(".")), |
| 52 | tips, | 47 | tips, |
| 53 | input, | 48 | input, |
| 54 | stdout_lock, | 49 | output_directory, |
| 55 | DoOrDiscard::from(progress), | 50 | DoOrDiscard::from(progress), |
| 56 | core::pack::create::Context { | 51 | core::pack::create::Context { |
| 57 | expansion, | 52 | expansion, |
| @@ -176,6 +176,10 @@ pub struct PackCreate { · 175 unmodified lines | |||
| 176 | /// Possible values are "none" and "tree-traversal". Default is "none". | 176 | /// Possible values are "none" and "tree-traversal". Default is "none". |
| 177 | pub expansion: Option<core::pack::create::ObjectExpansion>, | 177 | pub expansion: Option<core::pack::create::ObjectExpansion>, |
| 178 | 178 | ||
| 179 | /// the directory into which to write the pack file. | ||
| 180 | #[argh(option, short = 'o')] | ||
| 181 | pub output_directory: PathBuf, | ||
| 182 | |||
| 179 | /// the tips from which to start the commit graph iteration. | 183 | /// the tips from which to start the commit graph iteration. |
| 180 | /// | 184 | /// |
| 181 | /// If empty, we expect to read objects on stdin and default to 'none' as expansion mode. | 185 | /// If empty, we expect to read objects on stdin and default to 'none' as expansion mode. |
| @@ -28,23 +28,16 @@ pub fn main() -> Result<()> { · 27 unmodified lines | |||
| 28 | repository, | 28 | repository, |
| 29 | expansion, | 29 | expansion, |
| 30 | tips, | 30 | tips, |
| 31 | output_directory, | ||
| 31 | } => { | 32 | } => { |
| 32 | let has_tips = !tips.is_empty(); | 33 | let has_tips = !tips.is_empty(); |
| 33 | let has_tui = progress; | ||
| 34 | if atty::is(if has_tui { | ||
| 35 | atty::Stream::Stderr | ||
| 36 | } else { | ||
| 37 | atty::Stream::Stdout | ||
| 38 | }) { | ||
| 39 | anyhow::bail!("Refusing to output pack data stream to stderr.") | ||
| 40 | } | ||
| 41 | prepare_and_run( | 34 | prepare_and_run( |
| 42 | "pack-create", | 35 | "pack-create", |
| 43 | verbose, | 36 | verbose, |
| 44 | progress, | 37 | progress, |
| 45 | progress_keep_open, | 38 | progress_keep_open, |
| 46 | core::pack::create::PROGRESS_RANGE, | 39 | core::pack::create::PROGRESS_RANGE, |
| 47 | move |progress, out, err| { | 40 | move |progress, _out, _err| { |
| 48 | let input = if has_tips { | 41 | let input = if has_tips { |
| 49 | None | 42 | None |
| 50 | } else { | 43 | } else { |
| @@ -67,11 +60,7 @@ pub fn main() -> Result<()> { · 16 unmodified lines | |||
| 67 | }), | 60 | }), |
| 68 | }; | 61 | }; |
| 69 | let progress = git_features::progress::DoOrDiscard::from(progress); | 62 | let progress = git_features::progress::DoOrDiscard::from(progress); |
| 70 | if has_tui { | 63 | core::pack::create(repository, tips, input, output_directory, progress, context) |
| 71 | core::pack::create(repository, tips, input, err, progress, context) | ||
| 72 | } else { | ||
| 73 | core::pack::create(repository, tips, input, out, progress, context) | ||
| 74 | } | ||
| 75 | }, | 64 | }, |
| 76 | ) | 65 | ) |
| 77 | } | 66 | } |
| @@ -56,6 +56,10 @@ pub enum Subcommands { · 55 unmodified lines | |||
| 56 | /// Possible values are "none" and "tree-traversal". Default is "none". | 56 | /// Possible values are "none" and "tree-traversal". Default is "none". |
| 57 | expansion: Option<core::pack::create::ObjectExpansion>, | 57 | expansion: Option<core::pack::create::ObjectExpansion>, |
| 58 | 58 | ||
| 59 | /// The directory into which to write the pack file. | ||
| 60 | #[clap(long, short = 'o')] | ||
| 61 | output_directory: PathBuf, | ||
| 62 | |||
| 59 | /// the tips from which to start the commit graph iteration. | 63 | /// the tips from which to start the commit graph iteration. |
| 60 | /// | 64 | /// |
| 61 | /// If empty, we expect to read objects on stdin and default to 'none' as expansion mode. | 65 | /// If empty, we expect to read objects on stdin and default to 'none' as expansion mode. |