@@ -3,7 +3,10 @@ use std::{collections::HashMap, ffi::OsStr, io::Write, mem, sync::Arc};
3
3
use ansi_control_codes:: control_sequences:: { CUP , ED } ;
4
4
use anyhow:: { self , bail, Context as _} ;
5
5
use colored:: Colorize ;
6
+ use futures:: future:: try_join;
6
7
use lazy_static:: lazy_static;
8
+ #[ allow( unused_imports) ]
9
+ use log:: debug;
7
10
use regex:: Regex ;
8
11
9
12
use crate :: {
@@ -207,6 +210,60 @@ impl GraphBuffer {
207
210
}
208
211
}
209
212
213
+ // Helper for OutputBuffer - a way to grab log info for a bunch of commits with
214
+ // a single git command. It's important that we don't do N git commands, that
215
+ // can really slow things down when the range is large.
216
+
217
+ struct CommitInfoBuffer {
218
+ raw_buf : String , // Output straight from Git.
219
+ }
220
+
221
+ impl CommitInfoBuffer {
222
+ // log_format must not contain %x00 as that's used internally for splitting
223
+ // up the raw buffer.
224
+ pub async fn new (
225
+ repo : & Arc < impl Worktree > ,
226
+ range_spec : impl AsRef < OsStr > ,
227
+ log_format : & str ,
228
+ ) -> anyhow:: Result < Self > {
229
+ if log_format. contains ( "%x00" ) {
230
+ bail ! ( "NUL bytes not allowed in log format" ) ;
231
+ }
232
+ let raw_buf = repo
233
+ . log (
234
+ range_spec. as_ref ( ) ,
235
+ format ! ( "%H {log_format}%x00" ) ,
236
+ LogStyle :: NoGraph ,
237
+ )
238
+ . await ?;
239
+ // Hack: OsStr doesn't have a proper API, so just squash to utf-8, sorry
240
+ // users.
241
+ Ok ( Self {
242
+ raw_buf : String :: from_utf8_lossy ( & raw_buf) . to_string ( ) ,
243
+ } )
244
+ }
245
+
246
+ pub fn info ( & self ) -> anyhow:: Result < HashMap < CommitHash , & str > > {
247
+ let b = self . raw_buf . trim ( ) ;
248
+ let b = b. strip_suffix ( '\0' ) . unwrap_or ( b) ;
249
+ if b. is_empty ( ) {
250
+ return Ok ( HashMap :: new ( ) ) ;
251
+ }
252
+ b. split ( '\0' )
253
+ . map ( |raw_chunk| {
254
+ let [ hash, chunk] = raw_chunk
255
+ . splitn ( 2 , ' ' )
256
+ . collect :: < Vec < _ > > ( )
257
+ . try_into ( )
258
+ . map_err ( |v : Vec < _ > | {
259
+ anyhow:: anyhow!( "expected chunk split len 2, got {}" , v. len( ) )
260
+ } ) ?;
261
+ Ok ( ( CommitHash :: new ( hash. trim ( ) ) , chunk) )
262
+ } )
263
+ . collect ( )
264
+ }
265
+ }
266
+
210
267
// Represents the buffer showing the current status of all the commits being tested.
211
268
struct OutputBuffer {
212
269
// Pre-rendered lines containing static information (graph, commit log info etc).
@@ -254,22 +311,24 @@ impl OutputBuffer {
254
311
// buffer pairwise. If it has more lines then we will need to stretch
255
312
// out the graph vertically to make space first.
256
313
314
+ let ( graph_buf, info_buf) = try_join (
315
+ GraphBuffer :: new ( repo, range_spec. as_ref ( ) ) ,
316
+ CommitInfoBuffer :: new ( repo, range_spec. as_ref ( ) , log_format) ,
317
+ )
318
+ . await ?;
319
+
320
+ let commit_info = info_buf. info ( ) ?;
257
321
let mut lines = Vec :: new ( ) ;
258
322
let mut status_commits = HashMap :: new ( ) ;
259
- let graph_buf = GraphBuffer :: new ( repo, range_spec) . await ?;
260
323
for ( hash, mut chunk) in graph_buf. chunks ( ) ? {
261
- let log_n1_os = repo
262
- . log_n1 ( & hash, log_format)
263
- . await
264
- . context ( format ! ( "couldn't get commit data for {:?}" , hash) ) ?;
265
- // Hack: because OsStr doesn't have a proper API, luckily we can
266
- // just squash to utf-8, sorry users.
267
- let log_n1 = log_n1_os. to_string_lossy ( ) ;
324
+ let log_info = commit_info
325
+ . get ( & hash)
326
+ . with_context ( || format ! ( "missing commit info for {:?}" , hash) ) ?;
268
327
269
328
// We're gonna add our own newlines in so we don't need the one that
270
329
// Git printed.
271
- let log_n1 = log_n1 . strip_suffix ( '\n' ) . unwrap_or ( & log_n1 ) ;
272
- let mut info_lines: Vec < & str > = log_n1 . split ( '\n' ) . collect ( ) ;
330
+ let log_info = log_info . strip_suffix ( '\n' ) . unwrap_or ( log_info ) ;
331
+ let mut info_lines: Vec < & str > = log_info . split ( '\n' ) . collect ( ) ;
273
332
274
333
// Here's where we'll inject the live status
275
334
status_commits. insert ( lines. len ( ) + info_lines. len ( ) , hash) ;
0 commit comments