@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
40
40
};
41
41
42
42
enum 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
+ };
44
49
45
50
struct replay_opts {
46
51
enum replay_action action ;
@@ -133,11 +138,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
133
138
{
134
139
const char * const * usage_str = revert_or_cherry_pick_usage (opts );
135
140
const char * me = action_name (opts );
136
- int reset = 0 ;
141
+ int remove_state = 0 ;
137
142
int contin = 0 ;
143
+ int rollback = 0 ;
138
144
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" ),
141
148
OPT_BOOLEAN ('n' , "no-commit" , & opts -> no_commit , "don't automatically commit" ),
142
149
OPT_BOOLEAN ('e' , "edit" , & opts -> edit , "edit the commit message" ),
143
150
OPT_NOOP_NOARG ('r' , NULL ),
@@ -168,25 +175,32 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
168
175
169
176
/* Check for incompatible subcommands */
170
177
verify_opt_mutually_compatible (me ,
171
- "--reset " , reset ,
178
+ "--quit " , remove_state ,
172
179
"--continue" , contin ,
180
+ "--abort" , rollback ,
173
181
NULL );
174
182
175
183
/* Set the subcommand */
176
- if (reset )
177
- opts -> subcommand = REPLAY_RESET ;
184
+ if (remove_state )
185
+ opts -> subcommand = REPLAY_REMOVE_STATE ;
178
186
else if (contin )
179
187
opts -> subcommand = REPLAY_CONTINUE ;
188
+ else if (rollback )
189
+ opts -> subcommand = REPLAY_ROLLBACK ;
180
190
else
181
191
opts -> subcommand = REPLAY_NONE ;
182
192
183
193
/* Check for incompatible command line arguments */
184
194
if (opts -> subcommand != REPLAY_NONE ) {
185
195
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 )
189
199
this_operation = "--continue" ;
200
+ else {
201
+ assert (opts -> subcommand == REPLAY_ROLLBACK );
202
+ this_operation = "--abort" ;
203
+ }
190
204
191
205
verify_opt_compatible (me , this_operation ,
192
206
"--no-commit" , opts -> no_commit ,
@@ -286,15 +300,15 @@ static char *get_encoding(const char *message)
286
300
return NULL ;
287
301
}
288
302
289
- static void write_cherry_pick_head (struct commit * commit )
303
+ static void write_cherry_pick_head (struct commit * commit , const char * pseudoref )
290
304
{
291
305
const char * filename ;
292
306
int fd ;
293
307
struct strbuf buf = STRBUF_INIT ;
294
308
295
309
strbuf_addf (& buf , "%s\n" , sha1_to_hex (commit -> object .sha1 ));
296
310
297
- filename = git_path ("CHERRY_PICK_HEAD" );
311
+ filename = git_path (pseudoref );
298
312
fd = open (filename , O_WRONLY | O_CREAT , 0666 );
299
313
if (fd < 0 )
300
314
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)
594
608
* write it at all.
595
609
*/
596
610
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" );
598
614
599
615
if (res ) {
600
616
error (opts -> action == REVERT
@@ -843,8 +859,11 @@ static int create_seq_dir(void)
843
859
{
844
860
const char * seq_dir = git_path (SEQ_DIR );
845
861
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
+ }
848
867
else if (mkdir (seq_dir , 0777 ) < 0 )
849
868
die_errno (_ ("Could not create sequencer directory %s" ), seq_dir );
850
869
return 0 ;
@@ -865,6 +884,71 @@ static void save_head(const char *head)
865
884
die (_ ("Error wrapping up %s." ), head_file );
866
885
}
867
886
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
+
868
952
static void save_todo (struct commit_list * todo_list , struct replay_opts * opts )
869
953
{
870
954
const char * todo_file = git_path (SEQ_TODO_FILE );
@@ -965,43 +1049,41 @@ static int pick_revisions(struct replay_opts *opts)
965
1049
* cherry-pick should be handled differently from an existing
966
1050
* one that is being continued
967
1051
*/
968
- if (opts -> subcommand == REPLAY_RESET ) {
1052
+ if (opts -> subcommand == REPLAY_REMOVE_STATE ) {
969
1053
remove_sequencer_state (1 );
970
1054
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 ) {
972
1059
if (!file_exists (git_path (SEQ_TODO_FILE )))
973
- goto error ;
1060
+ return error ( _ ( "No %s in progress" ), action_name ( opts )) ;
974
1061
read_populate_opts (& opts );
975
1062
read_populate_todo (& todo_list , opts );
976
1063
977
1064
/* Verify that the conflict has been resolved */
978
1065
if (!index_differs_from ("HEAD" , 0 ))
979
1066
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
+ }
986
1069
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" ));
1001
1083
}
1084
+ save_head (sha1_to_hex (sha1 ));
1085
+ save_opts (opts );
1002
1086
return pick_commits (todo_list , opts );
1003
- error :
1004
- return error (_ ("No %s in progress" ), action_name (opts ));
1005
1087
}
1006
1088
1007
1089
int cmd_revert (int argc , const char * * argv , const char * prefix )
0 commit comments