1# This shell script fragment is sourced by git-rebase to implement 2# its interactive mode. "git rebase --interactive" makes it easy 3# to fix up commits in the middle of a series and rearrange commits. 4# 5# Copyright (c) 2006 Johannes E. Schindelin 6# 7# The original idea comes from Eric W. Biederman, in 8# https://public-inbox.org/git/m1odwkyuf5.fsf_-_@ebiederm.dsl.xmission.com/ 9# 10# The file containing rebase commands, comments, and empty lines. 11# This file is created by "git rebase -i" then edited by the user. As 12# the lines are processed, they are removed from the front of this 13# file and written to the tail of $done. 14todo="$state_dir"/git-rebase-todo 15 16GIT_CHERRY_PICK_HELP="$resolvemsg" 17export GIT_CHERRY_PICK_HELP 18 19comment_char=$(git config --get core.commentchar 2>/dev/null) 20case "$comment_char" in 21'' | auto) 22 comment_char="#" 23 ;; 24?) 25 ;; 26*) 27 comment_char=$(echo "$comment_char" | cut -c1) 28 ;; 29esac 30 31orig_reflog_action="$GIT_REFLOG_ACTION" 32 33comment_for_reflog () { 34 case "$orig_reflog_action" in 35 ''|rebase*) 36 GIT_REFLOG_ACTION="rebase -i ($1)" 37 export GIT_REFLOG_ACTION 38 ;; 39 esac 40} 41 42append_todo_help () { 43 gettext " 44Commands: 45p, pick <commit> = use commit 46r, reword <commit> = use commit, but edit the commit message 47e, edit <commit> = use commit, but stop for amending 48s, squash <commit> = use commit, but meld into previous commit 49f, fixup <commit> = like \"squash\", but discard this commit's log message 50x, exec <command> = run command (the rest of the line) using shell 51d, drop <commit> = remove commit 52l, label <label> = label current HEAD with a name 53t, reset <label> = reset HEAD to a label 54m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] 55. create a merge commit using the original merge commit's 56. message (or the oneline, if no original merge commit was 57. specified). Use -c <commit> to reword the commit message. 58 59These lines can be re-ordered; they are executed from top to bottom. 60" | git stripspace --comment-lines >>"$todo" 61 62 if test $(get_missing_commit_check_level) = error 63 then 64 gettext " 65Do not remove any line. Use 'drop' explicitly to remove a commit. 66" | git stripspace --comment-lines >>"$todo" 67 else 68 gettext " 69If you remove a line here THAT COMMIT WILL BE LOST. 70" | git stripspace --comment-lines >>"$todo" 71 fi 72} 73 74die_abort () { 75 apply_autostash 76 rm -rf "$state_dir" 77 die "$1" 78} 79 80has_action () { 81 test -n "$(git stripspace --strip-comments <"$1")" 82} 83 84git_sequence_editor () { 85 if test -z "$GIT_SEQUENCE_EDITOR" 86 then 87 GIT_SEQUENCE_EDITOR="$(git config sequence.editor)" 88 if [ -z "$GIT_SEQUENCE_EDITOR" ] 89 then 90 GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $? 91 fi 92 fi 93 94 eval "$GIT_SEQUENCE_EDITOR" '"$@"' 95} 96 97expand_todo_ids() { 98 git rebase--helper --expand-ids 99} 100 101collapse_todo_ids() { 102 git rebase--helper --shorten-ids 103} 104 105# Switch to the branch in $into and notify it in the reflog 106checkout_onto () { 107 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" 108 output git checkout $onto || die_abort "$(gettext "could not detach HEAD")" 109 git update-ref ORIG_HEAD $orig_head 110} 111 112get_missing_commit_check_level () { 113 check_level=$(git config --get rebase.missingCommitsCheck) 114 check_level=${check_level:-ignore} 115 # Don't be case sensitive 116 printf '%s' "$check_level" | tr 'A-Z' 'a-z' 117} 118 119# Initiate an action. If the cannot be any 120# further action it may exec a command 121# or exit and not return. 122# 123# TODO: Consider a cleaner return model so it 124# never exits and always return 0 if process 125# is complete. 126# 127# Parameter 1 is the action to initiate. 128# 129# Returns 0 if the action was able to complete 130# and if 1 if further processing is required. 131initiate_action () { 132 case "$1" in 133 continue) 134 exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \ 135 --continue 136 ;; 137 skip) 138 git rerere clear 139 exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \ 140 --continue 141 ;; 142 edit-todo) 143 git stripspace --strip-comments <"$todo" >"$todo".new 144 mv -f "$todo".new "$todo" 145 collapse_todo_ids 146 append_todo_help 147 gettext " 148You are editing the todo file of an ongoing interactive rebase. 149To continue rebase after editing, run: 150 git rebase --continue 151 152" | git stripspace --comment-lines >>"$todo" 153 154 git_sequence_editor "$todo" || 155 die "$(gettext "Could not execute editor")" 156 expand_todo_ids 157 158 exit 159 ;; 160 show-current-patch) 161 exec git show REBASE_HEAD -- 162 ;; 163 *) 164 return 1 # continue 165 ;; 166 esac 167} 168 169setup_reflog_action () { 170 comment_for_reflog start 171 172 if test ! -z "$switch_to" 173 then 174 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" 175 output git checkout "$switch_to" -- || 176 die "$(eval_gettext "Could not checkout \$switch_to")" 177 178 comment_for_reflog start 179 fi 180} 181 182init_basic_state () { 183 orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")" 184 mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")" 185 rm -f "$(git rev-parse --git-path REBASE_HEAD)" 186 187 : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")" 188 write_basic_state 189} 190 191init_revisions_and_shortrevisions () { 192 shorthead=$(git rev-parse --short $orig_head) 193 shortonto=$(git rev-parse --short $onto) 194 if test -z "$rebase_root" 195 # this is now equivalent to ! -z "$upstream" 196 then 197 shortupstream=$(git rev-parse --short $upstream) 198 revisions=$upstream...$orig_head 199 shortrevisions=$shortupstream..$shorthead 200 else 201 revisions=$onto...$orig_head 202 shortrevisions=$shorthead 203 test -z "$squash_onto" || 204 echo "$squash_onto" >"$state_dir"/squash-onto 205 fi 206} 207 208complete_action() { 209 test -s "$todo" || echo noop >> "$todo" 210 test -z "$autosquash" || git rebase--helper --rearrange-squash || exit 211 test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd" 212 213 todocount=$(git stripspace --strip-comments <"$todo" | wc -l) 214 todocount=${todocount##* } 215 216cat >>"$todo" <<EOF 217 218$comment_char $(eval_ngettext \ 219 "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \ 220 "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \ 221 "$todocount") 222EOF 223 append_todo_help 224 gettext " 225 However, if you remove everything, the rebase will be aborted. 226 227 " | git stripspace --comment-lines >>"$todo" 228 229 if test -z "$keep_empty" 230 then 231 printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo" 232 fi 233 234 235 has_action "$todo" || 236 return 2 237 238 cp "$todo" "$todo".backup 239 collapse_todo_ids 240 git_sequence_editor "$todo" || 241 die_abort "$(gettext "Could not execute editor")" 242 243 has_action "$todo" || 244 return 2 245 246 git rebase--helper --check-todo-list || { 247 ret=$? 248 checkout_onto 249 exit $ret 250 } 251 252 expand_todo_ids 253 254 test -n "$force_rebase" || 255 onto="$(git rebase--helper --skip-unnecessary-picks)" || 256 die "Could not skip unnecessary pick commands" 257 258 checkout_onto 259 require_clean_work_tree "rebase" 260 exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \ 261 --continue 262} 263 264git_rebase__interactive () { 265 initiate_action "$action" 266 ret=$? 267 if test $ret = 0; then 268 return 0 269 fi 270 271 setup_reflog_action 272 init_basic_state 273 274 init_revisions_and_shortrevisions 275 276 git rebase--helper --make-script ${keep_empty:+--keep-empty} \ 277 ${rebase_merges:+--rebase-merges} \ 278 ${rebase_cousins:+--rebase-cousins} \ 279 $revisions ${restrict_revision+^$restrict_revision} >"$todo" || 280 die "$(gettext "Could not generate todo list")" 281 282 complete_action 283}