88#include "git-compat-util.h"
99
1010#include "builtin.h"
11+ #include "config.h"
1112#include "environment.h"
1213#include "hex.h"
1314#include "lockfile.h"
2021#include <oidset.h>
2122#include <tree.h>
2223
24+ enum ref_action_mode {
25+ REF_ACTION_UPDATE ,
26+ REF_ACTION_PRINT ,
27+ };
28+
2329static const char * short_commit_name (struct repository * repo ,
2430 struct commit * commit )
2531{
@@ -284,6 +290,54 @@ static struct commit *pick_regular_commit(struct repository *repo,
284290 return create_commit (repo , result -> tree , pickme , replayed_base );
285291}
286292
293+ static enum ref_action_mode parse_ref_action_mode (const char * ref_action , const char * source )
294+ {
295+ if (!ref_action || !strcmp (ref_action , "update" ))
296+ return REF_ACTION_UPDATE ;
297+ if (!strcmp (ref_action , "print" ))
298+ return REF_ACTION_PRINT ;
299+ die (_ ("invalid %s value: '%s'" ), source , ref_action );
300+ }
301+
302+ static enum ref_action_mode get_ref_action_mode (struct repository * repo , const char * ref_action )
303+ {
304+ const char * config_value = NULL ;
305+
306+ /* Command line option takes precedence */
307+ if (ref_action )
308+ return parse_ref_action_mode (ref_action , "--ref-action" );
309+
310+ /* Check config value */
311+ if (!repo_config_get_string_tmp (repo , "replay.refAction" , & config_value ))
312+ return parse_ref_action_mode (config_value , "replay.refAction" );
313+
314+ /* Default to update mode */
315+ return REF_ACTION_UPDATE ;
316+ }
317+
318+ static int handle_ref_update (enum ref_action_mode mode ,
319+ struct ref_transaction * transaction ,
320+ const char * refname ,
321+ const struct object_id * new_oid ,
322+ const struct object_id * old_oid ,
323+ const char * reflog_msg ,
324+ struct strbuf * err )
325+ {
326+ switch (mode ) {
327+ case REF_ACTION_PRINT :
328+ printf ("update %s %s %s\n" ,
329+ refname ,
330+ oid_to_hex (new_oid ),
331+ oid_to_hex (old_oid ));
332+ return 0 ;
333+ case REF_ACTION_UPDATE :
334+ return ref_transaction_update (transaction , refname , new_oid , old_oid ,
335+ NULL , NULL , 0 , reflog_msg , err );
336+ default :
337+ BUG ("unknown ref_action_mode %d" , mode );
338+ }
339+ }
340+
287341int cmd_replay (int argc ,
288342 const char * * argv ,
289343 const char * prefix ,
@@ -294,6 +348,8 @@ int cmd_replay(int argc,
294348 struct commit * onto = NULL ;
295349 const char * onto_name = NULL ;
296350 int contained = 0 ;
351+ const char * ref_action = NULL ;
352+ enum ref_action_mode ref_mode ;
297353
298354 struct rev_info revs ;
299355 struct commit * last_commit = NULL ;
@@ -302,12 +358,15 @@ int cmd_replay(int argc,
302358 struct merge_result result ;
303359 struct strset * update_refs = NULL ;
304360 kh_oid_map_t * replayed_commits ;
361+ struct ref_transaction * transaction = NULL ;
362+ struct strbuf transaction_err = STRBUF_INIT ;
363+ struct strbuf reflog_msg = STRBUF_INIT ;
305364 int ret = 0 ;
306365
307- const char * const replay_usage [] = {
366+ const char * const replay_usage [] = {
308367 N_ ("(EXPERIMENTAL!) git replay "
309368 "([--contained] --onto <newbase> | --advance <branch>) "
310- "<revision-range>..." ),
369+ "[--ref-action[=<mode>]] <revision-range>..." ),
311370 NULL
312371 };
313372 struct option replay_options [] = {
@@ -319,6 +378,9 @@ int cmd_replay(int argc,
319378 N_ ("replay onto given commit" )),
320379 OPT_BOOL (0 , "contained" , & contained ,
321380 N_ ("advance all branches contained in revision-range" )),
381+ OPT_STRING (0 , "ref-action" , & ref_action ,
382+ N_ ("mode" ),
383+ N_ ("control ref update behavior (update|print)" )),
322384 OPT_END ()
323385 };
324386
@@ -330,9 +392,12 @@ int cmd_replay(int argc,
330392 usage_with_options (replay_usage , replay_options );
331393 }
332394
333- if (advance_name_opt && contained )
334- die (_ ("options '%s' and '%s' cannot be used together" ),
335- "--advance" , "--contained" );
395+ die_for_incompatible_opt2 (!!advance_name_opt , "--advance" ,
396+ contained , "--contained" );
397+
398+ /* Parse ref action mode from command line or config */
399+ ref_mode = get_ref_action_mode (repo , ref_action );
400+
336401 advance_name = xstrdup_or_null (advance_name_opt );
337402
338403 repo_init_revisions (repo , & revs , prefix );
@@ -389,6 +454,24 @@ int cmd_replay(int argc,
389454 determine_replay_mode (repo , & revs .cmdline , onto_name , & advance_name ,
390455 & onto , & update_refs );
391456
457+ /* Build reflog message */
458+ if (advance_name_opt )
459+ strbuf_addf (& reflog_msg , "replay --advance %s" , advance_name_opt );
460+ else
461+ strbuf_addf (& reflog_msg , "replay --onto %s" ,
462+ oid_to_hex (& onto -> object .oid ));
463+
464+ /* Initialize ref transaction if using update mode */
465+ if (ref_mode == REF_ACTION_UPDATE ) {
466+ transaction = ref_store_transaction_begin (get_main_ref_store (repo ),
467+ 0 , & transaction_err );
468+ if (!transaction ) {
469+ ret = error (_ ("failed to begin ref transaction: %s" ),
470+ transaction_err .buf );
471+ goto cleanup ;
472+ }
473+ }
474+
392475 if (!onto ) /* FIXME: Should handle replaying down to root commit */
393476 die ("Replaying down to root commit is not supported yet!" );
394477
@@ -434,21 +517,41 @@ int cmd_replay(int argc,
434517 if (decoration -> type == DECORATION_REF_LOCAL &&
435518 (contained || strset_contains (update_refs ,
436519 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 ));
520+ if (handle_ref_update (ref_mode , transaction ,
521+ decoration -> name ,
522+ & last_commit -> object .oid ,
523+ & commit -> object .oid ,
524+ reflog_msg .buf ,
525+ & transaction_err ) < 0 ) {
526+ ret = error (_ ("failed to update ref '%s': %s" ),
527+ decoration -> name , transaction_err .buf );
528+ goto cleanup ;
529+ }
441530 }
442531 decoration = decoration -> next ;
443532 }
444533 }
445534
446535 /* In --advance mode, advance the target ref */
447536 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 ));
537+ if (handle_ref_update (ref_mode , transaction , advance_name ,
538+ & last_commit -> object .oid ,
539+ & onto -> object .oid ,
540+ reflog_msg .buf ,
541+ & transaction_err ) < 0 ) {
542+ ret = error (_ ("failed to update ref '%s': %s" ),
543+ advance_name , transaction_err .buf );
544+ goto cleanup ;
545+ }
546+ }
547+
548+ /* Commit the ref transaction if we have one */
549+ if (transaction && result .clean == 1 ) {
550+ if (ref_transaction_commit (transaction , & transaction_err )) {
551+ ret = error (_ ("failed to commit ref transaction: %s" ),
552+ transaction_err .buf );
553+ goto cleanup ;
554+ }
452555 }
453556
454557 merge_finalize (& merge_opt , & result );
@@ -460,6 +563,10 @@ int cmd_replay(int argc,
460563 ret = result .clean ;
461564
462565cleanup :
566+ if (transaction )
567+ ref_transaction_free (transaction );
568+ strbuf_release (& transaction_err );
569+ strbuf_release (& reflog_msg );
463570 release_revisions (& revs );
464571 free (advance_name );
465572
0 commit comments