@@ -2332,69 +2332,138 @@ int read_ref_at(const char *refname, unsigned long at_time, int cnt,
23322332 return 1 ;
23332333}
23342334
2335- int for_each_recent_reflog_ent (const char * refname , each_reflog_ent_fn fn , long ofs , void * cb_data )
2335+ static int show_one_reflog_ent (struct strbuf * sb , each_reflog_ent_fn fn , void * cb_data )
2336+ {
2337+ unsigned char osha1 [20 ], nsha1 [20 ];
2338+ char * email_end , * message ;
2339+ unsigned long timestamp ;
2340+ int tz ;
2341+
2342+ /* old SP new SP name <email> SP time TAB msg LF */
2343+ if (sb -> len < 83 || sb -> buf [sb -> len - 1 ] != '\n' ||
2344+ get_sha1_hex (sb -> buf , osha1 ) || sb -> buf [40 ] != ' ' ||
2345+ get_sha1_hex (sb -> buf + 41 , nsha1 ) || sb -> buf [81 ] != ' ' ||
2346+ !(email_end = strchr (sb -> buf + 82 , '>' )) ||
2347+ email_end [1 ] != ' ' ||
2348+ !(timestamp = strtoul (email_end + 2 , & message , 10 )) ||
2349+ !message || message [0 ] != ' ' ||
2350+ (message [1 ] != '+' && message [1 ] != '-' ) ||
2351+ !isdigit (message [2 ]) || !isdigit (message [3 ]) ||
2352+ !isdigit (message [4 ]) || !isdigit (message [5 ]))
2353+ return 0 ; /* corrupt? */
2354+ email_end [1 ] = '\0' ;
2355+ tz = strtol (message + 1 , NULL , 10 );
2356+ if (message [6 ] != '\t' )
2357+ message += 6 ;
2358+ else
2359+ message += 7 ;
2360+ return fn (osha1 , nsha1 , sb -> buf + 82 , timestamp , tz , message , cb_data );
2361+ }
2362+
2363+ static char * find_beginning_of_line (char * bob , char * scan )
2364+ {
2365+ while (bob < scan && * (-- scan ) != '\n' )
2366+ ; /* keep scanning backwards */
2367+ /*
2368+ * Return either beginning of the buffer, or LF at the end of
2369+ * the previous line.
2370+ */
2371+ return scan ;
2372+ }
2373+
2374+ int for_each_reflog_ent_reverse (const char * refname , each_reflog_ent_fn fn , void * cb_data )
23362375{
2337- const char * logfile ;
2338- FILE * logfp ;
23392376 struct strbuf sb = STRBUF_INIT ;
2340- int ret = 0 ;
2377+ FILE * logfp ;
2378+ long pos ;
2379+ int ret = 0 , at_tail = 1 ;
23412380
2342- logfile = git_path ("logs/%s" , refname );
2343- logfp = fopen (logfile , "r" );
2381+ logfp = fopen (git_path ("logs/%s" , refname ), "r" );
23442382 if (!logfp )
23452383 return -1 ;
23462384
2347- if (ofs ) {
2348- struct stat statbuf ;
2349- if (fstat (fileno (logfp ), & statbuf ) ||
2350- statbuf .st_size < ofs ||
2351- fseek (logfp , - ofs , SEEK_END ) ||
2352- strbuf_getwholeline (& sb , logfp , '\n' )) {
2353- fclose (logfp );
2354- strbuf_release (& sb );
2355- return -1 ;
2385+ /* Jump to the end */
2386+ if (fseek (logfp , 0 , SEEK_END ) < 0 )
2387+ return error ("cannot seek back reflog for %s: %s" ,
2388+ refname , strerror (errno ));
2389+ pos = ftell (logfp );
2390+ while (!ret && 0 < pos ) {
2391+ int cnt ;
2392+ size_t nread ;
2393+ char buf [BUFSIZ ];
2394+ char * endp , * scanp ;
2395+
2396+ /* Fill next block from the end */
2397+ cnt = (sizeof (buf ) < pos ) ? sizeof (buf ) : pos ;
2398+ if (fseek (logfp , pos - cnt , SEEK_SET ))
2399+ return error ("cannot seek back reflog for %s: %s" ,
2400+ refname , strerror (errno ));
2401+ nread = fread (buf , cnt , 1 , logfp );
2402+ if (nread != 1 )
2403+ return error ("cannot read %d bytes from reflog for %s: %s" ,
2404+ cnt , refname , strerror (errno ));
2405+ pos -= cnt ;
2406+
2407+ scanp = endp = buf + cnt ;
2408+ if (at_tail && scanp [-1 ] == '\n' )
2409+ /* Looking at the final LF at the end of the file */
2410+ scanp -- ;
2411+ at_tail = 0 ;
2412+
2413+ while (buf < scanp ) {
2414+ /*
2415+ * terminating LF of the previous line, or the beginning
2416+ * of the buffer.
2417+ */
2418+ char * bp ;
2419+
2420+ bp = find_beginning_of_line (buf , scanp );
2421+
2422+ if (* bp != '\n' ) {
2423+ strbuf_splice (& sb , 0 , 0 , buf , endp - buf );
2424+ if (pos )
2425+ break ; /* need to fill another block */
2426+ scanp = buf - 1 ; /* leave loop */
2427+ } else {
2428+ /*
2429+ * (bp + 1) thru endp is the beginning of the
2430+ * current line we have in sb
2431+ */
2432+ strbuf_splice (& sb , 0 , 0 , bp + 1 , endp - (bp + 1 ));
2433+ scanp = bp ;
2434+ endp = bp + 1 ;
2435+ }
2436+ ret = show_one_reflog_ent (& sb , fn , cb_data );
2437+ strbuf_reset (& sb );
2438+ if (ret )
2439+ break ;
23562440 }
2357- }
23582441
2359- while (!strbuf_getwholeline (& sb , logfp , '\n' )) {
2360- unsigned char osha1 [20 ], nsha1 [20 ];
2361- char * email_end , * message ;
2362- unsigned long timestamp ;
2363- int tz ;
2364-
2365- /* old SP new SP name <email> SP time TAB msg LF */
2366- if (sb .len < 83 || sb .buf [sb .len - 1 ] != '\n' ||
2367- get_sha1_hex (sb .buf , osha1 ) || sb .buf [40 ] != ' ' ||
2368- get_sha1_hex (sb .buf + 41 , nsha1 ) || sb .buf [81 ] != ' ' ||
2369- !(email_end = strchr (sb .buf + 82 , '>' )) ||
2370- email_end [1 ] != ' ' ||
2371- !(timestamp = strtoul (email_end + 2 , & message , 10 )) ||
2372- !message || message [0 ] != ' ' ||
2373- (message [1 ] != '+' && message [1 ] != '-' ) ||
2374- !isdigit (message [2 ]) || !isdigit (message [3 ]) ||
2375- !isdigit (message [4 ]) || !isdigit (message [5 ]))
2376- continue ; /* corrupt? */
2377- email_end [1 ] = '\0' ;
2378- tz = strtol (message + 1 , NULL , 10 );
2379- if (message [6 ] != '\t' )
2380- message += 6 ;
2381- else
2382- message += 7 ;
2383- ret = fn (osha1 , nsha1 , sb .buf + 82 , timestamp , tz , message ,
2384- cb_data );
2385- if (ret )
2386- break ;
23872442 }
2443+ if (!ret && sb .len )
2444+ ret = show_one_reflog_ent (& sb , fn , cb_data );
2445+
23882446 fclose (logfp );
23892447 strbuf_release (& sb );
23902448 return ret ;
23912449}
23922450
23932451int for_each_reflog_ent (const char * refname , each_reflog_ent_fn fn , void * cb_data )
23942452{
2395- return for_each_recent_reflog_ent (refname , fn , 0 , cb_data );
2396- }
2453+ FILE * logfp ;
2454+ struct strbuf sb = STRBUF_INIT ;
2455+ int ret = 0 ;
2456+
2457+ logfp = fopen (git_path ("logs/%s" , refname ), "r" );
2458+ if (!logfp )
2459+ return -1 ;
23972460
2461+ while (!ret && !strbuf_getwholeline (& sb , logfp , '\n' ))
2462+ ret = show_one_reflog_ent (& sb , fn , cb_data );
2463+ fclose (logfp );
2464+ strbuf_release (& sb );
2465+ return ret ;
2466+ }
23982467/*
23992468 * Call fn for each reflog in the namespace indicated by name. name
24002469 * must be empty or end with '/'. Name will be used as a scratch
0 commit comments