Skip to content

Commit 060e359

Browse files
committed
wip!: variant derivation
1 parent 71226b4 commit 060e359

2 files changed

Lines changed: 68 additions & 1 deletion

File tree

src/lib.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,33 @@ pub fn init(repo: &Repository) -> Result<()> {
3939
Ok(())
4040
}
4141

42+
pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()> {
43+
println!("Deriving variant '{}' with features: {:?}", name, features);
44+
// create a new orphan branch
45+
// walk the commit graph
46+
let mut revwalk = repo.revwalk()?;
47+
revwalk.push_head()?;
48+
revwalk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME | Sort::REVERSE)?;
49+
50+
let target_features: HashSet<String> = features.iter().cloned().collect();
51+
for oid in revwalk {
52+
let oid = oid?;
53+
let commit = repo.find_commit(oid)?;
54+
55+
let summary = commit.summary().context("Error extracting summary")?;
56+
if let Some(feature) = extract_scope(summary) {
57+
if target_features.contains(feature) {
58+
println!("Applying commit: {} {}", oid, summary);
59+
}
60+
}
61+
}
62+
63+
// for each commit that is in the specified feature set, extract the delta
64+
// put together the delta
65+
// add a custom reference to the newly created branch to keep track of variants
66+
Ok(())
67+
}
68+
4269
pub fn diff(repo: &Repository, target: &str, reverse: bool) -> Result<()> {
4370
let current_ref = repo.head()?;
4471
let target_ref = repo
@@ -121,6 +148,12 @@ fn extract_change_id(msg: &str) -> Option<String> {
121148
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
122149
}
123150

151+
fn extract_scope(commit: &str) -> Option<&str> {
152+
let re = Regex::new(r"^(?P<type>[a-z]+)(?:\((?P<scope>[^)]+)\))?(?P<breaking>!)?:").unwrap();
153+
re.captures(commit)
154+
.and_then(|caps| caps.name("scope").map(|m| m.as_str()))
155+
}
156+
124157
#[cfg(test)]
125158
mod tests {
126159
use super::*;
@@ -160,4 +193,27 @@ CHANGE_ID: fec54ceb-74fe-4c6e-b467-cee066ffa268abc
160193
let result = extract_change_id(msg);
161194
assert_eq!(result, None);
162195
}
196+
197+
#[test]
198+
fn extracts_scope_when_present() {
199+
assert_eq!(extract_scope("feat(auth): add login"), Some("auth"));
200+
}
201+
202+
#[test]
203+
fn extracts_scope_with_breaking_change() {
204+
assert_eq!(
205+
extract_scope("feat(api)!: change response format"),
206+
Some("api")
207+
);
208+
}
209+
210+
#[test]
211+
fn returns_none_when_no_scope() {
212+
assert_eq!(extract_scope("fix: missing semicolon"), None);
213+
}
214+
215+
#[test]
216+
fn does_not_match_invalid_format() {
217+
assert_eq!(extract_scope("not a conventional commit"), None);
218+
}
163219
}

src/main.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use anyhow::{Context, Result};
22
use clap::{Parser, Subcommand};
33
use git2::Repository;
44

5-
use git_varcs::{diff, init};
5+
use git_varcs::{derive, diff, init};
66

77
#[derive(Parser)]
88
#[command(name = "git-vms", version)]
@@ -15,6 +15,14 @@ struct Cli {
1515
enum Commands {
1616
/// Initialize variant management support in the git repository
1717
Init,
18+
/// Derive a new variant from the specified set of features
19+
Derive {
20+
/// The name of the variant
21+
name: String,
22+
/// The set of features to use for the derivation
23+
#[arg(short, long = "feature", required = true)]
24+
features: Vec<String>,
25+
},
1826
/// List the commits exist in the target branch but not in the current branch
1927
Diff {
2028
/// The branch to compare against
@@ -33,6 +41,9 @@ fn main() -> Result<()> {
3341
Commands::Init => {
3442
init(&repo).context("Failed to initialize VMS support")?;
3543
}
44+
Commands::Derive { name, features } => {
45+
derive(&repo, &name, &features).context("Failed to derive variant")?;
46+
}
3647
Commands::Diff { target, reverse } => {
3748
diff(&repo, &target, reverse).context(format!(
3849
"Failed to diff current branch against '{}'",

0 commit comments

Comments
 (0)