11use anyhow:: { Context , Result } ;
2- use git2:: { Repository , Signature , Sort } ;
2+ use git2:: { Index , IndexEntry , IndexTime , Repository , Signature , Sort , Tree } ;
33use regex:: Regex ;
4- use std:: path:: PathBuf ;
4+ use std:: path:: { Path , PathBuf } ;
55
66use std:: collections:: { HashMap , HashSet } ;
77
8+ fn get_file_bytes_from_tree ( repo : & Repository , tree : & Tree , path : & Path ) -> Result < Vec < u8 > > {
9+ match tree. get_path ( path) {
10+ Ok ( entry) => {
11+ let blob = repo. find_blob ( entry. id ( ) ) ?;
12+ Ok ( blob. content ( ) . to_vec ( ) )
13+ }
14+ Err ( _) => Ok ( Vec :: new ( ) ) ,
15+ }
16+ }
17+
18+ fn split_lines_including_newline ( bytes : & [ u8 ] ) -> Vec < Vec < u8 > > {
19+ let mut out = Vec :: new ( ) ;
20+ let mut start = 0usize ;
21+
22+ for ( i, & b) in bytes. iter ( ) . enumerate ( ) {
23+ if b == b'\n' {
24+ out. push ( bytes[ start..=i] . to_vec ( ) ) ;
25+ start = i + 1 ;
26+ }
27+ }
28+
29+ if start < bytes. len ( ) {
30+ out. push ( bytes[ start..] . to_vec ( ) ) ;
31+ }
32+ out
33+ }
34+
35+ fn join_lines ( lines : & [ Vec < u8 > ] ) -> Vec < u8 > {
36+ let mut out = Vec :: new ( ) ;
37+ for line in lines {
38+ out. extend_from_slice ( line) ;
39+ }
40+ out
41+ }
42+
43+ fn ensure_length ( lines : & mut Vec < Vec < u8 > > , len : usize ) {
44+ while lines. len ( ) < len {
45+ lines. push ( b"\n " . to_vec ( ) ) ;
46+ }
47+ }
48+
49+ fn apply_additions ( mut base : Vec < u8 > , mut additions : Vec < ( u32 , Vec < u8 > ) > ) -> Vec < u8 > {
50+ additions. sort_by_key ( |( ln, _) | * ln) ;
51+ let mut lines = split_lines_including_newline ( & base) ;
52+
53+ for ( new_lineno, content) in additions {
54+ let idx = ( new_lineno as usize ) . saturating_sub ( 1 ) ;
55+
56+ ensure_length ( & mut lines, idx) ;
57+ if idx >= lines. len ( ) {
58+ lines. push ( content) ;
59+ } else {
60+ lines. insert ( idx, content) ;
61+ }
62+ }
63+
64+ join_lines ( & lines)
65+ }
66+
867pub fn derive ( repo : & Repository , name : & str , features : & [ String ] ) -> Result < ( ) > {
968 println ! ( "Deriving variant '{}' with features: {:?}" , name, features) ;
1069 // create a new orphan branch
@@ -13,7 +72,6 @@ pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()>
1372 . write ( )
1473 . context ( "Failed to create empty tree." ) ?;
1574 let tree = repo. find_tree ( tree_oid) ?;
16- println ! ( "Treeid: {:?}" , tree_oid) ;
1775
1876 let sig = Signature :: now ( "user" , "user@example.com" ) ?;
1977
@@ -83,17 +141,58 @@ pub fn derive(repo: &Repository, name: &str, features: &[String]) -> Result<()>
83141 true
84142 } ) ,
85143 ) ?;
86- for ( path, contents) in & additions {
87- println ! ( "Path: {:?}" , path) ;
88- for ( no, line) in contents {
89- print ! ( "{:?}: {}" , no, String :: from_utf8_lossy( line) ) ;
144+
145+ // write changes to disk without touching the worktree
146+ if !additions. is_empty ( ) {
147+ let variant_parent = repo. find_commit ( variant_head_oid) ?;
148+ let variant_tree = variant_parent. tree ( ) ?;
149+
150+ let mut index = Index :: new ( ) ?;
151+ index. read_tree ( & variant_tree) ?;
152+
153+ for ( path, contents) in additions {
154+ let base = get_file_bytes_from_tree ( repo, & variant_tree, & path) ?;
155+ let merged = apply_additions ( base, contents) ;
156+
157+ let blob_oid = repo. blob ( & merged) ?;
158+ let path_string = path. to_string_lossy ( ) . replace ( '\\' , "/" ) ;
159+ let entry = IndexEntry {
160+ ctime : IndexTime {
161+ seconds : 0 ,
162+ nanoseconds : 0 ,
163+ } ,
164+ mtime : IndexTime {
165+ seconds : 0 ,
166+ nanoseconds : 0 ,
167+ } ,
168+ dev : 0 ,
169+ ino : 0 ,
170+ mode : 0o100644 ,
171+ uid : 0 ,
172+ gid : 0 ,
173+ file_size : merged. len ( ) as u32 ,
174+ id : blob_oid,
175+ flags : 0 ,
176+ flags_extended : 0 ,
177+ path : path_string. into_bytes ( ) ,
178+ } ;
179+ index. add ( & entry) ?;
90180 }
181+ let new_tree_oid = index. write_tree_to ( repo) ?;
182+ let new_tree = repo. find_tree ( new_tree_oid) ?;
183+
184+ let commit_msg = commit. message ( ) . unwrap ( ) ;
185+ variant_head_oid = repo. commit (
186+ Some ( & ref_name) ,
187+ & sig,
188+ & sig,
189+ & commit_msg,
190+ & new_tree,
191+ & [ & variant_parent] ,
192+ ) ?;
91193 }
92194 }
93195
94- // for each commit that is in the specified feature set, extract the delta
95- // put together the delta
96- // add a custom reference to the newly created branch to keep track of variants
97196 let var_ref_name = format ! ( "refs/variants/{name}" ) ;
98197 let target_ref = repo. find_reference ( & ref_name) ?;
99198 let var_ref_target = target_ref. name ( ) . context ( "Branch has invalid UTF-8 name" ) ?;
0 commit comments