]>
Commit | Line | Data |
---|---|---|
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 | GIT_CHERRY_PICK_HELP="$resolvemsg" | |
17 | export GIT_CHERRY_PICK_HELP | |
18 | ||
19 | comment_char=$(git config --get core.commentchar 2>/dev/null) | |
20 | case "$comment_char" in | |
21 | '' | auto) | |
22 | comment_char="#" | |
23 | ;; | |
24 | ?) | |
25 | ;; | |
26 | *) | |
27 | comment_char=$(echo "$comment_char" | cut -c1) | |
28 | ;; | |
29 | esac | |
30 | ||
31 | orig_reflog_action="$GIT_REFLOG_ACTION" | |
32 | ||
33 | comment_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 | ||
42 | append_todo_help () { | |
43 | gettext " | |
44 | Commands: | |
45 | p, pick <commit> = use commit | |
46 | r, reword <commit> = use commit, but edit the commit message | |
47 | e, edit <commit> = use commit, but stop for amending | |
48 | s, squash <commit> = use commit, but meld into previous commit | |
49 | f, fixup <commit> = like \"squash\", but discard this commit's log message | |
50 | x, exec <command> = run command (the rest of the line) using shell | |
51 | d, drop <commit> = remove commit | |
52 | l, label <label> = label current HEAD with a name | |
53 | t, reset <label> = reset HEAD to a label | |
54 | m, 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 | ||
59 | These 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 " | |
65 | Do not remove any line. Use 'drop' explicitly to remove a commit. | |
66 | " | git stripspace --comment-lines >>"$todo" | |
67 | else | |
68 | gettext " | |
69 | If you remove a line here THAT COMMIT WILL BE LOST. | |
70 | " | git stripspace --comment-lines >>"$todo" | |
71 | fi | |
72 | } | |
73 | ||
74 | die_abort () { | |
75 | apply_autostash | |
76 | rm -rf "$state_dir" | |
77 | die "$1" | |
78 | } | |
79 | ||
80 | has_action () { | |
81 | test -n "$(git stripspace --strip-comments <"$1")" | |
82 | } | |
83 | ||
84 | git_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 | ||
97 | expand_todo_ids() { | |
98 | git rebase--helper --expand-ids | |
99 | } | |
100 | ||
101 | collapse_todo_ids() { | |
102 | git rebase--helper --shorten-ids | |
103 | } | |
104 | ||
105 | # Switch to the branch in $into and notify it in the reflog | |
106 | checkout_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 | ||
112 | get_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. | |
131 | initiate_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 " | |
148 | You are editing the todo file of an ongoing interactive rebase. | |
149 | To 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 | ||
169 | setup_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 | ||
182 | init_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 | ||
191 | init_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 | ||
208 | complete_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 | ||
216 | cat >>"$todo" <<EOF | |
217 | ||
218 | $comment_char $(eval_ngettext \ | |
219 | "Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \ | |
220 | "Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \ | |
221 | "$todocount") | |
222 | EOF | |
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 | ||
264 | git_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 | } |