@@ -134,29 +134,8 @@ fn makes_no_sense_error(source: &CliId, target: &CliId) -> String {
134
134
)
135
135
}
136
136
137
- fn ids ( ctx : & mut CommandContext , source : & str , target : & str ) -> anyhow:: Result < ( CliId , CliId ) > {
138
- let source_result = crate :: id:: CliId :: from_str ( ctx, source) ?;
139
- if source_result. len ( ) != 1 {
140
- if source_result. is_empty ( ) {
141
- return Err ( anyhow:: anyhow!(
142
- "Source '{}' not found. If you just performed a Git operation (squash, rebase, etc.), try running 'but status' to refresh the current state." ,
143
- source
144
- ) ) ;
145
- } else {
146
- let matches: Vec < String > = source_result. iter ( ) . map ( |id| {
147
- match id {
148
- CliId :: Commit { oid } => format ! ( "{} (commit {})" , id. to_string( ) , & oid. to_string( ) [ ..7 ] ) ,
149
- CliId :: Branch { name } => format ! ( "{} (branch '{}')" , id. to_string( ) , name) ,
150
- _ => format ! ( "{} ({})" , id. to_string( ) , id. kind( ) )
151
- }
152
- } ) . collect ( ) ;
153
- return Err ( anyhow:: anyhow!(
154
- "Source '{}' is ambiguous. Matches: {}. Try using more characters, a longer SHA, or the full branch name to disambiguate." ,
155
- source,
156
- matches. join( ", " )
157
- ) ) ;
158
- }
159
- }
137
+ fn ids ( ctx : & mut CommandContext , source : & str , target : & str ) -> anyhow:: Result < ( Vec < CliId > , CliId ) > {
138
+ let sources = parse_sources ( ctx, source) ?;
160
139
let target_result = crate :: id:: CliId :: from_str ( ctx, target) ?;
161
140
if target_result. len ( ) != 1 {
162
141
if target_result. is_empty ( ) {
@@ -179,7 +158,124 @@ fn ids(ctx: &mut CommandContext, source: &str, target: &str) -> anyhow::Result<(
179
158
) ) ;
180
159
}
181
160
}
182
- Ok ( ( source_result[ 0 ] . clone ( ) , target_result[ 0 ] . clone ( ) ) )
161
+ Ok ( ( sources, target_result[ 0 ] . clone ( ) ) )
162
+ }
163
+
164
+ fn parse_sources ( ctx : & mut CommandContext , source : & str ) -> anyhow:: Result < Vec < CliId > > {
165
+ // Check if it's a range (contains '-')
166
+ if source. contains ( '-' ) {
167
+ parse_range ( ctx, source)
168
+ }
169
+ // Check if it's a list (contains ',')
170
+ else if source. contains ( ',' ) {
171
+ parse_list ( ctx, source)
172
+ }
173
+ // Single source
174
+ else {
175
+ let source_result = crate :: id:: CliId :: from_str ( ctx, source) ?;
176
+ if source_result. len ( ) != 1 {
177
+ if source_result. is_empty ( ) {
178
+ return Err ( anyhow:: anyhow!(
179
+ "Source '{}' not found. If you just performed a Git operation (squash, rebase, etc.), try running 'but status' to refresh the current state." ,
180
+ source
181
+ ) ) ;
182
+ } else {
183
+ let matches: Vec < String > = source_result. iter ( ) . map ( |id| {
184
+ match id {
185
+ CliId :: Commit { oid } => format ! ( "{} (commit {})" , id. to_string( ) , & oid. to_string( ) [ ..7 ] ) ,
186
+ CliId :: Branch { name } => format ! ( "{} (branch '{}')" , id. to_string( ) , name) ,
187
+ _ => format ! ( "{} ({})" , id. to_string( ) , id. kind( ) )
188
+ }
189
+ } ) . collect ( ) ;
190
+ return Err ( anyhow:: anyhow!(
191
+ "Source '{}' is ambiguous. Matches: {}. Try using more characters, a longer SHA, or the full branch name to disambiguate." ,
192
+ source,
193
+ matches. join( ", " )
194
+ ) ) ;
195
+ }
196
+ }
197
+ Ok ( vec ! [ source_result[ 0 ] . clone( ) ] )
198
+ }
199
+ }
200
+
201
+ fn parse_range ( ctx : & mut CommandContext , source : & str ) -> anyhow:: Result < Vec < CliId > > {
202
+ let parts: Vec < & str > = source. split ( '-' ) . collect ( ) ;
203
+ if parts. len ( ) != 2 {
204
+ return Err ( anyhow:: anyhow!( "Range format should be 'start-end', got '{}'" , source) ) ;
205
+ }
206
+
207
+ let start_str = parts[ 0 ] ;
208
+ let end_str = parts[ 1 ] ;
209
+
210
+ // Get the start and end IDs
211
+ let start_matches = crate :: id:: CliId :: from_str ( ctx, start_str) ?;
212
+ let end_matches = crate :: id:: CliId :: from_str ( ctx, end_str) ?;
213
+
214
+ if start_matches. len ( ) != 1 {
215
+ return Err ( anyhow:: anyhow!( "Start of range '{}' must match exactly one item" , start_str) ) ;
216
+ }
217
+ if end_matches. len ( ) != 1 {
218
+ return Err ( anyhow:: anyhow!( "End of range '{}' must match exactly one item" , end_str) ) ;
219
+ }
220
+
221
+ let start_id = & start_matches[ 0 ] ;
222
+ let end_id = & end_matches[ 0 ] ;
223
+
224
+ // Get all files from status to find the range
225
+ let all_files = crate :: status:: all_files ( ctx) ?;
226
+
227
+ // Find the positions of start and end in the file list
228
+ let start_pos = all_files. iter ( ) . position ( |id| id == start_id) ;
229
+ let end_pos = all_files. iter ( ) . position ( |id| id == end_id) ;
230
+
231
+ if let ( Some ( start_idx) , Some ( end_idx) ) = ( start_pos, end_pos) {
232
+ if start_idx <= end_idx {
233
+ return Ok ( all_files[ start_idx..=end_idx] . to_vec ( ) ) ;
234
+ } else {
235
+ return Ok ( all_files[ end_idx..=start_idx] . to_vec ( ) ) ;
236
+ }
237
+ }
238
+
239
+ // If not found in files, try committed files
240
+ let all_committed_files = crate :: status:: all_committed_files ( ctx) ?;
241
+ let start_pos = all_committed_files. iter ( ) . position ( |id| id == start_id) ;
242
+ let end_pos = all_committed_files. iter ( ) . position ( |id| id == end_id) ;
243
+
244
+ if let ( Some ( start_idx) , Some ( end_idx) ) = ( start_pos, end_pos) {
245
+ if start_idx <= end_idx {
246
+ return Ok ( all_committed_files[ start_idx..=end_idx] . to_vec ( ) ) ;
247
+ } else {
248
+ return Ok ( all_committed_files[ end_idx..=start_idx] . to_vec ( ) ) ;
249
+ }
250
+ }
251
+
252
+ Err ( anyhow:: anyhow!( "Could not find range from '{}' to '{}' in the same file list" , start_str, end_str) )
253
+ }
254
+
255
+ fn parse_list ( ctx : & mut CommandContext , source : & str ) -> anyhow:: Result < Vec < CliId > > {
256
+ let parts: Vec < & str > = source. split ( ',' ) . collect ( ) ;
257
+ let mut result = Vec :: new ( ) ;
258
+
259
+ for part in parts {
260
+ let part = part. trim ( ) ;
261
+ let matches = crate :: id:: CliId :: from_str ( ctx, part) ?;
262
+ if matches. len ( ) != 1 {
263
+ if matches. is_empty ( ) {
264
+ return Err ( anyhow:: anyhow!(
265
+ "Item '{}' in list not found. If you just performed a Git operation (squash, rebase, etc.), try running 'but status' to refresh the current state." ,
266
+ part
267
+ ) ) ;
268
+ } else {
269
+ return Err ( anyhow:: anyhow!(
270
+ "Item '{}' in list is ambiguous. Try using more characters to disambiguate." ,
271
+ part
272
+ ) ) ;
273
+ }
274
+ }
275
+ result. push ( matches[ 0 ] . clone ( ) ) ;
276
+ }
277
+
278
+ Ok ( result)
183
279
}
184
280
185
281
fn create_snapshot ( ctx : & mut CommandContext , project : & Project , operation : OperationKind ) {
0 commit comments