2323#include "hashmap.h"
2424#include "notes-utils.h"
2525#include "sigchain.h"
26+ #include "unpack-trees.h"
27+ #include "worktree.h"
2628
2729#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
2830
@@ -120,6 +122,13 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
120122static GIT_PATH_FUNC (rebase_path_rewritten_list , "rebase-merge/rewritten-list" )
121123static GIT_PATH_FUNC (rebase_path_rewritten_pending ,
122124 "rebase-merge/rewritten-pending" )
125+
126+ /*
127+ * The path of the file listing refs that need to be deleted after the rebase
128+ * finishes. This is used by the `label` command to record the need for cleanup.
129+ */
130+ static GIT_PATH_FUNC (rebase_path_refs_to_delete , "rebase-merge/refs-to-delete" )
131+
123132/*
124133 * The following files are written by git-rebase just after parsing the
125134 * command-line (and are only consumed, not modified, by the sequencer).
@@ -245,18 +254,34 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
245254
246255int sequencer_remove_state (struct replay_opts * opts )
247256{
248- struct strbuf dir = STRBUF_INIT ;
257+ struct strbuf buf = STRBUF_INIT ;
249258 int i ;
250259
260+ if (is_rebase_i (opts ) &&
261+ strbuf_read_file (& buf , rebase_path_refs_to_delete (), 0 ) > 0 ) {
262+ char * p = buf .buf ;
263+ while (* p ) {
264+ char * eol = strchr (p , '\n' );
265+ if (eol )
266+ * eol = '\0' ;
267+ if (delete_ref ("(rebase -i) cleanup" , p , NULL , 0 ) < 0 )
268+ warning (_ ("could not delete '%s'" ), p );
269+ if (!eol )
270+ break ;
271+ p = eol + 1 ;
272+ }
273+ }
274+
251275 free (opts -> gpg_sign );
252276 free (opts -> strategy );
253277 for (i = 0 ; i < opts -> xopts_nr ; i ++ )
254278 free (opts -> xopts [i ]);
255279 free (opts -> xopts );
256280
257- strbuf_addstr (& dir , get_dir (opts ));
258- remove_dir_recursively (& dir , 0 );
259- strbuf_release (& dir );
281+ strbuf_reset (& buf );
282+ strbuf_addstr (& buf , get_dir (opts ));
283+ remove_dir_recursively (& buf , 0 );
284+ strbuf_release (& buf );
260285
261286 return 0 ;
262287}
@@ -1280,6 +1305,8 @@ enum todo_command {
12801305 TODO_SQUASH ,
12811306 /* commands that do something else than handling a single commit */
12821307 TODO_EXEC ,
1308+ TODO_LABEL ,
1309+ TODO_RESET ,
12831310 /* commands that do nothing but are counted for reporting progress */
12841311 TODO_NOOP ,
12851312 TODO_DROP ,
@@ -1298,6 +1325,8 @@ static struct {
12981325 { 'f' , "fixup" },
12991326 { 's' , "squash" },
13001327 { 'x' , "exec" },
1328+ { 'l' , "label" },
1329+ { 't' , "reset" },
13011330 { 0 , "noop" },
13021331 { 'd' , "drop" },
13031332 { 0 , NULL }
@@ -1803,7 +1832,8 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
18031832 return error (_ ("missing arguments for %s" ),
18041833 command_to_string (item -> command ));
18051834
1806- if (item -> command == TODO_EXEC ) {
1835+ if (item -> command == TODO_EXEC || item -> command == TODO_LABEL ||
1836+ item -> command == TODO_RESET ) {
18071837 item -> commit = NULL ;
18081838 item -> arg = bol ;
18091839 item -> arg_len = (int )(eol - bol );
@@ -2471,6 +2501,159 @@ static int do_exec(const char *command_line)
24712501 return status ;
24722502}
24732503
2504+ static int safe_append (const char * filename , const char * fmt , ...)
2505+ {
2506+ va_list ap ;
2507+ struct lock_file lock = LOCK_INIT ;
2508+ int fd = hold_lock_file_for_update (& lock , filename ,
2509+ LOCK_REPORT_ON_ERROR );
2510+ struct strbuf buf = STRBUF_INIT ;
2511+
2512+ if (fd < 0 )
2513+ return -1 ;
2514+
2515+ if (strbuf_read_file (& buf , filename , 0 ) < 0 && errno != ENOENT ) {
2516+ error_errno (_ ("could not read '%s'" ), filename );
2517+ rollback_lock_file (& lock );
2518+ return -1 ;
2519+ }
2520+ strbuf_complete (& buf , '\n' );
2521+ va_start (ap , fmt );
2522+ strbuf_vaddf (& buf , fmt , ap );
2523+ va_end (ap );
2524+
2525+ if (write_in_full (fd , buf .buf , buf .len ) < 0 ) {
2526+ error_errno (_ ("could not write to '%s'" ), filename );
2527+ strbuf_release (& buf );
2528+ rollback_lock_file (& lock );
2529+ return -1 ;
2530+ }
2531+ if (commit_lock_file (& lock ) < 0 ) {
2532+ strbuf_release (& buf );
2533+ rollback_lock_file (& lock );
2534+ return error (_ ("failed to finalize '%s'" ), filename );
2535+ }
2536+
2537+ strbuf_release (& buf );
2538+ return 0 ;
2539+ }
2540+
2541+ static int do_label (const char * name , int len )
2542+ {
2543+ struct ref_store * refs = get_main_ref_store ();
2544+ struct ref_transaction * transaction ;
2545+ struct strbuf ref_name = STRBUF_INIT , err = STRBUF_INIT ;
2546+ struct strbuf msg = STRBUF_INIT ;
2547+ int ret = 0 ;
2548+ struct object_id head_oid ;
2549+
2550+ if (len == 1 && * name == '#' )
2551+ return error ("Illegal label name: '%.*s'" , len , name );
2552+
2553+ strbuf_addf (& ref_name , "refs/rewritten/%.*s" , len , name );
2554+ strbuf_addf (& msg , "rebase -i (label) '%.*s'" , len , name );
2555+
2556+ transaction = ref_store_transaction_begin (refs , & err );
2557+ if (!transaction ) {
2558+ error ("%s" , err .buf );
2559+ ret = -1 ;
2560+ } else if (get_oid ("HEAD" , & head_oid )) {
2561+ error (_ ("could not read HEAD" ));
2562+ ret = -1 ;
2563+ } else if (ref_transaction_update (transaction , ref_name .buf , & head_oid ,
2564+ NULL , 0 , msg .buf , & err ) < 0 ||
2565+ ref_transaction_commit (transaction , & err )) {
2566+ error ("%s" , err .buf );
2567+ ret = -1 ;
2568+ }
2569+ ref_transaction_free (transaction );
2570+ strbuf_release (& err );
2571+ strbuf_release (& msg );
2572+
2573+ if (!ret )
2574+ ret = safe_append (rebase_path_refs_to_delete (),
2575+ "%s\n" , ref_name .buf );
2576+ strbuf_release (& ref_name );
2577+
2578+ return ret ;
2579+ }
2580+
2581+ static const char * reflog_message (struct replay_opts * opts ,
2582+ const char * sub_action , const char * fmt , ...);
2583+
2584+ static int do_reset (const char * name , int len , struct replay_opts * opts )
2585+ {
2586+ struct strbuf ref_name = STRBUF_INIT ;
2587+ struct object_id oid ;
2588+ struct lock_file lock = LOCK_INIT ;
2589+ struct tree_desc desc ;
2590+ struct tree * tree ;
2591+ struct unpack_trees_options unpack_tree_opts ;
2592+ int ret = 0 , i ;
2593+
2594+ if (hold_locked_index (& lock , LOCK_REPORT_ON_ERROR ) < 0 )
2595+ return -1 ;
2596+
2597+ /* Determine the length of the label */
2598+ for (i = 0 ; i < len ; i ++ )
2599+ if (isspace (name [i ]))
2600+ len = i ;
2601+
2602+ strbuf_addf (& ref_name , "refs/rewritten/%.*s" , len , name );
2603+ if (get_oid (ref_name .buf , & oid ) &&
2604+ get_oid (ref_name .buf + strlen ("refs/rewritten/" ), & oid )) {
2605+ error (_ ("could not read '%s'" ), ref_name .buf );
2606+ rollback_lock_file (& lock );
2607+ strbuf_release (& ref_name );
2608+ return -1 ;
2609+ }
2610+
2611+ memset (& unpack_tree_opts , 0 , sizeof (unpack_tree_opts ));
2612+ setup_unpack_trees_porcelain (& unpack_tree_opts , "reset" );
2613+ unpack_tree_opts .head_idx = 1 ;
2614+ unpack_tree_opts .src_index = & the_index ;
2615+ unpack_tree_opts .dst_index = & the_index ;
2616+ unpack_tree_opts .fn = oneway_merge ;
2617+ unpack_tree_opts .merge = 1 ;
2618+ unpack_tree_opts .update = 1 ;
2619+
2620+ if (read_cache_unmerged ()) {
2621+ rollback_lock_file (& lock );
2622+ strbuf_release (& ref_name );
2623+ return error_resolve_conflict (_ (action_name (opts )));
2624+ }
2625+
2626+ if (!fill_tree_descriptor (& desc , & oid )) {
2627+ error (_ ("failed to find tree of %s" ), oid_to_hex (& oid ));
2628+ rollback_lock_file (& lock );
2629+ free ((void * )desc .buffer );
2630+ strbuf_release (& ref_name );
2631+ return -1 ;
2632+ }
2633+
2634+ if (unpack_trees (1 , & desc , & unpack_tree_opts )) {
2635+ rollback_lock_file (& lock );
2636+ free ((void * )desc .buffer );
2637+ strbuf_release (& ref_name );
2638+ return -1 ;
2639+ }
2640+
2641+ tree = parse_tree_indirect (& oid );
2642+ prime_cache_tree (& the_index , tree );
2643+
2644+ if (write_locked_index (& the_index , & lock , COMMIT_LOCK ) < 0 )
2645+ ret = error (_ ("could not write index" ));
2646+ free ((void * )desc .buffer );
2647+
2648+ if (!ret )
2649+ ret = update_ref (reflog_message (opts , "reset" , "'%.*s'" ,
2650+ len , name ), "HEAD" , & oid ,
2651+ NULL , 0 , UPDATE_REFS_MSG_ON_ERR );
2652+
2653+ strbuf_release (& ref_name );
2654+ return ret ;
2655+ }
2656+
24742657static int is_final_fixup (struct todo_list * todo_list )
24752658{
24762659 int i = todo_list -> current ;
@@ -2574,7 +2757,7 @@ N_("Could not execute the todo command\n"
25742757
25752758static int pick_commits (struct todo_list * todo_list , struct replay_opts * opts )
25762759{
2577- int res = 0 ;
2760+ int res = 0 , reschedule = 0 ;
25782761
25792762 setenv (GIT_REFLOG_ACTION , action_name (opts ), 0 );
25802763 if (opts -> allow_ff )
@@ -2645,7 +2828,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
26452828 intend_to_amend ();
26462829 return error_failed_squash (item -> commit , opts ,
26472830 item -> arg_len , item -> arg );
2648- } else if (res && is_rebase_i (opts ))
2831+ } else if (res && is_rebase_i (opts ) && item -> commit )
26492832 return res | error_with_patch (item -> commit ,
26502833 item -> arg , item -> arg_len , opts , res ,
26512834 item -> command == TODO_REWORD );
@@ -2671,9 +2854,25 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
26712854 /* `current` will be incremented below */
26722855 todo_list -> current = -1 ;
26732856 }
2857+ } else if (item -> command == TODO_LABEL ) {
2858+ if ((res = do_label (item -> arg , item -> arg_len )))
2859+ reschedule = 1 ;
2860+ } else if (item -> command == TODO_RESET ) {
2861+ if ((res = do_reset (item -> arg , item -> arg_len , opts )))
2862+ reschedule = 1 ;
26742863 } else if (!is_noop (item -> command ))
26752864 return error (_ ("unknown command %d" ), item -> command );
26762865
2866+ if (reschedule ) {
2867+ advise (_ (rescheduled_advice ),
2868+ get_item_line_length (todo_list ,
2869+ todo_list -> current ),
2870+ get_item_line (todo_list , todo_list -> current ));
2871+ todo_list -> current -- ;
2872+ if (save_todo (todo_list , opts ))
2873+ return -1 ;
2874+ }
2875+
26772876 todo_list -> current ++ ;
26782877 if (res )
26792878 return res ;
0 commit comments