summaryrefslogtreecommitdiffstats
path: root/examples/git.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/git.rs')
-rw-r--r--examples/git.rs146
1 files changed, 146 insertions, 0 deletions
diff --git a/examples/git.rs b/examples/git.rs
new file mode 100644
index 0000000..f3e613c
--- /dev/null
+++ b/examples/git.rs
@@ -0,0 +1,146 @@
+use std::path::{Path, PathBuf};
+
+#[derive(clap::Parser)]
+struct Options {
+ /// Show more verbose statement.
+ #[clap(long, short)]
+ #[clap(global = true)]
+ #[clap(action = clap::ArgAction::Count)]
+ verbose: u8,
+
+ /// The subcommand.
+ #[clap(subcommand)]
+ command: Command,
+}
+
+#[derive(clap::Subcommand)]
+enum Command {
+ Clone(CloneCommand),
+ Fetch(FetchCommand),
+ Push(PushCommand),
+}
+
+/// Clone a repository.
+#[derive(clap::Parser)]
+struct CloneCommand {
+ /// The URL of the repository to clone.
+ #[clap(value_name = "URL")]
+ repo: String,
+
+ /// The path where to clone the repository.
+ #[clap(value_name = "PATH")]
+ local_path: Option<PathBuf>,
+}
+
+/// Fetch from a remote.
+#[derive(clap::Parser)]
+struct FetchCommand {
+ /// The repository to operate on.
+ #[clap(value_name = "PATH")]
+ #[clap(short = 'C', long)]
+ repo: PathBuf,
+
+ /// The repository to operate on.
+ #[clap(value_name = "REMOTE")]
+ remote: String,
+
+ /// The refs to fetch.
+ #[clap(trailing_var_arg = true)]
+ #[clap(required = true)]
+ refspec: Vec<String>,
+}
+
+/// Push to a remote.
+#[derive(clap::Parser)]
+struct PushCommand {
+ /// The repository to operate on.
+ #[clap(value_name = "PATH")]
+ #[clap(short = 'C', long)]
+ #[clap(default_value = ".")]
+ repo: PathBuf,
+
+ /// The repository to operate on.
+ #[clap(value_name = "REMOTE")]
+ remote: String,
+
+ /// The refs to fetch.
+ #[clap(trailing_var_arg = true)]
+ #[clap(required = true)]
+ refspec: Vec<String>,
+}
+
+fn main() {
+ if let Err(()) = do_main(clap::Parser::parse()) {
+ std::process::exit(1);
+ }
+}
+
+fn log_level(verbose: u8) -> log::LevelFilter {
+ match verbose {
+ 0 => log::LevelFilter::Info,
+ 1 => log::LevelFilter::Debug,
+ 2.. => log::LevelFilter::Trace,
+ }
+}
+
+fn do_main(options: Options) -> Result<(), ()> {
+ let log_level = log_level(options.verbose);
+ env_logger::builder()
+ .parse_default_env()
+ .filter_module(module_path!(), log_level)
+ .filter_module("auth_git2", log_level)
+ .init();
+
+ match options.command {
+ Command::Clone(command) => clone(command),
+ Command::Fetch(command) => fetch(command),
+ Command::Push(command) => push(command),
+ }
+}
+
+fn clone(command: CloneCommand) -> Result<(), ()> {
+ let local_path = command.local_path.as_deref()
+ .unwrap_or_else(|| Path::new(repo_name_from_url(&command.repo)));
+
+ log::info!("Cloning {} into {}", command.repo, local_path.display());
+
+ let auth = auth_git2::GitAuthenticator::default();
+ auth.clone_repo(&command.repo, local_path)
+ .map_err(|e| log::error!("Failed to clone {}: {}", command.repo, e))?;
+ Ok(())
+}
+
+fn fetch(command: FetchCommand) -> Result<(), ()> {
+ let repo = git2::Repository::open(&command.repo)
+ .map_err(|e| log::error!("Failed to open git repo at {}: {e}", command.repo.display()))?;
+
+ let refspecs: Vec<_> = command.refspec.iter().map(|x| x.as_str()).collect();
+
+ let auth = auth_git2::GitAuthenticator::default();
+ let mut remote = repo.find_remote(&command.remote)
+ .map_err(|e| log::error!("Failed to find remote {:?}: {e}", command.remote))?;
+ auth.fetch(&repo, &mut remote, &refspecs, None)
+ .map_err(|e| log::error!("Failed to fetch from remote {:?}: {e}", command.remote))?;
+ Ok(())
+}
+
+fn push(command: PushCommand) -> Result<(), ()> {
+ let repo = git2::Repository::open(&command.repo)
+ .map_err(|e| log::error!("Failed to open git repo at {}: {e}", command.repo.display()))?;
+
+ log::info!("Fetching {:?} from remote {:?}", command.refspec, command.remote);
+ let refspecs: Vec<_> = command.refspec.iter().map(|x| x.as_str()).collect();
+
+ let auth = auth_git2::GitAuthenticator::default();
+ let mut remote = repo.find_remote(&command.remote)
+ .map_err(|e| log::error!("Failed to find remote {:?}: {e}", command.remote))?;
+ auth.push(&repo, &mut remote, &refspecs,)
+ .map_err(|e| log::error!("Failed to push to remote {:?}: {e}", command.remote))?;
+ Ok(())
+}
+
+fn repo_name_from_url(url: &str) -> &str {
+ url.rsplit_once('/')
+ .map(|(_head, tail)| tail)
+ .unwrap_or(url)
+}