Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 1 | #!/bin/sh |
| 2 | # |
| 3 | # Copyright (c) 2006 Johannes E. Schindelin |
| 4 | |
| 5 | # SHORT DESCRIPTION |
| 6 | # |
| 7 | # This script makes it easy to fix up commits in the middle of a series, |
| 8 | # and rearrange commits. |
| 9 | # |
| 10 | # The original idea comes from Eric W. Biederman, in |
| 11 | # http://article.gmane.org/gmane.comp.version-control.git/22407 |
| 12 | |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 13 | USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose] |
| 14 | [--onto <branch>] <upstream> [<branch>])' |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 15 | |
Junio C Hamano | 8f321a3 | 2007-11-06 01:50:02 -0800 | [diff] [blame] | 16 | OPTIONS_SPEC= |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 17 | . git-sh-setup |
| 18 | require_work_tree |
| 19 | |
| 20 | DOTEST="$GIT_DIR/.dotest-merge" |
Seth Falcon | c22486c | 2007-07-28 16:44:40 -0700 | [diff] [blame] | 21 | TODO="$DOTEST"/git-rebase-todo |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 22 | DONE="$DOTEST"/done |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 23 | MSG="$DOTEST"/message |
| 24 | SQUASH_MSG="$DOTEST"/message-squash |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 25 | REWRITTEN="$DOTEST"/rewritten |
| 26 | PRESERVE_MERGES= |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 27 | STRATEGY= |
| 28 | VERBOSE= |
Johannes Schindelin | 8e4a91b | 2007-07-08 03:02:47 +0100 | [diff] [blame] | 29 | test -d "$REWRITTEN" && PRESERVE_MERGES=t |
| 30 | test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" |
| 31 | test -f "$DOTEST"/verbose && VERBOSE=t |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 32 | |
Wincent Colaiuta | 804c717 | 2007-11-28 00:06:36 -0800 | [diff] [blame] | 33 | GIT_CHERRY_PICK_HELP=" After resolving the conflicts, |
| 34 | mark the corrected paths with 'git add <paths>', and |
| 35 | run 'git rebase --continue'" |
| 36 | export GIT_CHERRY_PICK_HELP |
| 37 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 38 | warn () { |
| 39 | echo "$*" >&2 |
| 40 | } |
| 41 | |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 42 | output () { |
| 43 | case "$VERBOSE" in |
| 44 | '') |
Matt Kraai | 5166810 | 2007-09-25 18:30:13 -0700 | [diff] [blame] | 45 | output=$("$@" 2>&1 ) |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 46 | status=$? |
Matt Kraai | 5166810 | 2007-09-25 18:30:13 -0700 | [diff] [blame] | 47 | test $status != 0 && printf "%s\n" "$output" |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 48 | return $status |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 49 | ;; |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 50 | *) |
| 51 | "$@" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 52 | ;; |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 53 | esac |
| 54 | } |
| 55 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 56 | require_clean_work_tree () { |
| 57 | # test if working tree is dirty |
| 58 | git rev-parse --verify HEAD > /dev/null && |
| 59 | git update-index --refresh && |
| 60 | git diff-files --quiet && |
Junio C Hamano | 38762c4 | 2007-11-28 16:15:04 -0800 | [diff] [blame] | 61 | git diff-index --cached --quiet HEAD -- || |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 62 | die "Working tree is dirty" |
| 63 | } |
| 64 | |
| 65 | ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION" |
| 66 | |
| 67 | comment_for_reflog () { |
| 68 | case "$ORIG_REFLOG_ACTION" in |
| 69 | ''|rebase*) |
| 70 | GIT_REFLOG_ACTION="rebase -i ($1)" |
| 71 | export GIT_REFLOG_ACTION |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 72 | ;; |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 73 | esac |
| 74 | } |
| 75 | |
| 76 | mark_action_done () { |
| 77 | sed -e 1q < "$TODO" >> "$DONE" |
| 78 | sed -e 1d < "$TODO" >> "$TODO".new |
| 79 | mv -f "$TODO".new "$TODO" |
Johannes Schindelin | dad4e32 | 2007-09-25 16:43:04 +0100 | [diff] [blame] | 80 | count=$(($(grep -ve '^$' -e '^#' < "$DONE" | wc -l))) |
| 81 | total=$(($count+$(grep -ve '^$' -e '^#' < "$TODO" | wc -l))) |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 82 | printf "Rebasing (%d/%d)\r" $count $total |
| 83 | test -z "$VERBOSE" || echo |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | make_patch () { |
Johannes Schindelin | be6ff20 | 2007-09-25 16:42:36 +0100 | [diff] [blame] | 87 | parent_sha1=$(git rev-parse --verify "$1"^) || |
| 88 | die "Cannot get patch for $1^" |
Johannes Schindelin | f3d5e46 | 2007-10-09 13:59:43 +0100 | [diff] [blame] | 89 | git diff-tree -p "$parent_sha1".."$1" > "$DOTEST"/patch |
Johannes Schindelin | be6ff20 | 2007-09-25 16:42:36 +0100 | [diff] [blame] | 90 | test -f "$DOTEST"/message || |
| 91 | git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message |
| 92 | test -f "$DOTEST"/author-script || |
| 93 | get_author_ident_from_commit "$1" > "$DOTEST"/author-script |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | die_with_patch () { |
| 97 | make_patch "$1" |
Johannes Schindelin | ecfe72f | 2007-11-22 11:18:10 +0000 | [diff] [blame] | 98 | git rerere |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 99 | die "$2" |
| 100 | } |
| 101 | |
Johannes Schindelin | c54b781 | 2007-06-25 18:56:55 +0100 | [diff] [blame] | 102 | die_abort () { |
| 103 | rm -rf "$DOTEST" |
| 104 | die "$1" |
| 105 | } |
| 106 | |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 107 | has_action () { |
| 108 | grep -vqe '^$' -e '^#' "$1" |
| 109 | } |
| 110 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 111 | pick_one () { |
Johannes Schindelin | 1d25c8c | 2007-08-23 09:55:41 +0100 | [diff] [blame] | 112 | no_ff= |
| 113 | case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 114 | output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 115 | test -d "$REWRITTEN" && |
| 116 | pick_one_preserving_merges "$@" && return |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 117 | parent_sha1=$(git rev-parse --verify $sha1^) || |
| 118 | die "Could not get the parent of $sha1" |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 119 | current_sha1=$(git rev-parse --verify HEAD) |
Michael W. Olson | 2858028 | 2007-10-15 13:48:27 -0400 | [diff] [blame] | 120 | if test "$no_ff$current_sha1" = "$parent_sha1"; then |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 121 | output git reset --hard $sha1 |
| 122 | test "a$1" = a-n && output git reset --soft $current_sha1 |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 123 | sha1=$(git rev-parse --short $sha1) |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 124 | output warn Fast forward to $sha1 |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 125 | else |
Björn Steinbrink | 2a9c53e | 2007-10-31 03:20:31 +0100 | [diff] [blame] | 126 | output git cherry-pick "$@" |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 127 | fi |
| 128 | } |
| 129 | |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 130 | pick_one_preserving_merges () { |
| 131 | case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac |
| 132 | sha1=$(git rev-parse $sha1) |
| 133 | |
Johannes Schindelin | 3b38ec1 | 2007-07-24 03:18:28 +0100 | [diff] [blame] | 134 | if test -f "$DOTEST"/current-commit |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 135 | then |
| 136 | current_commit=$(cat "$DOTEST"/current-commit) && |
| 137 | git rev-parse HEAD > "$REWRITTEN"/$current_commit && |
| 138 | rm "$DOTEST"/current-commit || |
| 139 | die "Cannot write current commit's replacement sha1" |
| 140 | fi |
| 141 | |
| 142 | # rewrite parents; if none were rewritten, we can fast-forward. |
| 143 | fast_forward=t |
| 144 | preserve=t |
| 145 | new_parents= |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 146 | for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-) |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 147 | do |
Johannes Schindelin | 3b38ec1 | 2007-07-24 03:18:28 +0100 | [diff] [blame] | 148 | if test -f "$REWRITTEN"/$p |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 149 | then |
| 150 | preserve=f |
| 151 | new_p=$(cat "$REWRITTEN"/$p) |
| 152 | test $p != $new_p && fast_forward=f |
| 153 | case "$new_parents" in |
| 154 | *$new_p*) |
| 155 | ;; # do nothing; that parent is already there |
| 156 | *) |
| 157 | new_parents="$new_parents $new_p" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 158 | ;; |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 159 | esac |
| 160 | fi |
| 161 | done |
| 162 | case $fast_forward in |
| 163 | t) |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 164 | output warn "Fast forward to $sha1" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 165 | test $preserve = f || echo $sha1 > "$REWRITTEN"/$sha1 |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 166 | ;; |
| 167 | f) |
| 168 | test "a$1" = a-n && die "Refusing to squash a merge: $sha1" |
| 169 | |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 170 | first_parent=$(expr "$new_parents" : ' \([^ ]*\)') |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 171 | # detach HEAD to current parent |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 172 | output git checkout $first_parent 2> /dev/null || |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 173 | die "Cannot move HEAD to $first_parent" |
| 174 | |
| 175 | echo $sha1 > "$DOTEST"/current-commit |
| 176 | case "$new_parents" in |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 177 | ' '*' '*) |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 178 | # redo merge |
| 179 | author_script=$(get_author_ident_from_commit $sha1) |
| 180 | eval "$author_script" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 181 | msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')" |
Björn Steinbrink | f91333d | 2007-10-31 03:20:32 +0100 | [diff] [blame] | 182 | # No point in merging the first parent, that's HEAD |
| 183 | new_parents=${new_parents# $first_parent} |
Johannes Schindelin | ae830ed | 2007-09-25 16:43:44 +0100 | [diff] [blame] | 184 | if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ |
| 185 | GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ |
| 186 | GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ |
| 187 | output git merge $STRATEGY -m "$msg" \ |
| 188 | $new_parents |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 189 | then |
Johannes Schindelin | ecfe72f | 2007-11-22 11:18:10 +0000 | [diff] [blame] | 190 | git rerere |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 191 | printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG |
Johannes Schindelin | 18640d9 | 2007-07-08 03:01:29 +0100 | [diff] [blame] | 192 | die Error redoing merge $sha1 |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 193 | fi |
| 194 | ;; |
| 195 | *) |
Björn Steinbrink | 2a9c53e | 2007-10-31 03:20:31 +0100 | [diff] [blame] | 196 | output git cherry-pick "$@" || |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 197 | die_with_patch $sha1 "Could not pick $sha1" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 198 | ;; |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 199 | esac |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 200 | ;; |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 201 | esac |
| 202 | } |
| 203 | |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 204 | nth_string () { |
| 205 | case "$1" in |
| 206 | *1[0-9]|*[04-9]) echo "$1"th;; |
| 207 | *1) echo "$1"st;; |
| 208 | *2) echo "$1"nd;; |
| 209 | *3) echo "$1"rd;; |
| 210 | esac |
| 211 | } |
| 212 | |
| 213 | make_squash_message () { |
Johannes Schindelin | 3b38ec1 | 2007-07-24 03:18:28 +0100 | [diff] [blame] | 214 | if test -f "$SQUASH_MSG"; then |
Junio C Hamano | c7965af | 2007-09-01 02:17:28 -0700 | [diff] [blame] | 215 | COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \ |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 216 | < "$SQUASH_MSG" | tail -n 1)+1)) |
| 217 | echo "# This is a combination of $COUNT commits." |
| 218 | sed -n "2,\$p" < "$SQUASH_MSG" |
| 219 | else |
| 220 | COUNT=2 |
| 221 | echo "# This is a combination of two commits." |
| 222 | echo "# The first commit's message is:" |
| 223 | echo |
| 224 | git cat-file commit HEAD | sed -e '1,/^$/d' |
| 225 | echo |
| 226 | fi |
| 227 | echo "# This is the $(nth_string $COUNT) commit message:" |
| 228 | echo |
| 229 | git cat-file commit $1 | sed -e '1,/^$/d' |
| 230 | } |
| 231 | |
| 232 | peek_next_command () { |
| 233 | sed -n "1s/ .*$//p" < "$TODO" |
| 234 | } |
| 235 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 236 | do_next () { |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 237 | rm -f "$DOTEST"/message "$DOTEST"/author-script \ |
| 238 | "$DOTEST"/amend || exit |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 239 | read command sha1 rest < "$TODO" |
| 240 | case "$command" in |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 241 | '#'*|'') |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 242 | mark_action_done |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 243 | ;; |
Johannes Schindelin | f8babc4 | 2007-09-29 03:32:11 +0100 | [diff] [blame] | 244 | pick|p) |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 245 | comment_for_reflog pick |
| 246 | |
| 247 | mark_action_done |
| 248 | pick_one $sha1 || |
| 249 | die_with_patch $sha1 "Could not apply $sha1... $rest" |
| 250 | ;; |
Johannes Schindelin | f8babc4 | 2007-09-29 03:32:11 +0100 | [diff] [blame] | 251 | edit|e) |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 252 | comment_for_reflog edit |
| 253 | |
| 254 | mark_action_done |
| 255 | pick_one $sha1 || |
| 256 | die_with_patch $sha1 "Could not apply $sha1... $rest" |
| 257 | make_patch $sha1 |
Johannes Schindelin | be6ff20 | 2007-09-25 16:42:36 +0100 | [diff] [blame] | 258 | : > "$DOTEST"/amend |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 259 | warn |
| 260 | warn "You can amend the commit now, with" |
| 261 | warn |
| 262 | warn " git commit --amend" |
| 263 | warn |
| 264 | exit 0 |
| 265 | ;; |
Johannes Schindelin | f8babc4 | 2007-09-29 03:32:11 +0100 | [diff] [blame] | 266 | squash|s) |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 267 | comment_for_reflog squash |
| 268 | |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 269 | has_action "$DONE" || |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 270 | die "Cannot 'squash' without a previous commit" |
| 271 | |
| 272 | mark_action_done |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 273 | make_squash_message $sha1 > "$MSG" |
| 274 | case "$(peek_next_command)" in |
Johannes Schindelin | f8babc4 | 2007-09-29 03:32:11 +0100 | [diff] [blame] | 275 | squash|s) |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 276 | EDIT_COMMIT= |
Johannes Schindelin | 91e1ee7 | 2007-07-26 07:35:51 +0100 | [diff] [blame] | 277 | USE_OUTPUT=output |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 278 | cp "$MSG" "$SQUASH_MSG" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 279 | ;; |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 280 | *) |
| 281 | EDIT_COMMIT=-e |
Johannes Schindelin | 91e1ee7 | 2007-07-26 07:35:51 +0100 | [diff] [blame] | 282 | USE_OUTPUT= |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 283 | rm -f "$SQUASH_MSG" || exit |
| 284 | ;; |
Johannes Schindelin | 6368f3f | 2007-07-21 18:09:41 +0100 | [diff] [blame] | 285 | esac |
| 286 | |
Alex Riesen | 793ad04 | 2007-07-13 00:30:35 +0200 | [diff] [blame] | 287 | failed=f |
Johannes Schindelin | 81ab1cb | 2007-09-30 00:34:23 +0100 | [diff] [blame] | 288 | author_script=$(get_author_ident_from_commit HEAD) |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 289 | output git reset --soft HEAD^ |
Johannes Schindelin | fb47cfb | 2007-07-24 21:43:09 +0100 | [diff] [blame] | 290 | pick_one -n $sha1 || failed=t |
Johannes Schindelin | 18640d9 | 2007-07-08 03:01:29 +0100 | [diff] [blame] | 291 | echo "$author_script" > "$DOTEST"/author-script |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 292 | case $failed in |
| 293 | f) |
| 294 | # This is like --amend, but with a different message |
| 295 | eval "$author_script" |
Johannes Schindelin | ae830ed | 2007-09-25 16:43:44 +0100 | [diff] [blame] | 296 | GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \ |
| 297 | GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \ |
| 298 | GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \ |
Johannes Schindelin | 91e1ee7 | 2007-07-26 07:35:51 +0100 | [diff] [blame] | 299 | $USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 300 | ;; |
| 301 | t) |
| 302 | cp "$MSG" "$GIT_DIR"/MERGE_MSG |
| 303 | warn |
| 304 | warn "Could not apply $sha1... $rest" |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 305 | die_with_patch $sha1 "" |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 306 | ;; |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 307 | esac |
| 308 | ;; |
| 309 | *) |
| 310 | warn "Unknown command: $command $sha1 $rest" |
| 311 | die_with_patch $sha1 "Please fix this in the file $TODO." |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 312 | ;; |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 313 | esac |
| 314 | test -s "$TODO" && return |
| 315 | |
Johannes Schindelin | 68a163c | 2007-06-25 18:58:28 +0100 | [diff] [blame] | 316 | comment_for_reflog finish && |
| 317 | HEADNAME=$(cat "$DOTEST"/head-name) && |
| 318 | OLDHEAD=$(cat "$DOTEST"/head) && |
| 319 | SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) && |
Johannes Schindelin | 3b38ec1 | 2007-07-24 03:18:28 +0100 | [diff] [blame] | 320 | if test -d "$REWRITTEN" |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 321 | then |
| 322 | test -f "$DOTEST"/current-commit && |
| 323 | current_commit=$(cat "$DOTEST"/current-commit) && |
| 324 | git rev-parse HEAD > "$REWRITTEN"/$current_commit |
| 325 | NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD) |
| 326 | else |
| 327 | NEWHEAD=$(git rev-parse HEAD) |
| 328 | fi && |
Johannes Schindelin | 73697a0 | 2007-09-25 16:43:15 +0100 | [diff] [blame] | 329 | case $HEADNAME in |
| 330 | refs/*) |
| 331 | message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" && |
| 332 | git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD && |
| 333 | git symbolic-ref HEAD $HEADNAME |
| 334 | ;; |
| 335 | esac && { |
Johannes Schindelin | 3df0a85 | 2007-07-08 03:02:13 +0100 | [diff] [blame] | 336 | test ! -f "$DOTEST"/verbose || |
Johannes Schindelin | f3d5e46 | 2007-10-09 13:59:43 +0100 | [diff] [blame] | 337 | git diff-tree --stat $(cat "$DOTEST"/head)..HEAD |
Johannes Schindelin | 3df0a85 | 2007-07-08 03:02:13 +0100 | [diff] [blame] | 338 | } && |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 339 | rm -rf "$DOTEST" && |
Johannes Schindelin | 73697a0 | 2007-09-25 16:43:15 +0100 | [diff] [blame] | 340 | git gc --auto && |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 341 | warn "Successfully rebased and updated $HEADNAME." |
| 342 | |
| 343 | exit |
| 344 | } |
| 345 | |
| 346 | do_rest () { |
| 347 | while : |
| 348 | do |
| 349 | do_next |
| 350 | done |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 351 | } |
| 352 | |
David Kastrup | 822f7c7 | 2007-09-23 22:42:08 +0200 | [diff] [blame] | 353 | while test $# != 0 |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 354 | do |
| 355 | case "$1" in |
| 356 | --continue) |
| 357 | comment_for_reflog continue |
| 358 | |
| 359 | test -d "$DOTEST" || die "No interactive rebase running" |
| 360 | |
Johannes Schindelin | 18640d9 | 2007-07-08 03:01:29 +0100 | [diff] [blame] | 361 | # commit if necessary |
| 362 | git rev-parse --verify HEAD > /dev/null && |
| 363 | git update-index --refresh && |
| 364 | git diff-files --quiet && |
Junio C Hamano | 38762c4 | 2007-11-28 16:15:04 -0800 | [diff] [blame] | 365 | ! git diff-index --cached --quiet HEAD -- && |
Johannes Schindelin | be6ff20 | 2007-09-25 16:42:36 +0100 | [diff] [blame] | 366 | . "$DOTEST"/author-script && { |
| 367 | test ! -f "$DOTEST"/amend || git reset --soft HEAD^ |
| 368 | } && |
Johannes Schindelin | 18640d9 | 2007-07-08 03:01:29 +0100 | [diff] [blame] | 369 | export GIT_AUTHOR_NAME GIT_AUTHOR_NAME GIT_AUTHOR_DATE && |
| 370 | git commit -F "$DOTEST"/message -e |
| 371 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 372 | require_clean_work_tree |
| 373 | do_rest |
| 374 | ;; |
| 375 | --abort) |
| 376 | comment_for_reflog abort |
| 377 | |
Johannes Schindelin | ecfe72f | 2007-11-22 11:18:10 +0000 | [diff] [blame] | 378 | git rerere clear |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 379 | test -d "$DOTEST" || die "No interactive rebase running" |
| 380 | |
| 381 | HEADNAME=$(cat "$DOTEST"/head-name) |
| 382 | HEAD=$(cat "$DOTEST"/head) |
Johannes Schindelin | 73697a0 | 2007-09-25 16:43:15 +0100 | [diff] [blame] | 383 | case $HEADNAME in |
| 384 | refs/*) |
| 385 | git symbolic-ref HEAD $HEADNAME |
| 386 | ;; |
| 387 | esac && |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 388 | output git reset --hard $HEAD && |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 389 | rm -rf "$DOTEST" |
| 390 | exit |
| 391 | ;; |
| 392 | --skip) |
| 393 | comment_for_reflog skip |
| 394 | |
Johannes Schindelin | ecfe72f | 2007-11-22 11:18:10 +0000 | [diff] [blame] | 395 | git rerere clear |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 396 | test -d "$DOTEST" || die "No interactive rebase running" |
| 397 | |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 398 | output git reset --hard && do_rest |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 399 | ;; |
| 400 | -s|--strategy) |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 401 | case "$#,$1" in |
| 402 | *,*=*) |
Ralf Wildenhues | b5e960b | 2007-11-08 22:47:36 +0100 | [diff] [blame] | 403 | STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 404 | 1,*) |
| 405 | usage ;; |
| 406 | *) |
| 407 | STRATEGY="-s $2" |
| 408 | shift ;; |
| 409 | esac |
| 410 | ;; |
| 411 | --merge) |
| 412 | # we use merge anyway |
| 413 | ;; |
| 414 | -C*) |
| 415 | die "Interactive rebase uses merge, so $1 does not make sense" |
| 416 | ;; |
Johannes Schindelin | c54b781 | 2007-06-25 18:56:55 +0100 | [diff] [blame] | 417 | -v|--verbose) |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 418 | VERBOSE=t |
| 419 | ;; |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 420 | -p|--preserve-merges) |
| 421 | PRESERVE_MERGES=t |
| 422 | ;; |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 423 | -i|--interactive) |
| 424 | # yeah, we know |
| 425 | ;; |
| 426 | ''|-h) |
| 427 | usage |
| 428 | ;; |
| 429 | *) |
| 430 | test -d "$DOTEST" && |
| 431 | die "Interactive rebase already started" |
| 432 | |
| 433 | git var GIT_COMMITTER_IDENT >/dev/null || |
| 434 | die "You need to set your committer info first" |
| 435 | |
| 436 | comment_for_reflog start |
| 437 | |
| 438 | ONTO= |
| 439 | case "$1" in |
| 440 | --onto) |
| 441 | ONTO=$(git rev-parse --verify "$2") || |
| 442 | die "Does not point to a valid commit: $2" |
| 443 | shift; shift |
| 444 | ;; |
| 445 | esac |
| 446 | |
| 447 | require_clean_work_tree |
| 448 | |
Johannes Schindelin | 3b38ec1 | 2007-07-24 03:18:28 +0100 | [diff] [blame] | 449 | if test ! -z "$2" |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 450 | then |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 451 | output git show-ref --verify --quiet "refs/heads/$2" || |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 452 | die "Invalid branchname: $2" |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 453 | output git checkout "$2" || |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 454 | die "Could not checkout $2" |
| 455 | fi |
| 456 | |
| 457 | HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" |
| 458 | UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" |
| 459 | |
Matt Kraai | 5166810 | 2007-09-25 18:30:13 -0700 | [diff] [blame] | 460 | mkdir "$DOTEST" || die "Could not create temporary $DOTEST" |
| 461 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 462 | test -z "$ONTO" && ONTO=$UPSTREAM |
| 463 | |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 464 | : > "$DOTEST"/interactive || die "Could not mark as interactive" |
Johannes Schindelin | 73697a0 | 2007-09-25 16:43:15 +0100 | [diff] [blame] | 465 | git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || |
| 466 | echo "detached HEAD" > "$DOTEST"/head-name |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 467 | |
| 468 | echo $HEAD > "$DOTEST"/head |
| 469 | echo $UPSTREAM > "$DOTEST"/upstream |
| 470 | echo $ONTO > "$DOTEST"/onto |
Johannes Schindelin | 8e4a91b | 2007-07-08 03:02:47 +0100 | [diff] [blame] | 471 | test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 472 | test t = "$VERBOSE" && : > "$DOTEST"/verbose |
Johannes Schindelin | 3b38ec1 | 2007-07-24 03:18:28 +0100 | [diff] [blame] | 473 | if test t = "$PRESERVE_MERGES" |
Johannes Schindelin | f09c9b8 | 2007-06-25 18:59:43 +0100 | [diff] [blame] | 474 | then |
| 475 | # $REWRITTEN contains files for each commit that is |
| 476 | # reachable by at least one merge base of $HEAD and |
| 477 | # $UPSTREAM. They are not necessarily rewritten, but |
| 478 | # their children might be. |
| 479 | # This ensures that commits on merged, but otherwise |
| 480 | # unrelated side branches are left alone. (Think "X" |
| 481 | # in the man page's example.) |
| 482 | mkdir "$REWRITTEN" && |
| 483 | for c in $(git merge-base --all $HEAD $UPSTREAM) |
| 484 | do |
| 485 | echo $ONTO > "$REWRITTEN"/$c || |
| 486 | die "Could not init rewritten commits" |
| 487 | done |
| 488 | MERGES_OPTION= |
| 489 | else |
| 490 | MERGES_OPTION=--no-merges |
| 491 | fi |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 492 | |
Johannes Schindelin | c54b781 | 2007-06-25 18:56:55 +0100 | [diff] [blame] | 493 | SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) |
| 494 | SHORTHEAD=$(git rev-parse --short $HEAD) |
| 495 | SHORTONTO=$(git rev-parse --short $ONTO) |
Johannes Schindelin | 6047a23 | 2007-11-22 12:30:10 +0000 | [diff] [blame] | 496 | git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ |
| 497 | --abbrev=7 --reverse --left-right --cherry-pick \ |
| 498 | $UPSTREAM...$HEAD | \ |
| 499 | sed -n "s/^>/pick /p" > "$TODO" |
| 500 | cat >> "$TODO" << EOF |
| 501 | |
| 502 | # Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 503 | # |
| 504 | # Commands: |
| 505 | # pick = use commit |
| 506 | # edit = use commit, but stop for amending |
| 507 | # squash = use commit, but meld into previous commit |
Johannes Schindelin | 82576dd | 2007-07-08 21:32:22 +0100 | [diff] [blame] | 508 | # |
| 509 | # If you remove a line here THAT COMMIT WILL BE LOST. |
Johannes Schindelin | 6047a23 | 2007-11-22 12:30:10 +0000 | [diff] [blame] | 510 | # However, if you remove everything, the rebase will be aborted. |
Johannes Schindelin | 82576dd | 2007-07-08 21:32:22 +0100 | [diff] [blame] | 511 | # |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 512 | EOF |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 513 | |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 514 | has_action "$TODO" || |
Johannes Schindelin | c54b781 | 2007-06-25 18:56:55 +0100 | [diff] [blame] | 515 | die_abort "Nothing to do" |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 516 | |
| 517 | cp "$TODO" "$TODO".backup |
Adam Roben | ef0c2ab | 2007-07-19 22:09:35 -0700 | [diff] [blame] | 518 | git_editor "$TODO" || |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 519 | die "Could not execute editor" |
| 520 | |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 521 | has_action "$TODO" || |
Johannes Schindelin | c54b781 | 2007-06-25 18:56:55 +0100 | [diff] [blame] | 522 | die_abort "Nothing to do" |
| 523 | |
Johannes Schindelin | dfa49f3 | 2007-07-23 23:45:49 +0100 | [diff] [blame] | 524 | output git checkout $ONTO && do_rest |
Johannes Schindelin | 376ccb8 | 2007-09-25 16:42:51 +0100 | [diff] [blame] | 525 | ;; |
Johannes Schindelin | 1b1dce4 | 2007-06-25 01:11:14 +0100 | [diff] [blame] | 526 | esac |
| 527 | shift |
| 528 | done |