@@ -30,9 +30,12 @@ extern int stdout_format_has_i;
3030extern int logfile_format_has_i ;
3131extern int am_root ;
3232extern int am_server ;
33+ extern int am_sender ;
3334extern int am_daemon ;
3435extern int inc_recurse ;
36+ extern int no_i_r_skip_unchanged ;
3537extern int relative_paths ;
38+ extern struct stats stats ;
3639extern int implied_dirs ;
3740extern int keep_dirlinks ;
3841extern int write_devices ;
@@ -1242,6 +1245,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
12421245 return ;
12431246 }
12441247
1248+ if (!F_IS_ACTIVE (file )) {
1249+ /* File was marked as inactive (unchanged) during pre-scan */
1250+ if (DEBUG_GTE (GENR , 2 ))
1251+ rprintf (FINFO , "skipping inactive file: %s\n" , fname );
1252+ return ;
1253+ }
1254+
12451255 maybe_ATTRS_ACCURATE_TIME = always_checksum ? ATTRS_ACCURATE_TIME : 0 ;
12461256
12471257 if (skip_dir ) {
@@ -2223,6 +2233,80 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
22232233 }
22242234}
22252235
2236+ /* Pre-scan the file list to mark unchanged files and adjust stats.total_size.
2237+ * This allows accurate progress reporting on resumed transfers. */
2238+ static void prescan_for_unchanged (const char * local_name , int f_out )
2239+ {
2240+ int i , active_count = 0 , skipped_count = 0 ;
2241+ char fbuf [MAXPATHLEN ];
2242+ STRUCT_STAT st ;
2243+
2244+ if (!no_i_r_skip_unchanged || !cur_flist )
2245+ return ;
2246+
2247+ if (DEBUG_GTE (GENR , 1 ))
2248+ rprintf (FINFO , "pre-scanning for unchanged files\n" );
2249+
2250+ for (i = 0 ; i < cur_flist -> used ; i ++ ) {
2251+ struct file_struct * file = cur_flist -> files [i ];
2252+ enum filetype ftype ;
2253+
2254+ if (!file || !F_IS_ACTIVE (file ))
2255+ continue ;
2256+
2257+ ftype = get_file_type (file -> mode );
2258+
2259+ /* Only check regular files */
2260+ if (ftype != FT_REG ) {
2261+ active_count ++ ;
2262+ continue ;
2263+ }
2264+
2265+ /* Construct destination path */
2266+ if (local_name )
2267+ strlcpy (fbuf , local_name , sizeof fbuf );
2268+ else
2269+ f_name (file , fbuf );
2270+
2271+ /* Stat destination file */
2272+ if (do_stat (fbuf , & st ) < 0 ) {
2273+ active_count ++ ;
2274+ continue ;
2275+ }
2276+
2277+ /* Check if file is unchanged */
2278+ if (quick_check_ok (ftype , fbuf , file , & st )) {
2279+ if (DEBUG_GTE (GENR , 2 ))
2280+ rprintf (FINFO , "skipping unchanged: %s\n" , fbuf );
2281+
2282+ /* Subtract from total size for accurate progress */
2283+ stats .total_size -= F_LENGTH (file );
2284+
2285+ /* Mark as inactive to remove from file list */
2286+ clear_file (file );
2287+ skipped_count ++ ;
2288+ } else {
2289+ active_count ++ ;
2290+ }
2291+ }
2292+
2293+ /* Update stats to reflect skipped files */
2294+ stats .num_files = active_count ;
2295+ stats .num_skipped_files = skipped_count ;
2296+
2297+ if (DEBUG_GTE (GENR , 1 ))
2298+ rprintf (FINFO , "skipped %d unchanged files, %d active, adjusted size: %.2f GB\n" ,
2299+ skipped_count , active_count , (double )stats .total_size / 1024 / 1024 / 1024 );
2300+
2301+ /* Send skipped count and adjusted total_size to sender for accurate progress display */
2302+ if (f_out >= 0 ) {
2303+ char buf [12 ];
2304+ SIVAL (buf , 0 , skipped_count );
2305+ SIVAL64 (buf , 4 , stats .total_size );
2306+ send_msg (MSG_FLIST_COUNT , buf , 12 , -1 );
2307+ }
2308+ }
2309+
22262310void generate_files (int f_out , const char * local_name )
22272311{
22282312 int i , ndx , next_loopchk = 0 ;
@@ -2279,6 +2363,9 @@ void generate_files(int f_out, const char *local_name)
22792363
22802364 dflt_perms = (ACCESSPERMS & ~orig_umask );
22812365
2366+ /* Pre-scan to mark unchanged files for accurate progress reporting */
2367+ prescan_for_unchanged (local_name , f_out );
2368+
22822369 do {
22832370#ifdef SUPPORT_HARD_LINKS
22842371 if (preserve_hard_links && inc_recurse ) {
0 commit comments