@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
4040};
4141
4242enum replay_action { REVERT , CHERRY_PICK };
43- enum replay_subcommand { REPLAY_NONE , REPLAY_RESET , REPLAY_CONTINUE };
43+ enum replay_subcommand {
44+ REPLAY_NONE ,
45+ REPLAY_REMOVE_STATE ,
46+ REPLAY_CONTINUE ,
47+ REPLAY_ROLLBACK
48+ };
4449
4550struct replay_opts {
4651 enum replay_action action ;
@@ -133,11 +138,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
133138{
134139 const char * const * usage_str = revert_or_cherry_pick_usage (opts );
135140 const char * me = action_name (opts );
136- int reset = 0 ;
141+ int remove_state = 0 ;
137142 int contin = 0 ;
143+ int rollback = 0 ;
138144 struct option options [] = {
139- OPT_BOOLEAN (0 , "reset" , & reset , "forget the current operation" ),
140- OPT_BOOLEAN (0 , "continue" , & contin , "continue the current operation" ),
145+ OPT_BOOLEAN (0 , "quit" , & remove_state , "end revert or cherry-pick sequence" ),
146+ OPT_BOOLEAN (0 , "continue" , & contin , "resume revert or cherry-pick sequence" ),
147+ OPT_BOOLEAN (0 , "abort" , & rollback , "cancel revert or cherry-pick sequence" ),
141148 OPT_BOOLEAN ('n' , "no-commit" , & opts -> no_commit , "don't automatically commit" ),
142149 OPT_BOOLEAN ('e' , "edit" , & opts -> edit , "edit the commit message" ),
143150 OPT_NOOP_NOARG ('r' , NULL ),
@@ -168,25 +175,32 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
168175
169176 /* Check for incompatible subcommands */
170177 verify_opt_mutually_compatible (me ,
171- "--reset " , reset ,
178+ "--quit " , remove_state ,
172179 "--continue" , contin ,
180+ "--abort" , rollback ,
173181 NULL );
174182
175183 /* Set the subcommand */
176- if (reset )
177- opts -> subcommand = REPLAY_RESET ;
184+ if (remove_state )
185+ opts -> subcommand = REPLAY_REMOVE_STATE ;
178186 else if (contin )
179187 opts -> subcommand = REPLAY_CONTINUE ;
188+ else if (rollback )
189+ opts -> subcommand = REPLAY_ROLLBACK ;
180190 else
181191 opts -> subcommand = REPLAY_NONE ;
182192
183193 /* Check for incompatible command line arguments */
184194 if (opts -> subcommand != REPLAY_NONE ) {
185195 char * this_operation ;
186- if (opts -> subcommand == REPLAY_RESET )
187- this_operation = "--reset " ;
188- else
196+ if (opts -> subcommand == REPLAY_REMOVE_STATE )
197+ this_operation = "--quit " ;
198+ else if ( opts -> subcommand == REPLAY_CONTINUE )
189199 this_operation = "--continue" ;
200+ else {
201+ assert (opts -> subcommand == REPLAY_ROLLBACK );
202+ this_operation = "--abort" ;
203+ }
190204
191205 verify_opt_compatible (me , this_operation ,
192206 "--no-commit" , opts -> no_commit ,
@@ -286,15 +300,15 @@ static char *get_encoding(const char *message)
286300 return NULL ;
287301}
288302
289- static void write_cherry_pick_head (struct commit * commit )
303+ static void write_cherry_pick_head (struct commit * commit , const char * pseudoref )
290304{
291305 const char * filename ;
292306 int fd ;
293307 struct strbuf buf = STRBUF_INIT ;
294308
295309 strbuf_addf (& buf , "%s\n" , sha1_to_hex (commit -> object .sha1 ));
296310
297- filename = git_path ("CHERRY_PICK_HEAD" );
311+ filename = git_path (pseudoref );
298312 fd = open (filename , O_WRONLY | O_CREAT , 0666 );
299313 if (fd < 0 )
300314 die_errno (_ ("Could not open '%s' for writing" ), filename );
@@ -594,7 +608,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
594608 * write it at all.
595609 */
596610 if (opts -> action == CHERRY_PICK && !opts -> no_commit && (res == 0 || res == 1 ))
597- write_cherry_pick_head (commit );
611+ write_cherry_pick_head (commit , "CHERRY_PICK_HEAD" );
612+ if (opts -> action == REVERT && ((opts -> no_commit && res == 0 ) || res == 1 ))
613+ write_cherry_pick_head (commit , "REVERT_HEAD" );
598614
599615 if (res ) {
600616 error (opts -> action == REVERT
@@ -843,8 +859,11 @@ static int create_seq_dir(void)
843859{
844860 const char * seq_dir = git_path (SEQ_DIR );
845861
846- if (file_exists (seq_dir ))
847- return error (_ ("%s already exists." ), seq_dir );
862+ if (file_exists (seq_dir )) {
863+ error (_ ("a cherry-pick or revert is already in progress" ));
864+ advise (_ ("try \"git cherry-pick (--continue | --quit | --abort)\"" ));
865+ return -1 ;
866+ }
848867 else if (mkdir (seq_dir , 0777 ) < 0 )
849868 die_errno (_ ("Could not create sequencer directory %s" ), seq_dir );
850869 return 0 ;
@@ -865,6 +884,71 @@ static void save_head(const char *head)
865884 die (_ ("Error wrapping up %s." ), head_file );
866885}
867886
887+ static int reset_for_rollback (const unsigned char * sha1 )
888+ {
889+ const char * argv [4 ]; /* reset --merge <arg> + NULL */
890+ argv [0 ] = "reset" ;
891+ argv [1 ] = "--merge" ;
892+ argv [2 ] = sha1_to_hex (sha1 );
893+ argv [3 ] = NULL ;
894+ return run_command_v_opt (argv , RUN_GIT_CMD );
895+ }
896+
897+ static int rollback_single_pick (void )
898+ {
899+ unsigned char head_sha1 [20 ];
900+
901+ if (!file_exists (git_path ("CHERRY_PICK_HEAD" )) &&
902+ !file_exists (git_path ("REVERT_HEAD" )))
903+ return error (_ ("no cherry-pick or revert in progress" ));
904+ if (!resolve_ref ("HEAD" , head_sha1 , 0 , NULL ))
905+ return error (_ ("cannot resolve HEAD" ));
906+ if (is_null_sha1 (head_sha1 ))
907+ return error (_ ("cannot abort from a branch yet to be born" ));
908+ return reset_for_rollback (head_sha1 );
909+ }
910+
911+ static int sequencer_rollback (struct replay_opts * opts )
912+ {
913+ const char * filename ;
914+ FILE * f ;
915+ unsigned char sha1 [20 ];
916+ struct strbuf buf = STRBUF_INIT ;
917+
918+ filename = git_path (SEQ_HEAD_FILE );
919+ f = fopen (filename , "r" );
920+ if (!f && errno == ENOENT ) {
921+ /*
922+ * There is no multiple-cherry-pick in progress.
923+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
924+ * a single-cherry-pick in progress, abort that.
925+ */
926+ return rollback_single_pick ();
927+ }
928+ if (!f )
929+ return error (_ ("cannot open %s: %s" ), filename ,
930+ strerror (errno ));
931+ if (strbuf_getline (& buf , f , '\n' )) {
932+ error (_ ("cannot read %s: %s" ), filename , ferror (f ) ?
933+ strerror (errno ) : _ ("unexpected end of file" ));
934+ goto fail ;
935+ }
936+ if (get_sha1_hex (buf .buf , sha1 ) || buf .buf [40 ] != '\0' ) {
937+ error (_ ("stored pre-cherry-pick HEAD file '%s' is corrupt" ),
938+ filename );
939+ goto fail ;
940+ }
941+ if (reset_for_rollback (sha1 ))
942+ goto fail ;
943+ strbuf_release (& buf );
944+ fclose (f );
945+ return 0 ;
946+ fail :
947+ strbuf_release (& buf );
948+ fclose (f );
949+ return -1 ;
950+ }
951+
868952static void save_todo (struct commit_list * todo_list , struct replay_opts * opts )
869953{
870954 const char * todo_file = git_path (SEQ_TODO_FILE );
@@ -965,43 +1049,41 @@ static int pick_revisions(struct replay_opts *opts)
9651049 * cherry-pick should be handled differently from an existing
9661050 * one that is being continued
9671051 */
968- if (opts -> subcommand == REPLAY_RESET ) {
1052+ if (opts -> subcommand == REPLAY_REMOVE_STATE ) {
9691053 remove_sequencer_state (1 );
9701054 return 0 ;
971- } else if (opts -> subcommand == REPLAY_CONTINUE ) {
1055+ }
1056+ if (opts -> subcommand == REPLAY_ROLLBACK )
1057+ return sequencer_rollback (opts );
1058+ if (opts -> subcommand == REPLAY_CONTINUE ) {
9721059 if (!file_exists (git_path (SEQ_TODO_FILE )))
973- goto error ;
1060+ return error ( _ ( "No %s in progress" ), action_name ( opts )) ;
9741061 read_populate_opts (& opts );
9751062 read_populate_todo (& todo_list , opts );
9761063
9771064 /* Verify that the conflict has been resolved */
9781065 if (!index_differs_from ("HEAD" , 0 ))
9791066 todo_list = todo_list -> next ;
980- } else {
981- /*
982- * Start a new cherry-pick/ revert sequence; but
983- * first, make sure that an existing one isn't in
984- * progress
985- */
1067+ return pick_commits (todo_list , opts );
1068+ }
9861069
987- walk_revs_populate_todo (& todo_list , opts );
988- if (create_seq_dir () < 0 ) {
989- error (_ ("A cherry-pick or revert is in progress." ));
990- advise (_ ("Use --continue to continue the operation" ));
991- advise (_ ("or --reset to forget about it" ));
992- return -1 ;
993- }
994- if (get_sha1 ("HEAD" , sha1 )) {
995- if (opts -> action == REVERT )
996- return error (_ ("Can't revert as initial commit" ));
997- return error (_ ("Can't cherry-pick into empty head" ));
998- }
999- save_head (sha1_to_hex (sha1 ));
1000- save_opts (opts );
1070+ /*
1071+ * Start a new cherry-pick/ revert sequence; but
1072+ * first, make sure that an existing one isn't in
1073+ * progress
1074+ */
1075+
1076+ walk_revs_populate_todo (& todo_list , opts );
1077+ if (create_seq_dir () < 0 )
1078+ return -1 ;
1079+ if (get_sha1 ("HEAD" , sha1 )) {
1080+ if (opts -> action == REVERT )
1081+ return error (_ ("Can't revert as initial commit" ));
1082+ return error (_ ("Can't cherry-pick into empty head" ));
10011083 }
1084+ save_head (sha1_to_hex (sha1 ));
1085+ save_opts (opts );
10021086 return pick_commits (todo_list , opts );
1003- error :
1004- return error (_ ("No %s in progress" ), action_name (opts ));
10051087}
10061088
10071089int cmd_revert (int argc , const char * * argv , const char * prefix )
0 commit comments