rebase: introduce a dedicated backend for --preserve-merges

This duplicates git-rebase--interactive.sh to git-rebase--preserve-merges.sh. This is done to split -p from -i. No modifications are made to this file here, but any code that is not used by -p will be stripped in the next commit. Signed-off-by: Alban Gruin <alban.gruin@gmail.com>

Alban Gruin committed May 28, 2018 at 14:34 UTC c42abfe785729587643dec5e7bba414dc3943817
3 files changed +1072
.gitignore
+1
@@ -117,6 +117,7 @@
117 /git-rebase--helper
118 /git-rebase--interactive
119 /git-rebase--merge
120 +/git-rebase--preserve-merges
121 /git-receive-pack
122 /git-reflog
123 /git-remote
Makefile
+2
@@ -582,6 +582,7 @@ SCRIPT_LIB += git-mergetool--lib
582 SCRIPT_LIB += git-parse-remote
583 SCRIPT_LIB += git-rebase--am
584 SCRIPT_LIB += git-rebase--interactive
585 +SCRIPT_LIB += git-rebase--preserve-merges
586 SCRIPT_LIB += git-rebase--merge
587 SCRIPT_LIB += git-sh-setup
588 SCRIPT_LIB += git-sh-i18n
@@ -2271,6 +2272,7 @@ LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
2272 LOCALIZED_SH = $(SCRIPT_SH)
2273 LOCALIZED_SH += git-parse-remote.sh
2274 LOCALIZED_SH += git-rebase--interactive.sh
2275 +LOCALIZED_SH += git-rebase--preserve-merges.sh
2276 LOCALIZED_SH += git-sh-setup.sh
2277 LOCALIZED_PERL = $(SCRIPT_PERL)
2278
git-rebase--preserve-merges.sh new
+1069
@@ -0,0 +1,1069 @@
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.
14 +todo="$state_dir"/git-rebase-todo
15 +
16 +# The rebase command lines that have already been processed. A line
17 +# is moved here when it is first handled, before any associated user
18 +# actions.
19 +done="$state_dir"/done
20 +
21 +# The commit message that is planned to be used for any changes that
22 +# need to be committed following a user interaction.
23 +msg="$state_dir"/message
24 +
25 +# The file into which is accumulated the suggested commit message for
26 +# squash/fixup commands. When the first of a series of squash/fixups
27 +# is seen, the file is created and the commit message from the
28 +# previous commit and from the first squash/fixup commit are written
29 +# to it. The commit message for each subsequent squash/fixup commit
30 +# is appended to the file as it is processed.
31 +#
32 +# The first line of the file is of the form
33 +# # This is a combination of $count commits.
34 +# where $count is the number of commits whose messages have been
35 +# written to the file so far (including the initial "pick" commit).
36 +# Each time that a commit message is processed, this line is read and
37 +# updated. It is deleted just before the combined commit is made.
38 +squash_msg="$state_dir"/message-squash
39 +
40 +# If the current series of squash/fixups has not yet included a squash
41 +# command, then this file exists and holds the commit message of the
42 +# original "pick" commit. (If the series ends without a "squash"
43 +# command, then this can be used as the commit message of the combined
44 +# commit without opening the editor.)
45 +fixup_msg="$state_dir"/message-fixup
46 +
47 +# $rewritten is the name of a directory containing files for each
48 +# commit that is reachable by at least one merge base of $head and
49 +# $upstream. They are not necessarily rewritten, but their children
50 +# might be. This ensures that commits on merged, but otherwise
51 +# unrelated side branches are left alone. (Think "X" in the man page's
52 +# example.)
53 +rewritten="$state_dir"/rewritten
54 +
55 +dropped="$state_dir"/dropped
56 +
57 +end="$state_dir"/end
58 +msgnum="$state_dir"/msgnum
59 +
60 +# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
61 +# GIT_AUTHOR_DATE that will be used for the commit that is currently
62 +# being rebased.
63 +author_script="$state_dir"/author-script
64 +
65 +# When an "edit" rebase command is being processed, the SHA1 of the
66 +# commit to be edited is recorded in this file. When "git rebase
67 +# --continue" is executed, if there are any staged changes then they
68 +# will be amended to the HEAD commit, but only provided the HEAD
69 +# commit is still the commit to be edited. When any other rebase
70 +# command is processed, this file is deleted.
71 +amend="$state_dir"/amend
72 +
73 +# For the post-rewrite hook, we make a list of rewritten commits and
74 +# their new sha1s. The rewritten-pending list keeps the sha1s of
75 +# commits that have been processed, but not committed yet,
76 +# e.g. because they are waiting for a 'squash' command.
77 +rewritten_list="$state_dir"/rewritten-list
78 +rewritten_pending="$state_dir"/rewritten-pending
79 +
80 +# Work around Git for Windows' Bash whose "read" does not strip CRLF
81 +# and leaves CR at the end instead.
82 +cr=$(printf "\015")
83 +
84 +strategy_args=${strategy:+--strategy=$strategy}
85 +test -n "$strategy_opts" &&
86 +eval '
87 + for strategy_opt in '"$strategy_opts"'
88 + do
89 + strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
90 + done
91 +'
92 +
93 +GIT_CHERRY_PICK_HELP="$resolvemsg"
94 +export GIT_CHERRY_PICK_HELP
95 +
96 +comment_char=$(git config --get core.commentchar 2>/dev/null)
97 +case "$comment_char" in
98 +'' | auto)
99 + comment_char="#"
100 + ;;
101 +?)
102 + ;;
103 +*)
104 + comment_char=$(echo "$comment_char" | cut -c1)
105 + ;;
106 +esac
107 +
108 +warn () {
109 + printf '%s\n' "$*" >&2
110 +}
111 +
112 +# Output the commit message for the specified commit.
113 +commit_message () {
114 + git cat-file commit "$1" | sed "1,/^$/d"
115 +}
116 +
117 +orig_reflog_action="$GIT_REFLOG_ACTION"
118 +
119 +comment_for_reflog () {
120 + case "$orig_reflog_action" in
121 + ''|rebase*)
122 + GIT_REFLOG_ACTION="rebase -i ($1)"
123 + export GIT_REFLOG_ACTION
124 + ;;
125 + esac
126 +}
127 +
128 +last_count=
129 +mark_action_done () {
130 + sed -e 1q < "$todo" >> "$done"
131 + sed -e 1d < "$todo" >> "$todo".new
132 + mv -f "$todo".new "$todo"
133 + new_count=$(( $(git stripspace --strip-comments <"$done" | wc -l) ))
134 + echo $new_count >"$msgnum"
135 + total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
136 + echo $total >"$end"
137 + if test "$last_count" != "$new_count"
138 + then
139 + last_count=$new_count
140 + eval_gettext "Rebasing (\$new_count/\$total)"; printf "\r"
141 + test -z "$verbose" || echo
142 + fi
143 +}
144 +
145 +# Put the last action marked done at the beginning of the todo list
146 +# again. If there has not been an action marked done yet, leave the list of
147 +# items on the todo list unchanged.
148 +reschedule_last_action () {
149 + tail -n 1 "$done" | cat - "$todo" >"$todo".new
150 + sed -e \$d <"$done" >"$done".new
151 + mv -f "$todo".new "$todo"
152 + mv -f "$done".new "$done"
153 +}
154 +
155 +append_todo_help () {
156 + gettext "
157 +Commands:
158 +p, pick <commit> = use commit
159 +r, reword <commit> = use commit, but edit the commit message
160 +e, edit <commit> = use commit, but stop for amending
161 +s, squash <commit> = use commit, but meld into previous commit
162 +f, fixup <commit> = like \"squash\", but discard this commit's log message
163 +x, exec <commit> = run command (the rest of the line) using shell
164 +d, drop <commit> = remove commit
165 +l, label <label> = label current HEAD with a name
166 +t, reset <label> = reset HEAD to a label
167 +m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
168 +. create a merge commit using the original merge commit's
169 +. message (or the oneline, if no original merge commit was
170 +. specified). Use -c <commit> to reword the commit message.
171 +
172 +These lines can be re-ordered; they are executed from top to bottom.
173 +" | git stripspace --comment-lines >>"$todo"
174 +
175 + if test $(get_missing_commit_check_level) = error
176 + then
177 + gettext "
178 +Do not remove any line. Use 'drop' explicitly to remove a commit.
179 +" | git stripspace --comment-lines >>"$todo"
180 + else
181 + gettext "
182 +If you remove a line here THAT COMMIT WILL BE LOST.
183 +" | git stripspace --comment-lines >>"$todo"
184 + fi
185 +}
186 +
187 +make_patch () {
188 + sha1_and_parents="$(git rev-list --parents -1 "$1")"
189 + case "$sha1_and_parents" in
190 + ?*' '?*' '?*)
191 + git diff --cc $sha1_and_parents
192 + ;;
193 + ?*' '?*)
194 + git diff-tree -p "$1^!"
195 + ;;
196 + *)
197 + echo "Root commit"
198 + ;;
199 + esac > "$state_dir"/patch
200 + test -f "$msg" ||
201 + commit_message "$1" > "$msg"
202 + test -f "$author_script" ||
203 + get_author_ident_from_commit "$1" > "$author_script"
204 +}
205 +
206 +die_with_patch () {
207 + echo "$1" > "$state_dir"/stopped-sha
208 + git update-ref REBASE_HEAD "$1"
209 + make_patch "$1"
210 + die "$2"
211 +}
212 +
213 +exit_with_patch () {
214 + echo "$1" > "$state_dir"/stopped-sha
215 + git update-ref REBASE_HEAD "$1"
216 + make_patch $1
217 + git rev-parse --verify HEAD > "$amend"
218 + gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
219 + warn "$(eval_gettext "\
220 +You can amend the commit now, with
221 +
222 + git commit --amend \$gpg_sign_opt_quoted
223 +
224 +Once you are satisfied with your changes, run
225 +
226 + git rebase --continue")"
227 + warn
228 + exit $2
229 +}
230 +
231 +die_abort () {
232 + apply_autostash
233 + rm -rf "$state_dir"
234 + die "$1"
235 +}
236 +
237 +has_action () {
238 + test -n "$(git stripspace --strip-comments <"$1")"
239 +}
240 +
241 +is_empty_commit() {
242 + tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null) || {
243 + sha1=$1
244 + die "$(eval_gettext "\$sha1: not a commit that can be picked")"
245 + }
246 + ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null) ||
247 + ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
248 + test "$tree" = "$ptree"
249 +}
250 +
251 +is_merge_commit()
252 +{
253 + git rev-parse --verify --quiet "$1"^2 >/dev/null 2>&1
254 +}
255 +
256 +# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
257 +# GIT_AUTHOR_DATE exported from the current environment.
258 +do_with_author () {
259 + (
260 + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
261 + "$@"
262 + )
263 +}
264 +
265 +git_sequence_editor () {
266 + if test -z "$GIT_SEQUENCE_EDITOR"
267 + then
268 + GIT_SEQUENCE_EDITOR="$(git config sequence.editor)"
269 + if [ -z "$GIT_SEQUENCE_EDITOR" ]
270 + then
271 + GIT_SEQUENCE_EDITOR="$(git var GIT_EDITOR)" || return $?
272 + fi
273 + fi
274 +
275 + eval "$GIT_SEQUENCE_EDITOR" '"$@"'
276 +}
277 +
278 +pick_one () {
279 + ff=--ff
280 +
281 + case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
282 + case "$force_rebase" in '') ;; ?*) ff= ;; esac
283 + output git rev-parse --verify $sha1 || die "$(eval_gettext "Invalid commit name: \$sha1")"
284 +
285 + if is_empty_commit "$sha1"
286 + then
287 + empty_args="--allow-empty"
288 + fi
289 +
290 + test -d "$rewritten" &&
291 + pick_one_preserving_merges "$@" && return
292 + output eval git cherry-pick $allow_rerere_autoupdate $allow_empty_message \
293 + ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
294 + $signoff "$strategy_args" $empty_args $ff "$@"
295 +
296 + # If cherry-pick dies it leaves the to-be-picked commit unrecorded. Reschedule
297 + # previous task so this commit is not lost.
298 + ret=$?
299 + case "$ret" in [01]) ;; *) reschedule_last_action ;; esac
300 + return $ret
301 +}
302 +
303 +pick_one_preserving_merges () {
304 + fast_forward=t
305 + case "$1" in
306 + -n)
307 + fast_forward=f
308 + sha1=$2
309 + ;;
310 + *)
311 + sha1=$1
312 + ;;
313 + esac
314 + sha1=$(git rev-parse $sha1)
315 +
316 + if test -f "$state_dir"/current-commit && test "$fast_forward" = t
317 + then
318 + while read current_commit
319 + do
320 + git rev-parse HEAD > "$rewritten"/$current_commit
321 + done <"$state_dir"/current-commit
322 + rm "$state_dir"/current-commit ||
323 + die "$(gettext "Cannot write current commit's replacement sha1")"
324 + fi
325 +
326 + echo $sha1 >> "$state_dir"/current-commit
327 +
328 + # rewrite parents; if none were rewritten, we can fast-forward.
329 + new_parents=
330 + pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)"
331 + if test "$pend" = " "
332 + then
333 + pend=" root"
334 + fi
335 + while [ "$pend" != "" ]
336 + do
337 + p=$(expr "$pend" : ' \([^ ]*\)')
338 + pend="${pend# $p}"
339 +
340 + if test -f "$rewritten"/$p
341 + then
342 + new_p=$(cat "$rewritten"/$p)
343 +
344 + # If the todo reordered commits, and our parent is marked for
345 + # rewriting, but hasn't been gotten to yet, assume the user meant to
346 + # drop it on top of the current HEAD
347 + if test -z "$new_p"
348 + then
349 + new_p=$(git rev-parse HEAD)
350 + fi
351 +
352 + test $p != $new_p && fast_forward=f
353 + case "$new_parents" in
354 + *$new_p*)
355 + ;; # do nothing; that parent is already there
356 + *)
357 + new_parents="$new_parents $new_p"
358 + ;;
359 + esac
360 + else
361 + if test -f "$dropped"/$p
362 + then
363 + fast_forward=f
364 + replacement="$(cat "$dropped"/$p)"
365 + test -z "$replacement" && replacement=root
366 + pend=" $replacement$pend"
367 + else
368 + new_parents="$new_parents $p"
369 + fi
370 + fi
371 + done
372 + case $fast_forward in
373 + t)
374 + output warn "$(eval_gettext "Fast-forward to \$sha1")"
375 + output git reset --hard $sha1 ||
376 + die "$(eval_gettext "Cannot fast-forward to \$sha1")"
377 + ;;
378 + f)
379 + first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
380 +
381 + if [ "$1" != "-n" ]
382 + then
383 + # detach HEAD to current parent
384 + output git checkout $first_parent 2> /dev/null ||
385 + die "$(eval_gettext "Cannot move HEAD to \$first_parent")"
386 + fi
387 +
388 + case "$new_parents" in
389 + ' '*' '*)
390 + test "a$1" = a-n && die "$(eval_gettext "Refusing to squash a merge: \$sha1")"
391 +
392 + # redo merge
393 + author_script_content=$(get_author_ident_from_commit $sha1)
394 + eval "$author_script_content"
395 + msg_content="$(commit_message $sha1)"
396 + # No point in merging the first parent, that's HEAD
397 + new_parents=${new_parents# $first_parent}
398 + merge_args="--no-log --no-ff"
399 + if ! do_with_author output eval \
400 + git merge ${gpg_sign_opt:+$(git rev-parse \
401 + --sq-quote "$gpg_sign_opt")} \
402 + $allow_rerere_autoupdate "$merge_args" \
403 + "$strategy_args" \
404 + -m "$(git rev-parse --sq-quote "$msg_content")" \
405 + "$new_parents"
406 + then
407 + printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
408 + die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
409 + fi
410 + echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
411 + ;;
412 + *)
413 + output eval git cherry-pick $allow_rerere_autoupdate \
414 + $allow_empty_message \
415 + ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
416 + "$strategy_args" "$@" ||
417 + die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
418 + ;;
419 + esac
420 + ;;
421 + esac
422 +}
423 +
424 +this_nth_commit_message () {
425 + n=$1
426 + eval_gettext "This is the commit message #\${n}:"
427 +}
428 +
429 +skip_nth_commit_message () {
430 + n=$1
431 + eval_gettext "The commit message #\${n} will be skipped:"
432 +}
433 +
434 +update_squash_messages () {
435 + if test -f "$squash_msg"; then
436 + mv "$squash_msg" "$squash_msg".bak || exit
437 + count=$(($(sed -n \
438 + -e "1s/^$comment_char[^0-9]*\([0-9][0-9]*\).*/\1/p" \
439 + -e "q" < "$squash_msg".bak)+1))
440 + {
441 + printf '%s\n' "$comment_char $(eval_ngettext \
442 + "This is a combination of \$count commit." \
443 + "This is a combination of \$count commits." \
444 + $count)"
445 + sed -e 1d -e '2,/^./{
446 + /^$/d
447 + }' <"$squash_msg".bak
448 + } >"$squash_msg"
449 + else
450 + commit_message HEAD >"$fixup_msg" ||
451 + die "$(eval_gettext "Cannot write \$fixup_msg")"
452 + count=2
453 + {
454 + printf '%s\n' "$comment_char $(gettext "This is a combination of 2 commits.")"
455 + printf '%s\n' "$comment_char $(gettext "This is the 1st commit message:")"
456 + echo
457 + cat "$fixup_msg"
458 + } >"$squash_msg"
459 + fi
460 + case $1 in
461 + squash)
462 + rm -f "$fixup_msg"
463 + echo
464 + printf '%s\n' "$comment_char $(this_nth_commit_message $count)"
465 + echo
466 + commit_message $2
467 + ;;
468 + fixup)
469 + echo
470 + printf '%s\n' "$comment_char $(skip_nth_commit_message $count)"
471 + echo
472 + # Change the space after the comment character to TAB:
473 + commit_message $2 | git stripspace --comment-lines | sed -e 's/ / /'
474 + ;;
475 + esac >>"$squash_msg"
476 +}
477 +
478 +peek_next_command () {
479 + git stripspace --strip-comments <"$todo" | sed -n -e 's/ .*//p' -e q
480 +}
481 +
482 +# A squash/fixup has failed. Prepare the long version of the squash
483 +# commit message, then die_with_patch. This code path requires the
484 +# user to edit the combined commit message for all commits that have
485 +# been squashed/fixedup so far. So also erase the old squash
486 +# messages, effectively causing the combined commit to be used as the
487 +# new basis for any further squash/fixups. Args: sha1 rest
488 +die_failed_squash() {
489 + sha1=$1
490 + rest=$2
491 + mv "$squash_msg" "$msg" || exit
492 + rm -f "$fixup_msg"
493 + cp "$msg" "$GIT_DIR"/MERGE_MSG || exit
494 + warn
495 + warn "$(eval_gettext "Could not apply \$sha1... \$rest")"
496 + die_with_patch $sha1 ""
497 +}
498 +
499 +flush_rewritten_pending() {
500 + test -s "$rewritten_pending" || return
501 + newsha1="$(git rev-parse HEAD^0)"
502 + sed "s/$/ $newsha1/" < "$rewritten_pending" >> "$rewritten_list"
503 + rm -f "$rewritten_pending"
504 +}
505 +
506 +record_in_rewritten() {
507 + oldsha1="$(git rev-parse $1)"
508 + echo "$oldsha1" >> "$rewritten_pending"
509 +
510 + case "$(peek_next_command)" in
511 + squash|s|fixup|f)
512 + ;;
513 + *)
514 + flush_rewritten_pending
515 + ;;
516 + esac
517 +}
518 +
519 +do_pick () {
520 + sha1=$1
521 + rest=$2
522 + if test "$(git rev-parse HEAD)" = "$squash_onto"
523 + then
524 + # Set the correct commit message and author info on the
525 + # sentinel root before cherry-picking the original changes
526 + # without committing (-n). Finally, update the sentinel again
527 + # to include these changes. If the cherry-pick results in a
528 + # conflict, this means our behaviour is similar to a standard
529 + # failed cherry-pick during rebase, with a dirty index to
530 + # resolve before manually running git commit --amend then git
531 + # rebase --continue.
532 + git commit --allow-empty --allow-empty-message --amend \
533 + --no-post-rewrite -n -q -C $sha1 $signoff &&
534 + pick_one -n $sha1 &&
535 + git commit --allow-empty --allow-empty-message \
536 + --amend --no-post-rewrite -n -q -C $sha1 $signoff \
537 + ${gpg_sign_opt:+"$gpg_sign_opt"} ||
538 + die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
539 + else
540 + pick_one $sha1 ||
541 + die_with_patch $sha1 "$(eval_gettext "Could not apply \$sha1... \$rest")"
542 + fi
543 +}
544 +
545 +do_next () {
546 + rm -f "$msg" "$author_script" "$amend" "$state_dir"/stopped-sha || exit
547 + read -r command sha1 rest < "$todo"
548 + case "$command" in
549 + "$comment_char"*|''|noop|drop|d)
550 + mark_action_done
551 + ;;
552 + "$cr")
553 + # Work around CR left by "read" (e.g. with Git for Windows' Bash).
554 + mark_action_done
555 + ;;
556 + pick|p)
557 + comment_for_reflog pick
558 +
559 + mark_action_done
560 + do_pick $sha1 "$rest"
561 + record_in_rewritten $sha1
562 + ;;
563 + reword|r)
564 + comment_for_reflog reword
565 +
566 + mark_action_done
567 + do_pick $sha1 "$rest"
568 + git commit --amend --no-post-rewrite ${gpg_sign_opt:+"$gpg_sign_opt"} \
569 + $allow_empty_message || {
570 + warn "$(eval_gettext "\
571 +Could not amend commit after successfully picking \$sha1... \$rest
572 +This is most likely due to an empty commit message, or the pre-commit hook
573 +failed. If the pre-commit hook failed, you may need to resolve the issue before
574 +you are able to reword the commit.")"
575 + exit_with_patch $sha1 1
576 + }
577 + record_in_rewritten $sha1
578 + ;;
579 + edit|e)
580 + comment_for_reflog edit
581 +
582 + mark_action_done
583 + do_pick $sha1 "$rest"
584 + sha1_abbrev=$(git rev-parse --short $sha1)
585 + warn "$(eval_gettext "Stopped at \$sha1_abbrev... \$rest")"
586 + exit_with_patch $sha1 0
587 + ;;
588 + squash|s|fixup|f)
589 + case "$command" in
590 + squash|s)
591 + squash_style=squash
592 + ;;
593 + fixup|f)
594 + squash_style=fixup
595 + ;;
596 + esac
597 + comment_for_reflog $squash_style
598 +
599 + test -f "$done" && has_action "$done" ||
600 + die "$(eval_gettext "Cannot '\$squash_style' without a previous commit")"
601 +
602 + mark_action_done
603 + update_squash_messages $squash_style $sha1
604 + author_script_content=$(get_author_ident_from_commit HEAD)
605 + echo "$author_script_content" > "$author_script"
606 + eval "$author_script_content"
607 + if ! pick_one -n $sha1
608 + then
609 + git rev-parse --verify HEAD >"$amend"
610 + die_failed_squash $sha1 "$rest"
611 + fi
612 + case "$(peek_next_command)" in
613 + squash|s|fixup|f)
614 + # This is an intermediate commit; its message will only be
615 + # used in case of trouble. So use the long version:
616 + do_with_author output git commit --amend --no-verify -F "$squash_msg" \
617 + ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
618 + die_failed_squash $sha1 "$rest"
619 + ;;
620 + *)
621 + # This is the final command of this squash/fixup group
622 + if test -f "$fixup_msg"
623 + then
624 + do_with_author git commit --amend --no-verify -F "$fixup_msg" \
625 + ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
626 + die_failed_squash $sha1 "$rest"
627 + else
628 + cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
629 + rm -f "$GIT_DIR"/MERGE_MSG
630 + do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e \
631 + ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
632 + die_failed_squash $sha1 "$rest"
633 + fi
634 + rm -f "$squash_msg" "$fixup_msg"
635 + ;;
636 + esac
637 + record_in_rewritten $sha1
638 + ;;
639 + x|"exec")
640 + read -r command rest < "$todo"
641 + mark_action_done
642 + eval_gettextln "Executing: \$rest"
643 + "${SHELL:-@SHELL_PATH@}" -c "$rest" # Actual execution
644 + status=$?
645 + # Run in subshell because require_clean_work_tree can die.
646 + dirty=f
647 + (require_clean_work_tree "rebase" 2>/dev/null) || dirty=t
648 + if test "$status" -ne 0
649 + then
650 + warn "$(eval_gettext "Execution failed: \$rest")"
651 + test "$dirty" = f ||
652 + warn "$(gettext "and made changes to the index and/or the working tree")"
653 +
654 + warn "$(gettext "\
655 +You can fix the problem, and then run
656 +
657 + git rebase --continue")"
658 + warn
659 + if test $status -eq 127 # command not found
660 + then
661 + status=1
662 + fi
663 + exit "$status"
664 + elif test "$dirty" = t
665 + then
666 + # TRANSLATORS: after these lines is a command to be issued by the user
667 + warn "$(eval_gettext "\
668 +Execution succeeded: \$rest
669 +but left changes to the index and/or the working tree
670 +Commit or stash your changes, and then run
671 +
672 + git rebase --continue")"
673 + warn
674 + exit 1
675 + fi
676 + ;;
677 + *)
678 + warn "$(eval_gettext "Unknown command: \$command \$sha1 \$rest")"
679 + fixtodo="$(gettext "Please fix this using 'git rebase --edit-todo'.")"
680 + if git rev-parse --verify -q "$sha1" >/dev/null
681 + then
682 + die_with_patch $sha1 "$fixtodo"
683 + else
684 + die "$fixtodo"
685 + fi
686 + ;;
687 + esac
688 + test -s "$todo" && return
689 +
690 + comment_for_reflog finish &&
691 + newhead=$(git rev-parse HEAD) &&
692 + case $head_name in
693 + refs/*)
694 + message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
695 + git update-ref -m "$message" $head_name $newhead $orig_head &&
696 + git symbolic-ref \
697 + -m "$GIT_REFLOG_ACTION: returning to $head_name" \
698 + HEAD $head_name
699 + ;;
700 + esac && {
701 + test ! -f "$state_dir"/verbose ||
702 + git diff-tree --stat $orig_head..HEAD
703 + } &&
704 + {
705 + test -s "$rewritten_list" &&
706 + git notes copy --for-rewrite=rebase < "$rewritten_list" ||
707 + true # we don't care if this copying failed
708 + } &&
709 + hook="$(git rev-parse --git-path hooks/post-rewrite)"
710 + if test -x "$hook" && test -s "$rewritten_list"; then
711 + "$hook" rebase < "$rewritten_list"
712 + true # we don't care if this hook failed
713 + fi &&
714 + warn "$(eval_gettext "Successfully rebased and updated \$head_name.")"
715 +
716 + return 1 # not failure; just to break the do_rest loop
717 +}
718 +
719 +# can only return 0, when the infinite loop breaks
720 +do_rest () {
721 + while :
722 + do
723 + do_next || break
724 + done
725 +}
726 +
727 +expand_todo_ids() {
728 + git rebase--helper --expand-ids
729 +}
730 +
731 +collapse_todo_ids() {
732 + git rebase--helper --shorten-ids
733 +}
734 +
735 +# Switch to the branch in $into and notify it in the reflog
736 +checkout_onto () {
737 + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
738 + output git checkout $onto || die_abort "$(gettext "could not detach HEAD")"
739 + git update-ref ORIG_HEAD $orig_head
740 +}
741 +
742 +get_missing_commit_check_level () {
743 + check_level=$(git config --get rebase.missingCommitsCheck)
744 + check_level=${check_level:-ignore}
745 + # Don't be case sensitive
746 + printf '%s' "$check_level" | tr 'A-Z' 'a-z'
747 +}
748 +
749 +# Initiate an action. If the cannot be any
750 +# further action it may exec a command
751 +# or exit and not return.
752 +#
753 +# TODO: Consider a cleaner return model so it
754 +# never exits and always return 0 if process
755 +# is complete.
756 +#
757 +# Parameter 1 is the action to initiate.
758 +#
759 +# Returns 0 if the action was able to complete
760 +# and if 1 if further processing is required.
761 +initiate_action () {
762 + case "$1" in
763 + continue)
764 + if test ! -d "$rewritten"
765 + then
766 + exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
767 + --continue
768 + fi
769 + # do we have anything to commit?
770 + if git diff-index --cached --quiet HEAD --
771 + then
772 + # Nothing to commit -- skip this commit
773 +
774 + test ! -f "$GIT_DIR"/CHERRY_PICK_HEAD ||
775 + rm "$GIT_DIR"/CHERRY_PICK_HEAD ||
776 + die "$(gettext "Could not remove CHERRY_PICK_HEAD")"
777 + else
778 + if ! test -f "$author_script"
779 + then
780 + gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
781 + die "$(eval_gettext "\
782 +You have staged changes in your working tree.
783 +If these changes are meant to be
784 +squashed into the previous commit, run:
785 +
786 + git commit --amend \$gpg_sign_opt_quoted
787 +
788 +If they are meant to go into a new commit, run:
789 +
790 + git commit \$gpg_sign_opt_quoted
791 +
792 +In both cases, once you're done, continue with:
793 +
794 + git rebase --continue
795 +")"
796 + fi
797 + . "$author_script" ||
798 + die "$(gettext "Error trying to find the author identity to amend commit")"
799 + if test -f "$amend"
800 + then
801 + current_head=$(git rev-parse --verify HEAD)
802 + test "$current_head" = $(cat "$amend") ||
803 + die "$(gettext "\
804 +You have uncommitted changes in your working tree. Please commit them
805 +first and then run 'git rebase --continue' again.")"
806 + do_with_author git commit --amend --no-verify -F "$msg" -e \
807 + ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
808 + die "$(gettext "Could not commit staged changes.")"
809 + else
810 + do_with_author git commit --no-verify -F "$msg" -e \
811 + ${gpg_sign_opt:+"$gpg_sign_opt"} $allow_empty_message ||
812 + die "$(gettext "Could not commit staged changes.")"
813 + fi
814 + fi
815 +
816 + if test -r "$state_dir"/stopped-sha
817 + then
818 + record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
819 + fi
820 +
821 + require_clean_work_tree "rebase"
822 + do_rest
823 + return 0
824 + ;;
825 + skip)
826 + git rerere clear
827 +
828 + if test ! -d "$rewritten"
829 + then
830 + exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
831 + --continue
832 + fi
833 + do_rest
834 + return 0
835 + ;;
836 + edit-todo)
837 + git stripspace --strip-comments <"$todo" >"$todo".new
838 + mv -f "$todo".new "$todo"
839 + collapse_todo_ids
840 + append_todo_help
841 + gettext "
842 +You are editing the todo file of an ongoing interactive rebase.
843 +To continue rebase after editing, run:
844 + git rebase --continue
845 +
846 +" | git stripspace --comment-lines >>"$todo"
847 +
848 + git_sequence_editor "$todo" ||
849 + die "$(gettext "Could not execute editor")"
850 + expand_todo_ids
851 +
852 + exit
853 + ;;
854 + show-current-patch)
855 + exec git show REBASE_HEAD --
856 + ;;
857 + *)
858 + return 1 # continue
859 + ;;
860 + esac
861 +}
862 +
863 +setup_reflog_action () {
864 + comment_for_reflog start
865 +
866 + if test ! -z "$switch_to"
867 + then
868 + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
869 + output git checkout "$switch_to" -- ||
870 + die "$(eval_gettext "Could not checkout \$switch_to")"
871 +
872 + comment_for_reflog start
873 + fi
874 +}
875 +
876 +init_basic_state () {
877 + orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
878 + mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
879 + rm -f "$(git rev-parse --git-path REBASE_HEAD)"
880 +
881 + : > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
882 + write_basic_state
883 +}
884 +
885 +init_revisions_and_shortrevisions () {
886 + shorthead=$(git rev-parse --short $orig_head)
887 + shortonto=$(git rev-parse --short $onto)
888 + if test -z "$rebase_root"
889 + # this is now equivalent to ! -z "$upstream"
890 + then
891 + shortupstream=$(git rev-parse --short $upstream)
892 + revisions=$upstream...$orig_head
893 + shortrevisions=$shortupstream..$shorthead
894 + else
895 + revisions=$onto...$orig_head
896 + shortrevisions=$shorthead
897 + test -z "$squash_onto" ||
898 + echo "$squash_onto" >"$state_dir"/squash-onto
899 + fi
900 +}
901 +
902 +complete_action() {
903 + test -s "$todo" || echo noop >> "$todo"
904 + test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
905 + test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
906 +
907 + todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
908 + todocount=${todocount##* }
909 +
910 +cat >>"$todo" <<EOF
911 +
912 +$comment_char $(eval_ngettext \
913 + "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
914 + "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
915 + "$todocount")
916 +EOF
917 + append_todo_help
918 + gettext "
919 + However, if you remove everything, the rebase will be aborted.
920 +
921 + " | git stripspace --comment-lines >>"$todo"
922 +
923 + if test -z "$keep_empty"
924 + then
925 + printf '%s\n' "$comment_char $(gettext "Note that empty commits are commented out")" >>"$todo"
926 + fi
927 +
928 +
929 + has_action "$todo" ||
930 + return 2
931 +
932 + cp "$todo" "$todo".backup
933 + collapse_todo_ids
934 + git_sequence_editor "$todo" ||
935 + die_abort "$(gettext "Could not execute editor")"
936 +
937 + has_action "$todo" ||
938 + return 2
939 +
940 + git rebase--helper --check-todo-list || {
941 + ret=$?
942 + checkout_onto
943 + exit $ret
944 + }
945 +
946 + expand_todo_ids
947 +
948 + test -d "$rewritten" || test -n "$force_rebase" ||
949 + onto="$(git rebase--helper --skip-unnecessary-picks)" ||
950 + die "Could not skip unnecessary pick commands"
951 +
952 + checkout_onto
953 + if test ! -d "$rewritten"
954 + then
955 + require_clean_work_tree "rebase"
956 + exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
957 + --continue
958 + fi
959 + do_rest
960 +}
961 +
962 +git_rebase__interactive () {
963 + initiate_action "$action"
964 + ret=$?
965 + if test $ret = 0; then
966 + return 0
967 + fi
968 +
969 + setup_reflog_action
970 + init_basic_state
971 +
972 + init_revisions_and_shortrevisions
973 +
974 + git rebase--helper --make-script ${keep_empty:+--keep-empty} \
975 + ${rebase_merges:+--rebase-merges} \
976 + ${rebase_cousins:+--rebase-cousins} \
977 + $revisions ${restrict_revision+^$restrict_revision} >"$todo" ||
978 + die "$(gettext "Could not generate todo list")"
979 +
980 + complete_action
981 +}
982 +
983 +git_rebase__interactive__preserve_merges () {
984 + initiate_action "$action"
985 + ret=$?
986 + if test $ret = 0; then
987 + return 0
988 + fi
989 +
990 + setup_reflog_action
991 + init_basic_state
992 +
993 + if test -z "$rebase_root"
994 + then
995 + mkdir "$rewritten" &&
996 + for c in $(git merge-base --all $orig_head $upstream)
997 + do
998 + echo $onto > "$rewritten"/$c ||
999 + die "$(gettext "Could not init rewritten commits")"
1000 + done
1001 + else
1002 + mkdir "$rewritten" &&
1003 + echo $onto > "$rewritten"/root ||
1004 + die "$(gettext "Could not init rewritten commits")"
1005 + fi
1006 +
1007 + init_revisions_and_shortrevisions
1008 +
1009 + format=$(git config --get rebase.instructionFormat)
1010 + # the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
1011 + git rev-list --format="%m%H ${format:-%s}" \
1012 + --reverse --left-right --topo-order \
1013 + $revisions ${restrict_revision+^$restrict_revision} | \
1014 + sed -n "s/^>//p" |
1015 + while read -r sha1 rest
1016 + do
1017 + if test -z "$keep_empty" && is_empty_commit $sha1 && ! is_merge_commit $sha1
1018 + then
1019 + comment_out="$comment_char "
1020 + else
1021 + comment_out=
1022 + fi
1023 +
1024 + if test -z "$rebase_root"
1025 + then
1026 + preserve=t
1027 + for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
1028 + do
1029 + if test -f "$rewritten"/$p
1030 + then
1031 + preserve=f
1032 + fi
1033 + done
1034 + else
1035 + preserve=f
1036 + fi
1037 + if test f = "$preserve"
1038 + then
1039 + touch "$rewritten"/$sha1
1040 + printf '%s\n' "${comment_out}pick $sha1 $rest" >>"$todo"
1041 + fi
1042 + done
1043 +
1044 + # Watch for commits that been dropped by --cherry-pick
1045 + mkdir "$dropped"
1046 + # Save all non-cherry-picked changes
1047 + git rev-list $revisions --left-right --cherry-pick | \
1048 + sed -n "s/^>//p" > "$state_dir"/not-cherry-picks
1049 + # Now all commits and note which ones are missing in
1050 + # not-cherry-picks and hence being dropped
1051 + git rev-list $revisions |
1052 + while read rev
1053 + do
1054 + if test -f "$rewritten"/$rev &&
1055 + ! sane_grep "$rev" "$state_dir"/not-cherry-picks >/dev/null
1056 + then
1057 + # Use -f2 because if rev-list is telling us this commit is
1058 + # not worthwhile, we don't want to track its multiple heads,
1059 + # just the history of its first-parent for others that will
1060 + # be rebasing on top of it
1061 + git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$dropped"/$rev
1062 + sha1=$(git rev-list -1 $rev)
1063 + sane_grep -v "^[a-z][a-z]* $sha1" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo"
1064 + rm "$rewritten"/$rev
1065 + fi
1066 + done
1067 +
1068 + complete_action
1069 +}