…there really should be control over where to write them exactly
Unified · Split
| @@ -1,9 +1,12 @@ | |||
| 1 | use crate::{remote::refs::JsonRef, OutputFormat, Protocol}; | 1 | use crate::{remote::refs::JsonRef, OutputFormat, Protocol}; |
| 2 | use git_features::progress::Progress; | 2 | use git_features::progress::Progress; |
| 3 | use git_object::owned; | 3 | use git_object::{bstr::ByteSlice, owned}; |
| 4 | use git_odb::pack; | 4 | use git_odb::pack; |
| 5 | use git_protocol::fetch::{Action, Arguments, Ref, Response}; | 5 | use git_protocol::fetch::{Action, Arguments, Ref, Response}; |
| 6 | use std::{io, io::BufRead, path::PathBuf}; | 6 | use std::{ |
| 7 | io::{self, BufRead}, | ||
| 8 | path::PathBuf, | ||
| 9 | }; | ||
| 7 | 10 | ||
| 8 | pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3; | 11 | pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3; |
| 9 | 12 | ||
| @@ -16,6 +19,7 @@ pub struct Context<W: io::Write> { · 6 unmodified lines | |||
| 16 | struct CloneDelegate<W: io::Write> { | 19 | struct CloneDelegate<W: io::Write> { |
| 17 | ctx: Context<W>, | 20 | ctx: Context<W>, |
| 18 | directory: Option<PathBuf>, | 21 | directory: Option<PathBuf>, |
| 22 | write_refs: bool, | ||
| 19 | } | 23 | } |
| 20 | 24 | ||
| 21 | impl<W: io::Write> git_protocol::fetch::Delegate for CloneDelegate<W> { | 25 | impl<W: io::Write> git_protocol::fetch::Delegate for CloneDelegate<W> { |
| @@ -43,8 +47,30 @@ impl<W: io::Write> git_protocol::fetch::Delegate for CloneDelegate<W> { · 21 unmodified lines | |||
| 43 | index_kind: pack::index::Kind::V2, | 47 | index_kind: pack::index::Kind::V2, |
| 44 | iteration_mode: pack::data::iter::Mode::Verify, | 48 | iteration_mode: pack::data::iter::Mode::Verify, |
| 45 | }; | 49 | }; |
| 46 | let outcome = pack::bundle::Bundle::write_stream_to_directory(input, self.directory.take(), progress, options) | 50 | let outcome = pack::bundle::Bundle::write_stream_to_directory(input, self.directory.clone(), progress, options) |
| 47 | .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; | 51 | .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; |
| 52 | |||
| 53 | if let Some(directory) = self.directory.take() { | ||
| 54 | let assure_dir = |path: &git_object::bstr::BString| { | ||
| 55 | assert!(!path.starts_with_str("/"), "no ref start with a /, they are relative"); | ||
| 56 | let path = directory.join(path.to_path_lossy()); | ||
| 57 | std::fs::create_dir_all(path.parent().expect("multi-component path")).map(|_| path) | ||
| 58 | }; | ||
| 59 | if self.write_refs { | ||
| 60 | for r in refs { | ||
| 61 | match r { | ||
| 62 | Ref::Symbolic { path, target, .. } => { | ||
| 63 | assure_dir(path).map(|path| (path, format!("ref: {}", target))) | ||
| 64 | } | ||
| 65 | Ref::Peeled { path, tag: object, .. } | Ref::Direct { path, object } => { | ||
| 66 | assure_dir(path).map(|path| (path, object.to_string())) | ||
| 67 | } | ||
| 68 | } | ||
| 69 | .and_then(|(path, content)| std::fs::write(path, content.as_bytes()))?; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 48 | match self.ctx.format { | 74 | match self.ctx.format { |
| 49 | OutputFormat::Human => drop(print(&mut self.ctx.out, outcome, refs)), | 75 | OutputFormat::Human => drop(print(&mut self.ctx.out, outcome, refs)), |
| 50 | #[cfg(feature = "serde1")] | 76 | #[cfg(feature = "serde1")] |
| @@ -118,6 +144,7 @@ pub fn receive<P, W: io::Write>( · 67 unmodified lines | |||
| 118 | protocol: Option<Protocol>, | 144 | protocol: Option<Protocol>, |
| 119 | url: &str, | 145 | url: &str, |
| 120 | directory: Option<PathBuf>, | 146 | directory: Option<PathBuf>, |
| 147 | write_refs: bool, | ||
| 121 | progress: P, | 148 | progress: P, |
| 122 | ctx: Context<W>, | 149 | ctx: Context<W>, |
| 123 | ) -> anyhow::Result<()> | 150 | ) -> anyhow::Result<()> |
| @@ -127,7 +154,11 @@ where · 3 unmodified lines | |||
| 127 | <<P as Progress>::SubProgress as Progress>::SubProgress: Send, | 154 | <<P as Progress>::SubProgress as Progress>::SubProgress: Send, |
| 128 | { | 155 | { |
| 129 | let transport = git_protocol::git_transport::client::connect(url.as_bytes(), protocol.unwrap_or_default().into())?; | 156 | let transport = git_protocol::git_transport::client::connect(url.as_bytes(), protocol.unwrap_or_default().into())?; |
| 130 | let mut delegate = CloneDelegate { ctx, directory }; | 157 | let mut delegate = CloneDelegate { |
| 158 | ctx, | ||
| 159 | directory, | ||
| 160 | write_refs, | ||
| 161 | }; | ||
| 131 | git_protocol::fetch(transport, &mut delegate, git_protocol::credentials::helper, progress)?; | 162 | git_protocol::fetch(transport, &mut delegate, git_protocol::credentials::helper, progress)?; |
| 132 | Ok(()) | 163 | Ok(()) |
| 133 | } | 164 | } |
| @@ -53,12 +53,14 @@ pub fn main() -> Result<()> { · 52 unmodified lines | |||
| 53 | protocol, | 53 | protocol, |
| 54 | url, | 54 | url, |
| 55 | directory, | 55 | directory, |
| 56 | write_refs, | ||
| 56 | }) => { | 57 | }) => { |
| 57 | let (_handle, progress) = prepare(verbose, "pack-receive", core::pack::receive::PROGRESS_RANGE); | 58 | let (_handle, progress) = prepare(verbose, "pack-receive", core::pack::receive::PROGRESS_RANGE); |
| 58 | core::pack::receive( | 59 | core::pack::receive( |
| 59 | protocol, | 60 | protocol, |
| 60 | &url, | 61 | &url, |
| 61 | directory, | 62 | directory, |
| 63 | write_refs, | ||
| 62 | progress::DoOrDiscard::from(progress), | 64 | progress::DoOrDiscard::from(progress), |
| 63 | core::pack::receive::Context { | 65 | core::pack::receive::Context { |
| 64 | thread_limit, | 66 | thread_limit, |
| @@ -92,6 +92,10 @@ pub struct PackReceive { · 91 unmodified lines | |||
| 92 | #[argh(option, short = 'p')] | 92 | #[argh(option, short = 'p')] |
| 93 | pub protocol: Option<core::Protocol>, | 93 | pub protocol: Option<core::Protocol>, |
| 94 | 94 | ||
| 95 | /// write references to the given directory as well, right next to the pack and index file. | ||
| 96 | #[argh(switch, short = 'r')] | ||
| 97 | pub write_refs: bool, | ||
| 98 | |||
| 95 | /// the URLs or path from which to receive the pack. | 99 | /// the URLs or path from which to receive the pack. |
| 96 | /// | 100 | /// |
| 97 | /// See here for a list of supported URLs: https://www.git-scm.com/docs/git-clone#_git_urls | 101 | /// See here for a list of supported URLs: https://www.git-scm.com/docs/git-clone#_git_urls |
| @@ -127,6 +127,7 @@ pub fn main() -> Result<()> { · 126 unmodified lines | |||
| 127 | match cmd { | 127 | match cmd { |
| 128 | Subcommands::PackReceive { | 128 | Subcommands::PackReceive { |
| 129 | protocol, | 129 | protocol, |
| 130 | write_refs, | ||
| 130 | url, | 131 | url, |
| 131 | directory, | 132 | directory, |
| 132 | } => prepare_and_run( | 133 | } => prepare_and_run( |
| @@ -140,6 +141,7 @@ pub fn main() -> Result<()> { · 7 unmodified lines | |||
| 140 | protocol, | 141 | protocol, |
| 141 | &url, | 142 | &url, |
| 142 | directory, | 143 | directory, |
| 144 | write_refs, | ||
| 143 | git_features::progress::DoOrDiscard::from(progress), | 145 | git_features::progress::DoOrDiscard::from(progress), |
| 144 | core::pack::receive::Context { | 146 | core::pack::receive::Context { |
| 145 | thread_limit, | 147 | thread_limit, |
| @@ -49,6 +49,10 @@ pub enum Subcommands { · 48 unmodified lines | |||
| 49 | #[clap(long, short = "p")] | 49 | #[clap(long, short = "p")] |
| 50 | protocol: Option<core::Protocol>, | 50 | protocol: Option<core::Protocol>, |
| 51 | 51 | ||
| 52 | /// Write references to the given directory as well, right next to the pack and index file. | ||
| 53 | #[clap(long, short = "r")] | ||
| 54 | write_refs: bool, | ||
| 55 | |||
| 52 | /// The URLs or path from which to receive the pack. | 56 | /// The URLs or path from which to receive the pack. |
| 53 | /// | 57 | /// |
| 54 | /// See here for a list of supported URLs: https://www.git-scm.com/docs/git-clone#_git_urls | 58 | /// See here for a list of supported URLs: https://www.git-scm.com/docs/git-clone#_git_urls |
| @@ -0,0 +1 @@ | |||
| 1 | ee3c97678e89db4eab7420b04aef51758359f152 | ||
| @@ -0,0 +1 @@ | |||
| 1 | 3f72b39ad1600e6dac63430c15e0d875e9d3f9d6 | ||
| @@ -0,0 +1 @@ | |||
| 1 | feae03400632392a7f38e5b2775f98a439f5eaf5 | ||
| @@ -0,0 +1 @@ | |||
| 1 | efa596d621559707b2d221f10490959b2decbc6c | ||
| @@ -106,6 +106,15 @@ snapshot="$snapshot/plumbing" · 105 unmodified lines | |||
| 106 | WITH_SNAPSHOT="$snapshot/ls-in-output-dir" \ | 106 | WITH_SNAPSHOT="$snapshot/ls-in-output-dir" \ |
| 107 | expect_run $SUCCESSFULLY ls out/ | 107 | expect_run $SUCCESSFULLY ls out/ |
| 108 | } | 108 | } |
| 109 | (with "--write-refs set" | ||
| 110 | it "generates the correct output" && { | ||
| 111 | WITH_SNAPSHOT="$snapshot/file-v-any-with-output" \ | ||
| 112 | expect_run $SUCCESSFULLY "$exe_plumbing" pack-receive -p 1 --write-refs .git out/ | ||
| 113 | } | ||
| 114 | it "writes references into the refs folder of the output directory" && { | ||
| 115 | expect_snapshot "$snapshot/repo-refs" out/refs | ||
| 116 | } | ||
| 117 | ) | ||
| 109 | ) | 118 | ) |
| 110 | if test "$kind" = "max"; then | 119 | if test "$kind" = "max"; then |
| 111 | (with "--format json" | 120 | (with "--format json" |