22
22
#include "wt-status.h"
23
23
#include "revision.h"
24
24
#include "commit-reach.h"
25
+ #include "rerere.h"
25
26
26
27
static char const * const builtin_rebase_usage [] = {
27
28
N_ ("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -92,6 +93,7 @@ struct rebase_options {
92
93
REBASE_INTERACTIVE_EXPLICIT = 1 <<4 ,
93
94
} flags ;
94
95
struct strbuf git_am_opt ;
96
+ const char * action ;
95
97
};
96
98
97
99
static int is_interactive (struct rebase_options * opts )
@@ -116,6 +118,62 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
116
118
return path .buf ;
117
119
}
118
120
121
+ /* Read one file, then strip line endings */
122
+ static int read_one (const char * path , struct strbuf * buf )
123
+ {
124
+ if (strbuf_read_file (buf , path , 0 ) < 0 )
125
+ return error_errno (_ ("could not read '%s'" ), path );
126
+ strbuf_trim_trailing_newline (buf );
127
+ return 0 ;
128
+ }
129
+
130
+ /* Initialize the rebase options from the state directory. */
131
+ static int read_basic_state (struct rebase_options * opts )
132
+ {
133
+ struct strbuf head_name = STRBUF_INIT ;
134
+ struct strbuf buf = STRBUF_INIT ;
135
+ struct object_id oid ;
136
+
137
+ if (read_one (state_dir_path ("head-name" , opts ), & head_name ) ||
138
+ read_one (state_dir_path ("onto" , opts ), & buf ))
139
+ return -1 ;
140
+ opts -> head_name = starts_with (head_name .buf , "refs/" ) ?
141
+ xstrdup (head_name .buf ) : NULL ;
142
+ strbuf_release (& head_name );
143
+ if (get_oid (buf .buf , & oid ))
144
+ return error (_ ("could not get 'onto': '%s'" ), buf .buf );
145
+ opts -> onto = lookup_commit_or_die (& oid , buf .buf );
146
+
147
+ /*
148
+ * We always write to orig-head, but interactive rebase used to write to
149
+ * head. Fall back to reading from head to cover for the case that the
150
+ * user upgraded git with an ongoing interactive rebase.
151
+ */
152
+ strbuf_reset (& buf );
153
+ if (file_exists (state_dir_path ("orig-head" , opts ))) {
154
+ if (read_one (state_dir_path ("orig-head" , opts ), & buf ))
155
+ return -1 ;
156
+ } else if (read_one (state_dir_path ("head" , opts ), & buf ))
157
+ return -1 ;
158
+ if (get_oid (buf .buf , & opts -> orig_head ))
159
+ return error (_ ("invalid orig-head: '%s'" ), buf .buf );
160
+
161
+ strbuf_reset (& buf );
162
+ if (read_one (state_dir_path ("quiet" , opts ), & buf ))
163
+ return -1 ;
164
+ if (buf .len )
165
+ opts -> flags &= ~REBASE_NO_QUIET ;
166
+ else
167
+ opts -> flags |= REBASE_NO_QUIET ;
168
+
169
+ if (file_exists (state_dir_path ("verbose" , opts )))
170
+ opts -> flags |= REBASE_VERBOSE ;
171
+
172
+ strbuf_release (& buf );
173
+
174
+ return 0 ;
175
+ }
176
+
119
177
static int finish_rebase (struct rebase_options * opts )
120
178
{
121
179
struct strbuf dir = STRBUF_INIT ;
@@ -169,12 +227,13 @@ static int run_specific_rebase(struct rebase_options *opts)
169
227
add_var (& script_snippet , "state_dir" , opts -> state_dir );
170
228
171
229
add_var (& script_snippet , "upstream_name" , opts -> upstream_name );
172
- add_var (& script_snippet , "upstream" ,
173
- oid_to_hex (& opts -> upstream -> object .oid ));
230
+ add_var (& script_snippet , "upstream" , opts -> upstream ?
231
+ oid_to_hex (& opts -> upstream -> object .oid ) : NULL );
174
232
add_var (& script_snippet , "head_name" ,
175
233
opts -> head_name ? opts -> head_name : "detached HEAD" );
176
234
add_var (& script_snippet , "orig_head" , oid_to_hex (& opts -> orig_head ));
177
- add_var (& script_snippet , "onto" , oid_to_hex (& opts -> onto -> object .oid ));
235
+ add_var (& script_snippet , "onto" , opts -> onto ?
236
+ oid_to_hex (& opts -> onto -> object .oid ) : NULL );
178
237
add_var (& script_snippet , "onto_name" , opts -> onto_name );
179
238
add_var (& script_snippet , "revisions" , opts -> revisions );
180
239
add_var (& script_snippet , "restrict_revision" , opts -> restrict_revision ?
@@ -190,6 +249,7 @@ static int run_specific_rebase(struct rebase_options *opts)
190
249
opts -> flags & REBASE_FORCE ? "t" : "" );
191
250
if (opts -> switch_to )
192
251
add_var (& script_snippet , "switch_to" , opts -> switch_to );
252
+ add_var (& script_snippet , "action" , opts -> action ? opts -> action : "" );
193
253
194
254
switch (opts -> type ) {
195
255
case REBASE_AM :
@@ -401,12 +461,21 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
401
461
.git_am_opt = STRBUF_INIT ,
402
462
};
403
463
const char * branch_name ;
404
- int ret , flags , in_progress = 0 ;
464
+ int ret , flags , total_argc , in_progress = 0 ;
405
465
int ok_to_skip_pre_rebase = 0 ;
406
466
struct strbuf msg = STRBUF_INIT ;
407
467
struct strbuf revisions = STRBUF_INIT ;
408
468
struct strbuf buf = STRBUF_INIT ;
409
469
struct object_id merge_base ;
470
+ enum {
471
+ NO_ACTION ,
472
+ ACTION_CONTINUE ,
473
+ ACTION_SKIP ,
474
+ ACTION_ABORT ,
475
+ ACTION_QUIT ,
476
+ ACTION_EDIT_TODO ,
477
+ ACTION_SHOW_CURRENT_PATCH ,
478
+ } action = NO_ACTION ;
410
479
struct option builtin_rebase_options [] = {
411
480
OPT_STRING (0 , "onto" , & options .onto_name ,
412
481
N_ ("revision" ),
@@ -428,6 +497,20 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
428
497
OPT_BIT (0 , "no-ff" , & options .flags ,
429
498
N_ ("cherry-pick all commits, even if unchanged" ),
430
499
REBASE_FORCE ),
500
+ OPT_CMDMODE (0 , "continue" , & action , N_ ("continue" ),
501
+ ACTION_CONTINUE ),
502
+ OPT_CMDMODE (0 , "skip" , & action ,
503
+ N_ ("skip current patch and continue" ), ACTION_SKIP ),
504
+ OPT_CMDMODE (0 , "abort" , & action ,
505
+ N_ ("abort and check out the original branch" ),
506
+ ACTION_ABORT ),
507
+ OPT_CMDMODE (0 , "quit" , & action ,
508
+ N_ ("abort but keep HEAD where it is" ), ACTION_QUIT ),
509
+ OPT_CMDMODE (0 , "edit-todo" , & action , N_ ("edit the todo list "
510
+ "during an interactive rebase" ), ACTION_EDIT_TODO ),
511
+ OPT_CMDMODE (0 , "show-current-patch" , & action ,
512
+ N_ ("show the patch file being applied or merged" ),
513
+ ACTION_SHOW_CURRENT_PATCH ),
431
514
OPT_END (),
432
515
};
433
516
@@ -457,6 +540,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
457
540
458
541
git_config (rebase_config , & options );
459
542
543
+ strbuf_reset (& buf );
544
+ strbuf_addf (& buf , "%s/applying" , apply_dir ());
545
+ if (file_exists (buf .buf ))
546
+ die (_ ("It looks like 'git am' is in progress. Cannot rebase." ));
547
+
460
548
if (is_directory (apply_dir ())) {
461
549
options .type = REBASE_AM ;
462
550
options .state_dir = apply_dir ();
@@ -481,14 +569,110 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
481
569
if (options .type != REBASE_UNSPECIFIED )
482
570
in_progress = 1 ;
483
571
572
+ total_argc = argc ;
484
573
argc = parse_options (argc , argv , prefix ,
485
574
builtin_rebase_options ,
486
575
builtin_rebase_usage , 0 );
487
576
577
+ if (action != NO_ACTION && total_argc != 2 ) {
578
+ usage_with_options (builtin_rebase_usage ,
579
+ builtin_rebase_options );
580
+ }
581
+
488
582
if (argc > 2 )
489
583
usage_with_options (builtin_rebase_usage ,
490
584
builtin_rebase_options );
491
585
586
+ if (action != NO_ACTION && !in_progress )
587
+ die (_ ("No rebase in progress?" ));
588
+
589
+ if (action == ACTION_EDIT_TODO && !is_interactive (& options ))
590
+ die (_ ("The --edit-todo action can only be used during "
591
+ "interactive rebase." ));
592
+
593
+ switch (action ) {
594
+ case ACTION_CONTINUE : {
595
+ struct object_id head ;
596
+ struct lock_file lock_file = LOCK_INIT ;
597
+ int fd ;
598
+
599
+ options .action = "continue" ;
600
+
601
+ /* Sanity check */
602
+ if (get_oid ("HEAD" , & head ))
603
+ die (_ ("Cannot read HEAD" ));
604
+
605
+ fd = hold_locked_index (& lock_file , 0 );
606
+ if (read_index (the_repository -> index ) < 0 )
607
+ die (_ ("could not read index" ));
608
+ refresh_index (the_repository -> index , REFRESH_QUIET , NULL , NULL ,
609
+ NULL );
610
+ if (0 <= fd )
611
+ update_index_if_able (the_repository -> index ,
612
+ & lock_file );
613
+ rollback_lock_file (& lock_file );
614
+
615
+ if (has_unstaged_changes (1 )) {
616
+ puts (_ ("You must edit all merge conflicts and then\n"
617
+ "mark them as resolved using git add" ));
618
+ exit (1 );
619
+ }
620
+ if (read_basic_state (& options ))
621
+ exit (1 );
622
+ goto run_rebase ;
623
+ }
624
+ case ACTION_SKIP : {
625
+ struct string_list merge_rr = STRING_LIST_INIT_DUP ;
626
+
627
+ options .action = "skip" ;
628
+
629
+ rerere_clear (& merge_rr );
630
+ string_list_clear (& merge_rr , 1 );
631
+
632
+ if (reset_head (NULL , "reset" , NULL , 0 ) < 0 )
633
+ die (_ ("could not discard worktree changes" ));
634
+ if (read_basic_state (& options ))
635
+ exit (1 );
636
+ goto run_rebase ;
637
+ }
638
+ case ACTION_ABORT : {
639
+ struct string_list merge_rr = STRING_LIST_INIT_DUP ;
640
+ options .action = "abort" ;
641
+
642
+ rerere_clear (& merge_rr );
643
+ string_list_clear (& merge_rr , 1 );
644
+
645
+ if (read_basic_state (& options ))
646
+ exit (1 );
647
+ if (reset_head (& options .orig_head , "reset" ,
648
+ options .head_name , 0 ) < 0 )
649
+ die (_ ("could not move back to %s" ),
650
+ oid_to_hex (& options .orig_head ));
651
+ ret = finish_rebase (& options );
652
+ goto cleanup ;
653
+ }
654
+ case ACTION_QUIT : {
655
+ strbuf_reset (& buf );
656
+ strbuf_addstr (& buf , options .state_dir );
657
+ ret = !!remove_dir_recursively (& buf , 0 );
658
+ if (ret )
659
+ die (_ ("could not remove '%s'" ), options .state_dir );
660
+ goto cleanup ;
661
+ }
662
+ case ACTION_EDIT_TODO :
663
+ options .action = "edit-todo" ;
664
+ options .dont_finish_rebase = 1 ;
665
+ goto run_rebase ;
666
+ case ACTION_SHOW_CURRENT_PATCH :
667
+ options .action = "show-current-patch" ;
668
+ options .dont_finish_rebase = 1 ;
669
+ goto run_rebase ;
670
+ case NO_ACTION :
671
+ break ;
672
+ default :
673
+ BUG ("action: %d" , action );
674
+ }
675
+
492
676
/* Make sure no rebase is in progress */
493
677
if (in_progress ) {
494
678
const char * last_slash = strrchr (options .state_dir , '/' );
@@ -720,6 +904,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
720
904
721
905
options .revisions = revisions .buf ;
722
906
907
+ run_rebase :
723
908
ret = !!run_specific_rebase (& options );
724
909
725
910
cleanup :
0 commit comments