summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCyborus <cyborus@cyborus.xyz>2024-05-17 20:24:27 +0200
committerCyborus <cyborus@cyborus.xyz>2024-05-17 20:24:27 +0200
commitb262d31b01ac5819ede18f7b53c81dccd68bfe1f (patch)
treed26be7c78d6af9e30b3d1912745c0d5bef902b49
parentMerge pull request 'rename `repo info` to `repo view`' (#64) from info-view i... (diff)
downloadforgejo-cli-b262d31b01ac5819ede18f7b53c81dccd68bfe1f.tar.xz
forgejo-cli-b262d31b01ac5819ede18f7b53c81dccd68bfe1f.zip
add `repo clone`
-rw-r--r--src/main.rs12
-rw-r--r--src/repo.rs92
2 files changed, 104 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
index ffc7446..92b547c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -190,6 +190,10 @@ struct SpecialRender {
light_grey: &'static str,
white: &'static str,
reset: &'static str,
+
+ hide_cursor: &'static str,
+ show_cursor: &'static str,
+ clear_line: &'static str,
}
impl SpecialRender {
@@ -227,6 +231,10 @@ impl SpecialRender {
light_grey: "\x1b[37m",
white: "\x1b[97m",
reset: "\x1b[0m",
+
+ hide_cursor: "\x1b[?25l",
+ show_cursor: "\x1b[?25h",
+ clear_line: "\x1b[2K",
}
}
@@ -255,6 +263,10 @@ impl SpecialRender {
light_grey: "",
white: "",
reset: "",
+
+ hide_cursor: "",
+ show_cursor: "",
+ clear_line: "",
}
}
}
diff --git a/src/repo.rs b/src/repo.rs
index 859d05f..bb19908 100644
--- a/src/repo.rs
+++ b/src/repo.rs
@@ -1,3 +1,5 @@
+use std::{io::Write, path::PathBuf};
+
use clap::Subcommand;
use eyre::{eyre, OptionExt};
use forgejo_api::structs::CreateRepoOption;
@@ -248,6 +250,10 @@ pub enum RepoCommand {
#[clap(long, short = 'R')]
remote: Option<String>,
},
+ Clone {
+ repo: String,
+ path: Option<PathBuf>,
+ },
Star {
repo: String,
},
@@ -446,6 +452,92 @@ impl RepoCommand {
println!("View online at {html_url}");
}
}
+ RepoCommand::Clone { repo, path } => {
+ let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
+ let api = keys.get_api(&repo.host_url())?;
+ let name = repo.name().unwrap();
+
+ let repo_data = api.repo_get(name.owner(), name.name()).await?;
+ let clone_url = repo_data
+ .clone_url
+ .as_ref()
+ .ok_or_eyre("repo does not have clone url")?;
+
+ let repo_name = repo_data
+ .name
+ .as_deref()
+ .ok_or_eyre("repo does not have name")?;
+ let repo_full_name = repo_data
+ .full_name
+ .as_deref()
+ .ok_or_eyre("repo does not have full name")?;
+
+ let path = path.unwrap_or_else(|| PathBuf::from(format!("./{repo_name}")));
+
+ let SpecialRender {
+ colors, // actually using it to indicate fanciness FIXME
+ hide_cursor,
+ show_cursor,
+ clear_line,
+ ..
+ } = *crate::special_render();
+
+ let mut options = git2::FetchOptions::new();
+
+ if colors {
+ print!("{hide_cursor}");
+ print!(" Preparing...");
+ let _ = std::io::stdout().flush();
+
+ let mut callbacks = git2::RemoteCallbacks::new();
+ callbacks.transfer_progress(|progress| {
+ print!("{clear_line}\r");
+ if progress.received_objects() == progress.total_objects() {
+ if progress.indexed_deltas() == progress.total_deltas() {
+ print!("Finishing up...");
+ } else {
+ let percent = 100.0 * (progress.indexed_deltas() as f64)
+ / (progress.total_deltas() as f64);
+ print!(" Resolving... {percent:.01}%");
+ }
+ } else {
+ let bytes = progress.received_bytes();
+ let percent = 100.0 * (progress.received_objects() as f64)
+ / (progress.total_objects() as f64);
+ print!(" Downloading... {percent:.01}%");
+ match bytes {
+ 0..=1023 => print!(" ({}b)", bytes),
+ 1024..=1048575 => print!(" ({:.01}kb)", (bytes as f64) / 1024.0),
+ 1048576..=1073741823 => {
+ print!(" ({:.01}mb)", (bytes as f64) / 1048576.0)
+ }
+ 1073741824.. => {
+ print!(" ({:.01}gb)", (bytes as f64) / 1073741824.0)
+ }
+ }
+ }
+ let _ = std::io::stdout().flush();
+ true
+ });
+ options.remote_callbacks(callbacks);
+ }
+
+ let local_repo = git2::build::RepoBuilder::new()
+ .fetch_options(options)
+ .clone(clone_url.as_str(), &path)?;
+ if colors {
+ print!("{clear_line}{show_cursor}\r");
+ }
+ println!("Cloned {} into {}", repo_full_name, path.display());
+
+ if let Some(parent) = repo_data.parent.as_deref() {
+ let parent_clone_url = parent
+ .clone_url
+ .as_ref()
+ .ok_or_eyre("parent repo does not have clone url")?;
+ local_repo.remote("upstream", parent_clone_url.as_str())?;
+ }
+ }
RepoCommand::Star { repo } => {
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
let api = keys.get_api(&repo.host_url())?;