@@ -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+
4269pub 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) ]
125158mod 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}
0 commit comments