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 ;
10
6
11
7
use crate :: OutputFormat ;
12
8
@@ -17,11 +13,13 @@ pub enum Submodules {
17
13
RefChange ,
18
14
/// See if there are worktree modifications compared to the index, but do not check for untracked files.
19
15
Modifications ,
16
+ /// Ignore all submodule changes.
17
+ None ,
20
18
}
21
19
22
20
pub struct Options {
23
21
pub format : OutputFormat ,
24
- pub submodules : Submodules ,
22
+ pub submodules : Option < Submodules > ,
25
23
pub thread_limit : Option < usize > ,
26
24
pub statistics : bool ,
27
25
pub allow_write : bool ,
@@ -30,13 +28,12 @@ pub struct Options {
30
28
pub fn show (
31
29
repo : gix:: Repository ,
32
30
pathspecs : Vec < BString > ,
33
- out : impl std:: io:: Write ,
31
+ mut out : impl std:: io:: Write ,
34
32
mut err : impl std:: io:: Write ,
35
- mut progress : impl gix:: NestedProgress ,
33
+ mut progress : impl gix:: NestedProgress + ' static ,
36
34
Options {
37
35
format,
38
- // TODO: implement this
39
- submodules : _,
36
+ submodules,
40
37
thread_limit,
41
38
allow_write,
42
39
statistics,
@@ -45,198 +42,102 @@ pub fn show(
45
42
if format != OutputFormat :: Human {
46
43
bail ! ( "Only human format is supported right now" ) ;
47
44
}
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
+
51
46
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
- } ;
66
47
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
+ . should_interrupt_shared ( & gix:: interrupt:: IS_INTERRUPTED )
52
+ . index_worktree_options_mut ( |opts| {
53
+ opts. thread_limit = thread_limit;
54
+ opts. sorting = Some ( gix:: status:: plumbing:: index_as_worktree_with_renames:: Sorting :: ByPathCaseSensitive ) ;
55
+ } )
56
+ . index_worktree_submodules ( match submodules {
57
+ Some ( mode) => {
58
+ let ignore = match mode {
59
+ Submodules :: All => gix:: submodule:: config:: Ignore :: None ,
60
+ Submodules :: RefChange => gix:: submodule:: config:: Ignore :: Dirty ,
61
+ Submodules :: Modifications => gix:: submodule:: config:: Ignore :: Untracked ,
62
+ Submodules :: None => gix:: submodule:: config:: Ignore :: All ,
63
+ } ;
64
+ gix:: status:: Submodule :: Given {
65
+ ignore,
66
+ check_dirty : false ,
105
67
}
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
- }
137
-
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
- }
68
+ }
69
+ None => gix:: status:: Submodule :: AsConfigured { check_dirty : false } ,
70
+ } )
71
+ . into_index_worktree_iter ( pathspecs) ?;
72
+ for item in iter. by_ref ( ) {
73
+ let item = item?;
74
+ match item {
75
+ Item :: Modification {
76
+ entry : _,
77
+ entry_index : _,
78
+ rela_path,
79
+ status,
80
+ } => print_index_entry_status ( & mut out, prefix, rela_path. as_ref ( ) , status) ?,
81
+ Item :: DirectoryContents {
82
+ entry,
83
+ collapsed_directory_status,
84
+ } => {
85
+ if collapsed_directory_status. is_none ( ) {
86
+ writeln ! (
87
+ out,
88
+ "{status: >3} {rela_path}" ,
89
+ status = "?" ,
90
+ rela_path =
91
+ gix:: path:: relativize_with_prefix( & gix:: path:: from_bstr( entry. rela_path) , prefix) . display( )
92
+ ) ?;
150
93
}
151
94
}
95
+ Item :: Rewrite { .. } => { }
152
96
}
153
- index. write ( gix:: index:: write:: Options {
154
- extensions : Default :: default ( ) ,
155
- skip_hash : false , // TODO: make this based on configuration
156
- } ) ?;
157
97
}
158
-
159
- if statistics {
160
- writeln ! ( err, "{outcome:#?}" ) . ok ( ) ;
161
- writeln ! ( err, "{walk_outcome:#?}" ) . ok ( ) ;
98
+ if gix:: interrupt:: is_triggered ( ) {
99
+ bail ! ( "interrupted by user" ) ;
162
100
}
163
101
164
- writeln ! ( err, "\n head -> index and untracked files aren't implemented yet" ) ?;
165
- progress. show_throughput ( start) ;
166
- Ok ( ( ) )
167
- }
168
-
169
- #[ derive( Clone ) ]
170
- struct Submodule ;
102
+ let out = iter. outcome_mut ( ) . expect ( "successful iteration has outcome" ) ;
171
103
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 )
104
+ if out. has_changes ( ) && allow_write {
105
+ out. write_changes ( ) . transpose ( ) ?;
178
106
}
179
- }
180
107
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 ( ) ;
108
+ if statistics {
109
+ writeln ! ( err, "{outcome:#?}" , outcome = out. index_worktree) . ok ( ) ;
208
110
}
111
+
112
+ writeln ! ( err, "\n head -> index isn't implemented yet" ) ?;
113
+ progress. init ( Some ( out. index . entries ( ) . len ( ) ) , gix:: progress:: count ( "files" ) ) ;
114
+ progress. set ( out. index . entries ( ) . len ( ) ) ;
115
+ progress. show_throughput ( start) ;
116
+ Ok ( ( ) )
209
117
}
210
118
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
- } ;
119
+ fn print_index_entry_status (
120
+ out : & mut dyn std:: io:: Write ,
121
+ prefix : & Path ,
122
+ rela_path : & BStr ,
123
+ status : EntryStatus < ( ) , gix:: submodule:: Status > ,
124
+ ) -> std:: io:: Result < ( ) > {
125
+ let char_storage;
126
+ let status = match status {
127
+ EntryStatus :: Conflict ( conflict) => as_str ( conflict) ,
128
+ EntryStatus :: Change ( change) => {
129
+ char_storage = change_to_char ( & change) ;
130
+ std:: str:: from_utf8 ( std:: slice:: from_ref ( & char_storage) ) . expect ( "valid ASCII" )
131
+ }
132
+ EntryStatus :: NeedsUpdate ( _stat) => {
133
+ return Ok ( ( ) ) ;
134
+ }
135
+ EntryStatus :: IntentToAdd => "A" ,
136
+ } ;
235
137
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
- }
138
+ let rela_path = gix:: path:: from_bstr ( rela_path) ;
139
+ let display_path = gix:: path:: relativize_with_prefix ( & rela_path, prefix) ;
140
+ writeln ! ( out, "{status: >3} {}" , display_path. display( ) )
240
141
}
241
142
242
143
fn as_str ( c : Conflict ) -> & ' static str {
@@ -251,7 +152,7 @@ fn as_str(c: Conflict) -> &'static str {
251
152
}
252
153
}
253
154
254
- fn change_to_char ( change : & Change < ( ) > ) -> u8 {
155
+ fn change_to_char ( change : & Change < ( ) , gix :: submodule :: Status > ) -> u8 {
255
156
// Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613
256
157
match change {
257
158
Change :: Removed => b'D' ,
0 commit comments