@@ -4,8 +4,8 @@ use gix::bstr::{BStr, BString};
44use gix:: index:: Entry ;
55use gix:: prelude:: FindExt ;
66use gix:: Progress ;
7- use gix_status:: index_as_worktree:: content :: FastEq ;
8- use gix_status:: index_as_worktree:: Change ;
7+ use gix_status:: index_as_worktree:: traits :: FastEq ;
8+ use gix_status:: index_as_worktree:: { Change , Conflict , EntryStatus } ;
99
1010pub enum Submodules {
1111 /// display all information about submodules, including ref changes, modifications and untracked files.
@@ -20,6 +20,8 @@ pub struct Options {
2020 pub format : OutputFormat ,
2121 pub submodules : Submodules ,
2222 pub thread_limit : Option < usize > ,
23+ pub statistics : bool ,
24+ pub allow_write : bool ,
2325}
2426
2527pub fn show (
@@ -33,12 +35,14 @@ pub fn show(
3335 // TODO: implement this
3436 submodules : _,
3537 thread_limit,
38+ allow_write,
39+ statistics,
3640 } : Options ,
3741) -> anyhow:: Result < ( ) > {
3842 if format != OutputFormat :: Human {
3943 bail ! ( "Only human format is supported right now" ) ;
4044 }
41- let mut index = repo. index ( ) ?;
45+ let mut index = repo. index_or_empty ( ) ?;
4246 let index = gix:: threading:: make_mut ( & mut index) ;
4347 let pathspec = repo. pathspec (
4448 pathspecs,
@@ -48,73 +52,174 @@ pub fn show(
4852 ) ?;
4953 let mut progress = progress. add_child ( "traverse index" ) ;
5054 let start = std:: time:: Instant :: now ( ) ;
51- gix_status:: index_as_worktree (
55+ let options = gix_status:: index_as_worktree:: Options {
56+ fs : repo. filesystem_options ( ) ?,
57+ thread_limit,
58+ stat : repo. stat_options ( ) ?,
59+ attributes : match repo
60+ . attributes_only (
61+ index,
62+ gix:: worktree:: stack:: state:: attributes:: Source :: WorktreeThenIdMapping ,
63+ ) ?
64+ . detach ( )
65+ . state_mut ( )
66+ {
67+ gix:: worktree:: stack:: State :: AttributesStack ( attrs) => std:: mem:: take ( attrs) ,
68+ // TODO: this should be nicer by creating attributes directly, but it's a private API
69+ _ => unreachable ! ( "state must be attributes stack only" ) ,
70+ } ,
71+ } ;
72+ let mut printer = Printer {
73+ out,
74+ changes : Vec :: new ( ) ,
75+ } ;
76+ let outcome = gix_status:: index_as_worktree (
5277 index,
5378 repo. work_dir ( )
5479 . context ( "This operation cannot be run on a bare repository" ) ?,
55- & mut Printer ( out ) ,
80+ & mut printer ,
5681 FastEq ,
82+ Submodule ,
5783 {
5884 let odb = repo. objects . clone ( ) . into_arc ( ) ?;
5985 move |id, buf| odb. find_blob ( id, buf)
6086 } ,
6187 & mut progress,
6288 pathspec. detach ( ) ?,
63- gix_status:: index_as_worktree:: Options {
64- fs : repo. filesystem_options ( ) ?,
65- thread_limit,
66- stat : repo. stat_options ( ) ?,
67- } ,
89+ repo. filter_pipeline ( Some ( gix:: hash:: ObjectId :: empty_tree ( repo. object_hash ( ) ) ) ) ?
90+ . 0
91+ . into_parts ( )
92+ . 0 ,
93+ & gix:: interrupt:: IS_INTERRUPTED ,
94+ options,
6895 ) ?;
6996
97+ if outcome. entries_to_update != 0 && allow_write {
98+ {
99+ let entries = index. entries_mut ( ) ;
100+ for ( entry_index, change) in printer. changes {
101+ let entry = & mut entries[ entry_index] ;
102+ match change {
103+ ApplyChange :: SetSizeToZero => {
104+ entry. stat . size = 0 ;
105+ }
106+ ApplyChange :: NewStat ( new_stat) => {
107+ entry. stat = new_stat;
108+ }
109+ }
110+ }
111+ }
112+ index. write ( gix:: index:: write:: Options {
113+ extensions : Default :: default ( ) ,
114+ skip_hash : false , // TODO: make this based on configuration
115+ } ) ?;
116+ }
117+
118+ if statistics {
119+ writeln ! ( err, "{outcome:#?}" ) . ok ( ) ;
120+ }
121+
70122 writeln ! ( err, "\n head -> index and untracked files aren't implemented yet" ) ?;
71123 progress. show_throughput ( start) ;
72124 Ok ( ( ) )
73125}
74126
75- struct Printer < W > ( W ) ;
127+ #[ derive( Clone ) ]
128+ struct Submodule ;
129+
130+ impl gix_status:: index_as_worktree:: traits:: SubmoduleStatus for Submodule {
131+ type Output = ( ) ;
132+ type Error = std:: convert:: Infallible ;
133+
134+ fn status ( & mut self , _entry : & Entry , _rela_path : & BStr ) -> Result < Option < Self :: Output > , Self :: Error > {
135+ Ok ( None )
136+ }
137+ }
138+
139+ struct Printer < W > {
140+ out : W ,
141+ changes : Vec < ( usize , ApplyChange ) > ,
142+ }
143+
144+ enum ApplyChange {
145+ SetSizeToZero ,
146+ NewStat ( gix:: index:: entry:: Stat ) ,
147+ }
76148
77149impl < ' index , W > gix_status:: index_as_worktree:: VisitEntry < ' index > for Printer < W >
78150where
79151 W : std:: io:: Write ,
80152{
81153 type ContentChange = ( ) ;
154+ type SubmoduleStatus = ( ) ;
82155
83156 fn visit_entry (
84157 & mut self ,
85- entry : & ' index Entry ,
158+ _entries : & ' index [ Entry ] ,
159+ _entry : & ' index Entry ,
160+ entry_index : usize ,
86161 rela_path : & ' index BStr ,
87- change : Option < Change < Self :: ContentChange > > ,
88- conflict : bool ,
162+ status : EntryStatus < Self :: ContentChange > ,
89163 ) {
90- self . visit_inner ( entry , rela_path, change , conflict ) . ok ( ) ;
164+ self . visit_inner ( entry_index , rela_path, status ) . ok ( ) ;
91165 }
92166}
93167
94168impl < W : std:: io:: Write > Printer < W > {
95- fn visit_inner (
96- & mut self ,
97- _entry : & Entry ,
98- rela_path : & BStr ,
99- change : Option < Change < ( ) > > ,
100- conflict : bool ,
101- ) -> anyhow:: Result < ( ) > {
102- if let Some ( change) = conflict
103- . then_some ( 'U' )
104- . or_else ( || change. as_ref ( ) . and_then ( change_to_char) )
105- {
106- writeln ! ( & mut self . 0 , "{change} {rela_path}" ) ?;
107- }
108- Ok ( ( ) )
169+ fn visit_inner ( & mut self , entry_index : usize , rela_path : & BStr , status : EntryStatus < ( ) > ) -> std:: io:: Result < ( ) > {
170+ let char_storage;
171+ let status = match status {
172+ EntryStatus :: Conflict ( conflict) => as_str ( conflict) ,
173+ EntryStatus :: Change ( change) => {
174+ if matches ! (
175+ change,
176+ Change :: Modification {
177+ set_entry_stat_size_zero: true ,
178+ ..
179+ }
180+ ) {
181+ self . changes . push ( ( entry_index, ApplyChange :: SetSizeToZero ) )
182+ }
183+ char_storage = change_to_char ( & change) ;
184+ std:: str:: from_utf8 ( std:: slice:: from_ref ( & char_storage) ) . expect ( "valid ASCII" )
185+ }
186+ EntryStatus :: NeedsUpdate ( stat) => {
187+ self . changes . push ( ( entry_index, ApplyChange :: NewStat ( stat) ) ) ;
188+ return Ok ( ( ) ) ;
189+ }
190+ EntryStatus :: IntentToAdd => "A" ,
191+ } ;
192+
193+ writeln ! ( & mut self . out, "{status: >3} {rela_path}" )
194+ }
195+ }
196+
197+ fn as_str ( c : Conflict ) -> & ' static str {
198+ match c {
199+ Conflict :: BothDeleted => "DD" ,
200+ Conflict :: AddedByUs => "AU" ,
201+ Conflict :: DeletedByThem => "UD" ,
202+ Conflict :: AddedByThem => "UA" ,
203+ Conflict :: DeletedByUs => "DU" ,
204+ Conflict :: BothAdded => "AA" ,
205+ Conflict :: BothModified => "UU" ,
109206 }
110207}
111208
112- fn change_to_char ( change : & Change < ( ) > ) -> Option < char > {
209+ fn change_to_char ( change : & Change < ( ) > ) -> u8 {
113210 // Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613
114- Some ( match change {
115- Change :: Removed => 'D' ,
116- Change :: Type => 'T' ,
117- Change :: Modification { .. } => 'M' ,
118- Change :: IntentToAdd => return None ,
119- } )
211+ match change {
212+ Change :: Removed => b'D' ,
213+ Change :: Type => b'T' ,
214+ Change :: SubmoduleModification ( _) => b'M' ,
215+ Change :: Modification {
216+ executable_bit_changed, ..
217+ } => {
218+ if * executable_bit_changed {
219+ b'X'
220+ } else {
221+ b'M'
222+ }
223+ }
224+ }
120225}
0 commit comments