|
1 | 1 | use anyhow::{Context, Result}; |
2 | | -use git2::{Diff, Index, IndexEntry, IndexTime, Oid, Repository, Signature, Sort, Tree}; |
| 2 | +use git2::{ |
| 3 | + BlameOptions, Diff, Index, IndexEntry, IndexTime, Oid, Repository, Signature, Sort, Status, |
| 4 | + Tree, TreeWalkResult, |
| 5 | +}; |
3 | 6 | use regex::Regex; |
| 7 | +use std::fs::File; |
| 8 | +use std::io::{BufRead, BufReader}; |
4 | 9 | use std::path::{Path, PathBuf}; |
5 | 10 |
|
6 | 11 | use std::collections::{HashMap, HashSet}; |
@@ -263,6 +268,90 @@ pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()> |
263 | 268 | Ok(()) |
264 | 269 | } |
265 | 270 |
|
| 271 | +pub fn derive_no_history(repo: &Repository, name: &str, features: &[String]) -> Result<()> { |
| 272 | + let statuses = repo.statuses(None)?; |
| 273 | + for entry in statuses.iter() { |
| 274 | + let s = entry.status(); |
| 275 | + if s != Status::CURRENT { |
| 276 | + println!("Repository is not clean!"); |
| 277 | + return Ok(()); |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + let ref_name = format!("refs/heads/variant/{name}"); |
| 282 | + let sig = repo.signature()?; |
| 283 | + let variant_head_oid = create_variant_initial_commit(repo, &ref_name, &sig)?; |
| 284 | + |
| 285 | + let target_features: HashSet<String> = features.iter().cloned().collect(); |
| 286 | + let head = repo.head()?; |
| 287 | + let tree = head.peel_to_tree()?; |
| 288 | + |
| 289 | + let mut tree_builder = repo.treebuilder(None)?; |
| 290 | + |
| 291 | + tree.walk(git2::TreeWalkMode::PreOrder, |root, entry| { |
| 292 | + if let Some(name) = entry.name() { |
| 293 | + let path = Path::new(root).join(name); |
| 294 | + let content = process_file(repo, &path, &target_features).unwrap(); |
| 295 | + |
| 296 | + let oid = repo.blob(content.as_bytes()).unwrap(); |
| 297 | + tree_builder.insert(path, oid, 0o100644).unwrap(); |
| 298 | + } |
| 299 | + TreeWalkResult::Ok |
| 300 | + })?; |
| 301 | + |
| 302 | + let variant_tree_oid = tree_builder.write()?; |
| 303 | + let variant_tree = repo.find_tree(variant_tree_oid)?; |
| 304 | + let variant_parent = repo.find_commit(variant_head_oid)?; |
| 305 | + |
| 306 | + repo.commit( |
| 307 | + Some(&ref_name), |
| 308 | + &sig, |
| 309 | + &sig, |
| 310 | + "derive no history", |
| 311 | + &variant_tree, |
| 312 | + &[&variant_parent], |
| 313 | + )?; |
| 314 | + Ok(()) |
| 315 | +} |
| 316 | + |
| 317 | +fn process_file( |
| 318 | + repo: &Repository, |
| 319 | + name: &PathBuf, |
| 320 | + target_features: &HashSet<String>, |
| 321 | +) -> Result<String> { |
| 322 | + let file = File::open(name)?; |
| 323 | + let reader = BufReader::new(file); |
| 324 | + let mut lines: Vec<String> = reader.lines().collect::<Result<_, _>>()?; |
| 325 | + |
| 326 | + let mut commit_cache: HashMap<Oid, bool> = HashMap::new(); |
| 327 | + |
| 328 | + let mut blame_opts = BlameOptions::new(); |
| 329 | + let blame = repo.blame_file(Path::new(name), Some(&mut blame_opts))?; |
| 330 | + |
| 331 | + for (i, line) in lines.iter_mut().enumerate() { |
| 332 | + let line_no = i + 1; |
| 333 | + |
| 334 | + let hunk = blame |
| 335 | + .get_line(line_no) |
| 336 | + .context("Cant get line info in hunk")?; |
| 337 | + |
| 338 | + let oid = hunk.final_commit_id(); |
| 339 | + |
| 340 | + let contains_feature = commit_cache.entry(oid).or_insert_with(|| { |
| 341 | + let commit = repo.find_commit(oid).unwrap(); |
| 342 | + let summary = commit.summary().unwrap(); |
| 343 | + extract_scope(summary).is_some_and(|feature| target_features.contains(feature)) |
| 344 | + }); |
| 345 | + |
| 346 | + if !*contains_feature { |
| 347 | + *line = String::new(); |
| 348 | + } |
| 349 | + } |
| 350 | + |
| 351 | + // lines.retain(|line| !line.is_empty()); |
| 352 | + Ok(lines.join("\n")) |
| 353 | +} |
| 354 | + |
266 | 355 | // TODO: building each time can be expensive. Look for an alternative |
267 | 356 | fn extract_scope(commit: &str) -> Option<&str> { |
268 | 357 | let re = Regex::new(r"^(?P<type>[a-z]+)(?:\((?P<scope>[^)]+)\))?(?P<breaking>!)?:").unwrap(); |
|
0 commit comments