LCOV - code coverage report
Current view: top level - internal/src - git.rs (source / functions) Hit Total Coverage
Test: unnamed Lines: 32 48 66.7 %
Date: 2024-10-23 19:20:50 Functions: 3 9 33.3 %

          Line data    Source code
       1             : use crate::CommandExt;
       2             : use anyhow::{Context, Result};
       3             : use git2::Repository;
       4             : use if_chain::if_chain;
       5             : use std::{
       6             :     path::Path,
       7             :     process::{Command, Stdio},
       8             : };
       9             : 
      10             : // smoelius: I think this imitates Cargo's default behavior:
      11             : // https://doc.rust-lang.org/cargo/reference/config.html#netretry
      12             : const N_RETRIES: usize = 2;
      13             : 
      14             : // smoelius: I think I may have run into https://github.com/libgit2/libgit2/issues/5294 a few times,
      15             : // but I don't know of a good general-purpose solution. TODO: Investigate whether/how Cargo's
      16             : // wrappers handle this.
      17           9 : pub fn clone(url: &str, refname: &str, path: &Path, quiet: bool) -> Result<Repository> {
      18           9 :     let repository = if Command::new("git")
      19           9 :         .args(["--version"])
      20           9 :         .stdout(Stdio::null())
      21           9 :         .success()
      22           9 :         .is_ok()
      23             :     {
      24           9 :         clone_with_cli(url, path, quiet)
      25             :     } else {
      26           0 :         clone_with_git2(url, path, quiet)
      27           0 :     }?;
      28             : 
      29           9 :     checkout(&repository, refname)?;
      30             : 
      31           9 :     Ok(repository)
      32           9 : }
      33             : 
      34           9 : fn clone_with_cli(url: &str, path: &Path, quiet: bool) -> Result<Repository> {
      35           9 :     let mut command = Command::new("git");
      36           9 :     command.args(["clone", url, &path.to_string_lossy()]);
      37           9 :     if quiet {
      38           0 :         command.args(["--quiet"]);
      39           9 :     }
      40           9 :     command.success()?;
      41             : 
      42           9 :     Repository::open(path).map_err(Into::into)
      43           9 : }
      44             : 
      45           0 : fn clone_with_git2(url: &str, path: &Path, _quiet: bool) -> Result<Repository> {
      46           0 :     let mut result = Repository::clone(url, path);
      47             : 
      48           0 :     for _ in 0..N_RETRIES {
      49           0 :         if result.is_err() {
      50           0 :             result = Repository::clone(url, path);
      51           0 :         } else {
      52           0 :             break;
      53             :         }
      54             :     }
      55             : 
      56           0 :     result.map_err(Into::into)
      57           0 : }
      58             : 
      59             : // smoelius: `checkout` is based on: https://stackoverflow.com/a/67240436
      60           9 : pub fn checkout(repository: &Repository, refname: &str) -> Result<()> {
      61           9 :     let (object, reference) = repository
      62           9 :         .revparse_ext(refname)
      63           9 :         .with_context(|| format!("`revparse_ext` failed for `{refname}`"))?;
      64             : 
      65           9 :     repository
      66           9 :         .checkout_tree(&object, None)
      67           9 :         .with_context(|| format!("`checkout_tree` failed for `{object:?}`"))?;
      68             : 
      69           0 :     if_chain! {
      70           9 :         if let Some(reference) = reference;
      71           9 :         if let Some(refname) = reference.name();
      72             :         then {
      73           9 :             repository
      74           9 :                 .set_head(refname)
      75           9 :                 .with_context(|| format!("`set_head` failed for `{refname}`"))?;
      76             :         } else {
      77           0 :             repository
      78           0 :                 .set_head_detached(object.id())
      79           0 :                 .with_context(|| format!("`set_head_detached` failed for `{}`", object.id()))?;
      80             :         }
      81             :     }
      82             : 
      83           9 :     Ok(())
      84           9 : }

Generated by: LCOV version 1.14