@@ -284,6 +284,28 @@ static struct commit *pick_regular_commit(struct repository *repo,
284
284
return create_commit (repo , result -> tree , pickme , replayed_base );
285
285
}
286
286
287
+ static int add_ref_to_transaction (struct ref_transaction * transaction ,
288
+ const char * refname ,
289
+ const struct object_id * new_oid ,
290
+ const struct object_id * old_oid ,
291
+ struct strbuf * err )
292
+ {
293
+ return ref_transaction_update (transaction , refname , new_oid , old_oid ,
294
+ NULL , NULL , 0 , "git replay" , err );
295
+ }
296
+
297
+ static void print_rejected_update (const char * refname ,
298
+ const struct object_id * old_oid UNUSED ,
299
+ const struct object_id * new_oid UNUSED ,
300
+ const char * old_target UNUSED ,
301
+ const char * new_target UNUSED ,
302
+ enum ref_transaction_error err ,
303
+ void * cb_data UNUSED )
304
+ {
305
+ const char * reason = ref_transaction_error_msg (err );
306
+ warning (_ ("failed to update %s: %s" ), refname , reason );
307
+ }
308
+
287
309
int cmd_replay (int argc ,
288
310
const char * * argv ,
289
311
const char * prefix ,
@@ -294,6 +316,8 @@ int cmd_replay(int argc,
294
316
struct commit * onto = NULL ;
295
317
const char * onto_name = NULL ;
296
318
int contained = 0 ;
319
+ int output_commands = 0 ;
320
+ int allow_partial = 0 ;
297
321
298
322
struct rev_info revs ;
299
323
struct commit * last_commit = NULL ;
@@ -302,12 +326,15 @@ int cmd_replay(int argc,
302
326
struct merge_result result ;
303
327
struct strset * update_refs = NULL ;
304
328
kh_oid_map_t * replayed_commits ;
329
+ struct ref_transaction * transaction = NULL ;
330
+ struct strbuf transaction_err = STRBUF_INIT ;
331
+ int commits_processed = 0 ;
305
332
int ret = 0 ;
306
333
307
- const char * const replay_usage [] = {
334
+ const char * const replay_usage [] = {
308
335
N_ ("(EXPERIMENTAL!) git replay "
309
336
"([--contained] --onto <newbase> | --advance <branch>) "
310
- "<revision-range>..." ),
337
+ "[--output-commands | --allow-partial] <revision-range>..." ),
311
338
NULL
312
339
};
313
340
struct option replay_options [] = {
@@ -319,6 +346,10 @@ int cmd_replay(int argc,
319
346
N_ ("replay onto given commit" )),
320
347
OPT_BOOL (0 , "contained" , & contained ,
321
348
N_ ("advance all branches contained in revision-range" )),
349
+ OPT_BOOL (0 , "output-commands" , & output_commands ,
350
+ N_ ("output update commands instead of updating refs" )),
351
+ OPT_BOOL (0 , "allow-partial" , & allow_partial ,
352
+ N_ ("allow some ref updates to succeed even if others fail" )),
322
353
OPT_END ()
323
354
};
324
355
@@ -330,9 +361,12 @@ int cmd_replay(int argc,
330
361
usage_with_options (replay_usage , replay_options );
331
362
}
332
363
333
- if (advance_name_opt && contained )
334
- die (_ ("options '%s' and '%s' cannot be used together" ),
335
- "--advance" , "--contained" );
364
+ die_for_incompatible_opt2 (!!advance_name_opt , "--advance" ,
365
+ contained , "--contained" );
366
+
367
+ die_for_incompatible_opt2 (allow_partial , "--allow-partial" ,
368
+ output_commands , "--output-commands" );
369
+
336
370
advance_name = xstrdup_or_null (advance_name_opt );
337
371
338
372
repo_init_revisions (repo , & revs , prefix );
@@ -389,6 +423,17 @@ int cmd_replay(int argc,
389
423
determine_replay_mode (repo , & revs .cmdline , onto_name , & advance_name ,
390
424
& onto , & update_refs );
391
425
426
+ if (!output_commands ) {
427
+ unsigned int transaction_flags = allow_partial ? REF_TRANSACTION_ALLOW_FAILURE : 0 ;
428
+ transaction = ref_store_transaction_begin (get_main_ref_store (repo ),
429
+ transaction_flags ,
430
+ & transaction_err );
431
+ if (!transaction ) {
432
+ ret = error (_ ("failed to begin ref transaction: %s" ), transaction_err .buf );
433
+ goto cleanup ;
434
+ }
435
+ }
436
+
392
437
if (!onto ) /* FIXME: Should handle replaying down to root commit */
393
438
die ("Replaying down to root commit is not supported yet!" );
394
439
@@ -407,6 +452,8 @@ int cmd_replay(int argc,
407
452
khint_t pos ;
408
453
int hr ;
409
454
455
+ commits_processed = 1 ;
456
+
410
457
if (!commit -> parents )
411
458
die (_ ("replaying down to root commit is not supported yet!" ));
412
459
if (commit -> parents -> next )
@@ -434,21 +481,52 @@ int cmd_replay(int argc,
434
481
if (decoration -> type == DECORATION_REF_LOCAL &&
435
482
(contained || strset_contains (update_refs ,
436
483
decoration -> name ))) {
437
- printf ("update %s %s %s\n" ,
438
- decoration -> name ,
439
- oid_to_hex (& last_commit -> object .oid ),
440
- oid_to_hex (& commit -> object .oid ));
484
+ if (output_commands ) {
485
+ printf ("update %s %s %s\n" ,
486
+ decoration -> name ,
487
+ oid_to_hex (& last_commit -> object .oid ),
488
+ oid_to_hex (& commit -> object .oid ));
489
+ } else if (add_ref_to_transaction (transaction , decoration -> name ,
490
+ & last_commit -> object .oid ,
491
+ & commit -> object .oid ,
492
+ & transaction_err ) < 0 ) {
493
+ ret = error (_ ("failed to add ref update to transaction: %s" ), transaction_err .buf );
494
+ goto cleanup ;
495
+ }
441
496
}
442
497
decoration = decoration -> next ;
443
498
}
444
499
}
445
500
446
501
/* In --advance mode, advance the target ref */
447
502
if (result .clean == 1 && advance_name ) {
448
- printf ("update %s %s %s\n" ,
449
- advance_name ,
450
- oid_to_hex (& last_commit -> object .oid ),
451
- oid_to_hex (& onto -> object .oid ));
503
+ if (output_commands ) {
504
+ printf ("update %s %s %s\n" ,
505
+ advance_name ,
506
+ oid_to_hex (& last_commit -> object .oid ),
507
+ oid_to_hex (& onto -> object .oid ));
508
+ } else if (add_ref_to_transaction (transaction , advance_name ,
509
+ & last_commit -> object .oid ,
510
+ & onto -> object .oid ,
511
+ & transaction_err ) < 0 ) {
512
+ ret = error (_ ("failed to add ref update to transaction: %s" ), transaction_err .buf );
513
+ goto cleanup ;
514
+ }
515
+ }
516
+
517
+ /* Commit the ref transaction if we have one */
518
+ if (transaction && result .clean == 1 ) {
519
+ if (ref_transaction_commit (transaction , & transaction_err )) {
520
+ if (allow_partial ) {
521
+ warning (_ ("some ref updates failed: %s" ), transaction_err .buf );
522
+ ref_transaction_for_each_rejected_update (transaction ,
523
+ print_rejected_update , NULL );
524
+ ret = 0 ; /* Set failure even with allow_partial */
525
+ } else {
526
+ ret = error (_ ("failed to update refs: %s" ), transaction_err .buf );
527
+ goto cleanup ;
528
+ }
529
+ }
452
530
}
453
531
454
532
merge_finalize (& merge_opt , & result );
@@ -457,9 +535,17 @@ int cmd_replay(int argc,
457
535
strset_clear (update_refs );
458
536
free (update_refs );
459
537
}
460
- ret = result .clean ;
538
+
539
+ /* Handle empty ranges: if no commits were processed, treat as success */
540
+ if (!commits_processed )
541
+ ret = 1 ; /* Success - no commits to replay is not an error */
542
+ else
543
+ ret = result .clean ;
461
544
462
545
cleanup :
546
+ if (transaction )
547
+ ref_transaction_free (transaction );
548
+ strbuf_release (& transaction_err );
463
549
release_revisions (& revs );
464
550
free (advance_name );
465
551
0 commit comments