diff options
Diffstat (limited to 'src/git.rs')
-rw-r--r-- | src/git.rs | 75 |
1 files changed, 48 insertions, 27 deletions
@@ -10,6 +10,7 @@ use std::io::prelude::*; use std::path::Path; use std::process::*; use std::str; +use uuid::Uuid; use crate::config::*; use log::*; @@ -78,8 +79,9 @@ pub trait Git: Sized { pub struct LiveGit { repo: Repository, - tag_cache: RefCell<HashMap<Option<String>, HashMap<Oid, Vec<Tag>>>>, config: GitConfig, + tag_cache: RefCell<HashMap<Option<String>, HashMap<Oid, Vec<Tag>>>>, + temp_repo_clone: RefCell<Option<TempRepo>>, } impl Git for LiveGit { @@ -336,27 +338,28 @@ impl Git for LiveGit { verification_commit: &Commit, ) -> Result<bool, Box<dyn Error>> { use git2::MergeOptions; - let temp_repo = TempRepo::new(&self.repo, verification_commit.id)?; - let commit = temp_repo.repo.find_commit(verification_commit.id)?; - let parents = commit.parents().collect::<Vec<_>>(); - - match &parents[..] { - [a, b] => { - let expected_tree_id = commit.tree_id(); - let reproduced_tree_id = temp_repo - .repo - .merge_commits(&a, &b, Some(MergeOptions::new().fail_on_conflict(true))) - .and_then(|mut index| index.write_tree_to(&temp_repo.repo)); - trace!("Checking for a trivial merge commit, expecting tree_id of {}, result of reproducing tree is {:?}", expected_tree_id, reproduced_tree_id); - let matches = reproduced_tree_id - .as_ref() - .map(|id| *id == expected_tree_id) - .unwrap_or(false); - - Ok(matches) + + self.with_temp_repo_clone(|temp_repo| { + let commit = temp_repo.repo.find_commit(verification_commit.id)?; + let parents = commit.parents().collect::<Vec<_>>(); + match &parents[..] { + [a, b] => { + let expected_tree_id = commit.tree_id(); + let reproduced_tree_id = temp_repo + .repo + .merge_commits(&a, &b, Some(MergeOptions::new().fail_on_conflict(true))) + .and_then(|mut index| index.write_tree_to(&temp_repo.repo)); + trace!("Checking for a trivial merge commit, expecting tree_id of {}, result of reproducing tree is {:?}", expected_tree_id, reproduced_tree_id); + let matches = reproduced_tree_id + .as_ref() + .map(|id| *id == expected_tree_id) + .unwrap_or(false); + + Ok(matches) + } + _ => Ok(false), } - _ => Ok(false), - } + }) } fn is_mainline(&self, ref_name: &str) -> Result<bool, Box<dyn Error>> { @@ -409,8 +412,9 @@ impl LiveGit { let repo = Repository::discover(path)?; Ok(LiveGit { repo, - tag_cache: RefCell::new(HashMap::new()), config: GitConfig::default(), + tag_cache: RefCell::new(HashMap::new()), + temp_repo_clone: RefCell::new(None), }) } @@ -418,8 +422,9 @@ impl LiveGit { let repo = Repository::discover(path)?; Ok(LiveGit { repo, - tag_cache: RefCell::new(HashMap::new()), config, + tag_cache: RefCell::new(HashMap::new()), + temp_repo_clone: RefCell::new(None), }) } @@ -463,6 +468,21 @@ impl LiveGit { .cloned() .unwrap_or(Vec::new()) } + + fn with_temp_repo_clone<T>( + &self, + f: impl Fn(&TempRepo) -> Result<T, Box<dyn Error>>, + ) -> Result<T, Box<dyn Error>> { + let mut cache = self.temp_repo_clone.borrow_mut(); + if let Some(temp_repo) = cache.as_ref() { + return f(temp_repo); + } else { + let temp_repo = TempRepo::new(&self.repo)?; + let result = f(&temp_repo); + *cache = Some(temp_repo); + result + } + } } struct TempRepo { @@ -470,18 +490,19 @@ struct TempRepo { } impl TempRepo { - fn new(src_repo: &Repository, commit_id: Oid) -> Result<TempRepo, Box<dyn Error>> { + fn new(src_repo: &Repository) -> Result<TempRepo, Box<dyn Error>> { let max_attempts = 20; let tmp_dir = std::env::temp_dir(); - for suffix in 0..max_attempts { - let tmp_repo_path = tmp_dir.join(format!("capn_tmp_{}_{}.git", commit_id, suffix)); + for attempt in 1..=max_attempts { + let tmp_repo_path = tmp_dir.join(format!("capn_tmp_{}.git", Uuid::new_v4())); match std::fs::create_dir(&tmp_repo_path) { Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue, Err(e) => return Err(Box::new(e)), Ok(_) => { trace!( - "Created temp repo for verification: {}", + "Created temp repo for verification after {} attempts: {}", + attempt, tmp_repo_path.display() ); let src_path = src_repo.path().to_str().ok_or(Box::new(CapnError::new( |