1- use anyhow:: { bail, Context } ;
2- use gix:: bstr:: ByteSlice ;
3- use gix:: {
4- bstr:: { BStr , BString } ,
5- index:: Entry ,
6- Progress ,
7- } ;
8- use gix_status:: index_as_worktree:: { traits:: FastEq , Change , Conflict , EntryStatus } ;
9- use std:: path:: { Path , PathBuf } ;
1+ use anyhow:: bail;
2+ use gix:: bstr:: { BStr , BString } ;
3+ use gix:: status:: index_worktree:: iter:: Item ;
4+ use gix_status:: index_as_worktree:: { Change , Conflict , EntryStatus } ;
5+ use std:: path:: Path ;
106
117use crate :: OutputFormat ;
128
@@ -17,11 +13,13 @@ pub enum Submodules {
1713 RefChange ,
1814 /// See if there are worktree modifications compared to the index, but do not check for untracked files.
1915 Modifications ,
16+ /// Ignore all submodule changes.
17+ None ,
2018}
2119
2220pub struct Options {
2321 pub format : OutputFormat ,
24- pub submodules : Submodules ,
22+ pub submodules : Option < Submodules > ,
2523 pub thread_limit : Option < usize > ,
2624 pub statistics : bool ,
2725 pub allow_write : bool ,
@@ -30,13 +28,12 @@ pub struct Options {
3028pub fn show (
3129 repo : gix:: Repository ,
3230 pathspecs : Vec < BString > ,
33- out : impl std:: io:: Write ,
31+ mut out : impl std:: io:: Write ,
3432 mut err : impl std:: io:: Write ,
35- mut progress : impl gix:: NestedProgress ,
33+ mut progress : impl gix:: NestedProgress + ' static ,
3634 Options {
3735 format,
38- // TODO: implement this
39- submodules : _,
36+ submodules,
4037 thread_limit,
4138 allow_write,
4239 statistics,
@@ -45,198 +42,101 @@ pub fn show(
4542 if format != OutputFormat :: Human {
4643 bail ! ( "Only human format is supported right now" ) ;
4744 }
48- let mut index = repo. index_or_empty ( ) ?;
49- let index = gix:: threading:: make_mut ( & mut index) ;
50- let mut progress = progress. add_child ( "traverse index" ) ;
45+
5146 let start = std:: time:: Instant :: now ( ) ;
52- let stack = repo
53- . attributes_only (
54- index,
55- gix:: worktree:: stack:: state:: attributes:: Source :: WorktreeThenIdMapping ,
56- ) ?
57- . detach ( ) ;
58- let pathspec = gix:: Pathspec :: new ( & repo, false , pathspecs. iter ( ) . map ( |p| p. as_bstr ( ) ) , true , || {
59- Ok ( stack. clone ( ) )
60- } ) ?;
61- let options = gix_status:: index_as_worktree:: Options {
62- fs : repo. filesystem_options ( ) ?,
63- thread_limit,
64- stat : repo. stat_options ( ) ?,
65- } ;
6647 let prefix = repo. prefix ( ) ?. unwrap_or ( Path :: new ( "" ) ) ;
67- let mut printer = Printer {
68- out,
69- changes : Vec :: new ( ) ,
70- prefix : prefix. to_owned ( ) ,
71- } ;
72- let filter_pipeline = repo
73- . filter_pipeline ( Some ( gix:: hash:: ObjectId :: empty_tree ( repo. object_hash ( ) ) ) ) ?
74- . 0
75- . into_parts ( )
76- . 0 ;
77- let ctx = gix_status:: index_as_worktree:: Context {
78- pathspec : pathspec. into_parts ( ) . 0 ,
79- stack,
80- filter : filter_pipeline,
81- should_interrupt : & gix:: interrupt:: IS_INTERRUPTED ,
82- } ;
83- let mut collect = gix:: dir:: walk:: delegate:: Collect :: default ( ) ;
84- let ( outcome, walk_outcome) = gix:: features:: parallel:: threads ( |scope| -> anyhow:: Result < _ > {
85- // TODO: it's either this, or not running both in parallel and setting UPTODATE flags whereever
86- // there is no modification. This can save disk queries as dirwalk can then trust what's in
87- // the index regarding the type.
88- // NOTE: collect here as rename-tracking needs that anyway.
89- let walk_outcome = gix:: features:: parallel:: build_thread ( )
90- . name ( "gix status::dirwalk" . into ( ) )
91- . spawn_scoped ( scope, {
92- let repo = repo. clone ( ) . into_sync ( ) ;
93- let index = & index;
94- let collect = & mut collect;
95- move || -> anyhow:: Result < _ > {
96- let repo = repo. to_thread_local ( ) ;
97- let outcome = repo. dirwalk (
98- index,
99- pathspecs,
100- repo. dirwalk_options ( ) ?
101- . emit_untracked ( gix:: dir:: walk:: EmissionMode :: CollapseDirectory ) ,
102- collect,
103- ) ?;
104- Ok ( outcome. dirwalk )
48+ let index_progress = progress. add_child ( "traverse index" ) ;
49+ let mut iter = repo
50+ . status ( index_progress) ?
51+ . index_worktree_options_mut ( |opts| {
52+ opts. thread_limit = thread_limit;
53+ opts. sorting = Some ( gix:: status:: plumbing:: index_as_worktree_with_renames:: Sorting :: ByPathCaseSensitive ) ;
54+ } )
55+ . index_worktree_submodules ( match submodules {
56+ Some ( mode) => {
57+ let ignore = match mode {
58+ Submodules :: All => gix:: submodule:: config:: Ignore :: None ,
59+ Submodules :: RefChange => gix:: submodule:: config:: Ignore :: Dirty ,
60+ Submodules :: Modifications => gix:: submodule:: config:: Ignore :: Untracked ,
61+ Submodules :: None => gix:: submodule:: config:: Ignore :: All ,
62+ } ;
63+ gix:: status:: Submodule :: Given {
64+ ignore,
65+ check_dirty : false ,
10566 }
106- } ) ?;
107-
108- let outcome = gix_status:: index_as_worktree (
109- index,
110- repo. work_dir ( )
111- . context ( "This operation cannot be run on a bare repository" ) ?,
112- & mut printer,
113- FastEq ,
114- Submodule ,
115- repo. objects . clone ( ) . into_arc ( ) ?,
116- & mut progress,
117- ctx,
118- options,
119- ) ?;
120-
121- let walk_outcome = walk_outcome. join ( ) . expect ( "no panic" ) ?;
122- Ok ( ( outcome, walk_outcome) )
123- } ) ?;
124-
125- for entry in collect
126- . into_entries_by_path ( )
127- . into_iter ( )
128- . filter_map ( |( entry, dir_status) | dir_status. is_none ( ) . then_some ( entry) )
129- {
130- writeln ! (
131- printer. out,
132- "{status: >3} {rela_path}" ,
133- status = "?" ,
134- rela_path = gix:: path:: relativize_with_prefix( & gix:: path:: from_bstr( entry. rela_path) , prefix) . display( )
135- ) ?;
136- }
67+ }
68+ None => gix:: status:: Submodule :: AsConfigured { check_dirty : false } ,
69+ } )
70+ . into_index_worktree_iter ( pathspecs) ?;
71+ for item in iter. by_ref ( ) {
72+ let item = item?;
73+ if gix:: interrupt:: is_triggered ( ) {
74+ bail ! ( "interrupted by user" ) ;
75+ }
13776
138- if outcome. entries_to_update != 0 && allow_write {
139- {
140- let entries = index. entries_mut ( ) ;
141- for ( entry_index, change) in printer. changes {
142- let entry = & mut entries[ entry_index] ;
143- match change {
144- ApplyChange :: SetSizeToZero => {
145- entry. stat . size = 0 ;
146- }
147- ApplyChange :: NewStat ( new_stat) => {
148- entry. stat = new_stat;
149- }
77+ match item {
78+ Item :: Modification {
79+ entry : _,
80+ entry_index : _,
81+ rela_path,
82+ status,
83+ } => print_index_entry_status ( & mut out, prefix, rela_path. as_ref ( ) , status) ?,
84+ Item :: DirectoryContents {
85+ entry,
86+ collapsed_directory_status,
87+ } => {
88+ if collapsed_directory_status. is_none ( ) {
89+ writeln ! (
90+ out,
91+ "{status: >3} {rela_path}" ,
92+ status = "?" ,
93+ rela_path =
94+ gix:: path:: relativize_with_prefix( & gix:: path:: from_bstr( entry. rela_path) , prefix) . display( )
95+ ) ?;
15096 }
15197 }
98+ Item :: Rewrite { .. } => { }
15299 }
153- index. write ( gix:: index:: write:: Options {
154- extensions : Default :: default ( ) ,
155- skip_hash : false , // TODO: make this based on configuration
156- } ) ?;
100+ }
101+ let out = iter. outcome_mut ( ) . expect ( "successful iteration has outcome" ) ;
102+
103+ if out. has_changes ( ) && allow_write {
104+ out. write_changes ( ) . transpose ( ) ?;
157105 }
158106
159107 if statistics {
160- writeln ! ( err, "{outcome:#?}" ) . ok ( ) ;
161- writeln ! ( err, "{walk_outcome:#?}" ) . ok ( ) ;
108+ writeln ! ( err, "{outcome:#?}" , outcome = out. index_worktree) . ok ( ) ;
162109 }
163110
164- writeln ! ( err, "\n head -> index and untracked files aren't implemented yet" ) ?;
111+ writeln ! ( err, "\n head -> index isn't implemented yet" ) ?;
112+ progress. init ( Some ( out. index . entries ( ) . len ( ) ) , gix:: progress:: count ( "files" ) ) ;
113+ progress. set ( out. index . entries ( ) . len ( ) ) ;
165114 progress. show_throughput ( start) ;
166115 Ok ( ( ) )
167116}
168117
169- #[ derive( Clone ) ]
170- struct Submodule ;
171-
172- impl gix_status:: index_as_worktree:: traits:: SubmoduleStatus for Submodule {
173- type Output = ( ) ;
174- type Error = std:: convert:: Infallible ;
175-
176- fn status ( & mut self , _entry : & Entry , _rela_path : & BStr ) -> Result < Option < Self :: Output > , Self :: Error > {
177- Ok ( None )
178- }
179- }
180-
181- struct Printer < W > {
182- out : W ,
183- changes : Vec < ( usize , ApplyChange ) > ,
184- prefix : PathBuf ,
185- }
186-
187- enum ApplyChange {
188- SetSizeToZero ,
189- NewStat ( gix:: index:: entry:: Stat ) ,
190- }
191-
192- impl < ' index , W > gix_status:: index_as_worktree:: VisitEntry < ' index > for Printer < W >
193- where
194- W : std:: io:: Write ,
195- {
196- type ContentChange = ( ) ;
197- type SubmoduleStatus = ( ) ;
198-
199- fn visit_entry (
200- & mut self ,
201- _entries : & ' index [ Entry ] ,
202- _entry : & ' index Entry ,
203- entry_index : usize ,
204- rela_path : & ' index BStr ,
205- status : EntryStatus < Self :: ContentChange > ,
206- ) {
207- self . visit_inner ( entry_index, rela_path, status) . ok ( ) ;
208- }
209- }
210-
211- impl < W : std:: io:: Write > Printer < W > {
212- fn visit_inner ( & mut self , entry_index : usize , rela_path : & BStr , status : EntryStatus < ( ) > ) -> std:: io:: Result < ( ) > {
213- let char_storage;
214- let status = match status {
215- EntryStatus :: Conflict ( conflict) => as_str ( conflict) ,
216- EntryStatus :: Change ( change) => {
217- if matches ! (
218- change,
219- Change :: Modification {
220- set_entry_stat_size_zero: true ,
221- ..
222- }
223- ) {
224- self . changes . push ( ( entry_index, ApplyChange :: SetSizeToZero ) )
225- }
226- char_storage = change_to_char ( & change) ;
227- std:: str:: from_utf8 ( std:: slice:: from_ref ( & char_storage) ) . expect ( "valid ASCII" )
228- }
229- EntryStatus :: NeedsUpdate ( stat) => {
230- self . changes . push ( ( entry_index, ApplyChange :: NewStat ( stat) ) ) ;
231- return Ok ( ( ) ) ;
232- }
233- EntryStatus :: IntentToAdd => "A" ,
234- } ;
118+ fn print_index_entry_status (
119+ out : & mut dyn std:: io:: Write ,
120+ prefix : & Path ,
121+ rela_path : & BStr ,
122+ status : EntryStatus < ( ) , gix:: submodule:: Status > ,
123+ ) -> std:: io:: Result < ( ) > {
124+ let char_storage;
125+ let status = match status {
126+ EntryStatus :: Conflict ( conflict) => as_str ( conflict) ,
127+ EntryStatus :: Change ( change) => {
128+ char_storage = change_to_char ( & change) ;
129+ std:: str:: from_utf8 ( std:: slice:: from_ref ( & char_storage) ) . expect ( "valid ASCII" )
130+ }
131+ EntryStatus :: NeedsUpdate ( _stat) => {
132+ return Ok ( ( ) ) ;
133+ }
134+ EntryStatus :: IntentToAdd => "A" ,
135+ } ;
235136
236- let rela_path = gix:: path:: from_bstr ( rela_path) ;
237- let display_path = gix:: path:: relativize_with_prefix ( & rela_path, & self . prefix ) ;
238- writeln ! ( & mut self . out, "{status: >3} {}" , display_path. display( ) )
239- }
137+ let rela_path = gix:: path:: from_bstr ( rela_path) ;
138+ let display_path = gix:: path:: relativize_with_prefix ( & rela_path, prefix) ;
139+ writeln ! ( out, "{status: >3} {}" , display_path. display( ) )
240140}
241141
242142fn as_str ( c : Conflict ) -> & ' static str {
@@ -251,7 +151,7 @@ fn as_str(c: Conflict) -> &'static str {
251151 }
252152}
253153
254- fn change_to_char ( change : & Change < ( ) > ) -> u8 {
154+ fn change_to_char ( change : & Change < ( ) , gix :: submodule :: Status > ) -> u8 {
255155 // Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613
256156 match change {
257157 Change :: Removed => b'D' ,
0 commit comments