1#!/bin/sh 2# 3# Copyright (c) 2005, 2006 Junio C Hamano 4 5USAGE='[--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way] 6 [--interactive] [--whitespace=<option>] <mbox>... 7 or, when resuming [--skip | --resolved]' 8. git-sh-setup 9set_reflog_action am 10require_work_tree 11 12git var GIT_COMMITTER_IDENT >/dev/null ||exit 13 14stop_here () { 15echo"$1">"$dotest/next" 16exit1 17} 18 19stop_here_user_resolve () { 20if[-n"$resolvemsg"];then 21echo"$resolvemsg" 22 stop_here $1 23fi 24 cmdline=$(basename $0) 25iftest''!="$interactive" 26then 27 cmdline="$cmdline-i" 28fi 29iftest''!="$threeway" 30then 31 cmdline="$cmdline-3" 32fi 33iftest'.dotest'!="$dotest" 34then 35 cmdline="$cmdline-d=$dotest" 36fi 37echo"When you have resolved this problem run\"$cmdline--resolved\"." 38echo"If you would prefer to skip this patch, instead run\"$cmdline--skip\"." 39 40 stop_here $1 41} 42 43go_next () { 44rm-f"$dotest/$msgnum""$dotest/msg""$dotest/msg-clean" \ 45"$dotest/patch""$dotest/info" 46echo"$next">"$dotest/next" 47 this=$next 48} 49 50cannot_fallback () { 51echo"$1" 52echo"Cannot fall back to three-way merge." 53exit1 54} 55 56fall_back_3way () { 57 O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd` 58 59rm-fr"$dotest"/patch-merge-* 60mkdir"$dotest/patch-merge-tmp-dir" 61 62# First see if the patch records the index info that we can use. 63 git-apply -z --index-info"$dotest/patch" \ 64>"$dotest/patch-merge-index-info"&& 65 GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ 66 git-update-index -z --index-info<"$dotest/patch-merge-index-info"&& 67 GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ 68 git-write-tree>"$dotest/patch-merge-base+"|| 69 cannot_fallback "Patch does not record usable index information." 70 71echo Using index info to reconstruct a base tree... 72if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \ 73 git-apply$binary--cached<"$dotest/patch" 74then 75mv"$dotest/patch-merge-base+""$dotest/patch-merge-base" 76mv"$dotest/patch-merge-tmp-index""$dotest/patch-merge-index" 77else 78 cannot_fallback "Did you hand edit your patch? 79It does not apply to blobs recorded in its index." 80fi 81 82test -f"$dotest/patch-merge-index"&& 83 his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree)&& 84 orig_tree=$(cat "$dotest/patch-merge-base")&& 85rm-fr"$dotest"/patch-merge-* ||exit1 86 87echo Falling back to patching base and 3-way merge... 88 89# This is not so wrong. Depending on which base we picked, 90# orig_tree may be wildly different from ours, but his_tree 91# has the same set of wildly different changes in parts the 92# patch did not touch, so recursive ends up canceling them, 93# saying that we reverted all those changes. 94 95eval GITHEAD_$his_tree='"$SUBJECT"' 96export GITHEAD_$his_tree 97 git-merge-recursive$orig_tree-- HEAD $his_tree|| { 98iftest -d"$GIT_DIR/rr-cache" 99then 100 git-rerere 101fi 102echo Failed to merge in the changes. 103exit1 104} 105unset GITHEAD_$his_tree 106} 107 108prec=4 109dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= ws= resolvemsg= 110 111while case"$#"in0)break;;esac 112do 113case"$1"in 114-d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*) 115 dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`;shift;; 116-d|--d|--do|--dot|--dote|--dotes|--dotest) 117case"$#"in1) usage ;;esac;shift 118 dotest="$1";shift;; 119 120-i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\ 121--interacti|--interactiv|--interactive) 122 interactive=t;shift;; 123 124-b|--b|--bi|--bin|--bina|--binar|--binary) 125 binary=t;shift;; 126 127-3|--3|--3w|--3wa|--3way) 128 threeway=t;shift;; 129-s|--s|--si|--sig|--sign|--signo|--signof|--signoff) 130 sign=t;shift;; 131-u|--u|--ut|--utf|--utf8) 132 utf8=t;shift;;# this is now default 133--no-u|--no-ut|--no-utf|--no-utf8) 134 utf8=;shift;; 135-k|--k|--ke|--kee|--keep) 136 keep=t;shift;; 137 138-r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved) 139 resolved=t;shift;; 140 141--sk|--ski|--skip) 142 skip=t;shift;; 143 144--whitespace=*) 145 ws=$1;shift;; 146 147--resolvemsg=*) 148 resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//");shift;; 149 150--) 151shift;break;; 152-*) 153 usage ;; 154*) 155break;; 156esac 157done 158 159# If the dotest directory exists, but we have finished applying all the 160# patches in them, clear it out. 161iftest -d"$dotest"&& 162 last=$(cat "$dotest/last")&& 163 next=$(cat "$dotest/next")&& 164test$#!=0&& 165test"$next"-gt"$last" 166then 167rm-fr"$dotest" 168fi 169 170iftest -d"$dotest" 171then 172case"$#,$skip$resolved"in 1730,*t*) 174# Explicit resume command and we do not have file, so 175# we are happy. 176: ;; 1770,) 178# No file input but without resume parameters; catch 179# user error to feed us a patch from standard input 180# when there is already .dotest. This is somewhat 181# unreliable -- stdin could be /dev/null for example 182# and the caller did not intend to feed us a patch but 183# wanted to continue unattended. 184 tty -s 185;; 186*) 187 false 188;; 189esac|| 190 die "previous dotest directory$doteststill exists but mbox given." 191 resume=yes 192else 193# Make sure we are not given --skip nor --resolved 194test",$skip,$resolved,"= ,,, || 195 die "Resolve operation not in progress, we are not resuming." 196 197# Start afresh. 198mkdir-p"$dotest"||exit 199 200 git-mailsplit -d"$prec"-o"$dotest"-b --"$@">"$dotest/last"|| { 201rm-fr"$dotest" 202exit1 203} 204 205# -b, -s, -u, -k and --whitespace flags are kept for the 206# resuming session after a patch failure. 207# -3 and -i can and must be given when resuming. 208echo"$binary">"$dotest/binary" 209echo"$ws">"$dotest/whitespace" 210echo"$sign">"$dotest/sign" 211echo"$utf8">"$dotest/utf8" 212echo"$keep">"$dotest/keep" 213echo1>"$dotest/next" 214fi 215 216case"$resolved"in 217'') 218 files=$(git-diff-index --cached --name-only HEAD)||exit 219if["$files"];then 220echo"Dirty index: cannot apply patches (dirty:$files)">&2 221exit1 222fi 223esac 224 225iftest"$(cat "$dotest/binary")"= t 226then 227 binary=--allow-binary-replacement 228fi 229iftest"$(cat "$dotest/utf8")"= t 230then 231 utf8=-u 232else 233 utf8=-n 234fi 235iftest"$(cat "$dotest/keep")"= t 236then 237 keep=-k 238fi 239ws=`cat "$dotest/whitespace"` 240iftest"$(cat "$dotest/sign")"= t 241then 242 SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e ' 243 s/>.*/>/ 244 s/^/Signed-off-by: /' 245 ` 246else 247 SIGNOFF= 248fi 249 250last=`cat "$dotest/last"` 251this=`cat "$dotest/next"` 252iftest"$skip"= t 253then 254iftest -d"$GIT_DIR/rr-cache" 255then 256 git-rerereclear 257fi 258 this=`expr "$this" + 1` 259 resume= 260fi 261 262iftest"$this"-gt"$last" 263then 264echo Nothing to do. 265rm-fr"$dotest" 266exit 267fi 268 269whiletest"$this"-le"$last" 270do 271 msgnum=`printf "%0${prec}d"$this` 272 next=`expr "$this" + 1` 273test -f"$dotest/$msgnum"|| { 274 resume= 275 go_next 276continue 277} 278 279# If we are not resuming, parse and extract the patch information 280# into separate files: 281# - info records the authorship and title 282# - msg is the rest of commit log message 283# - patch is the patch body. 284# 285# When we are resuming, these files are either already prepared 286# by the user, or the user can tell us to do so by --resolved flag. 287case"$resume"in 288'') 289 git-mailinfo$keep $utf8"$dotest/msg""$dotest/patch" \ 290<"$dotest/$msgnum">"$dotest/info"|| 291 stop_here $this 292 git-stripspace<"$dotest/msg">"$dotest/msg-clean" 293;; 294esac 295 296 GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")" 297 GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")" 298 GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")" 299 300iftest -z"$GIT_AUTHOR_EMAIL" 301then 302echo"Patch does not have a valid e-mail address." 303 stop_here $this 304fi 305 306export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE 307 308 SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")" 309case"$keep_subject"in-k) SUBJECT="[PATCH]$SUBJECT";;esac 310 311case"$resume"in 312'') 313iftest''!="$SIGNOFF" 314then 315 LAST_SIGNED_OFF_BY=` 316 sed -ne '/^Signed-off-by: /p' \ 317 "$dotest/msg-clean" | 318 tail -n 1 319 ` 320 ADD_SIGNOFF=` 321 test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || { 322 test '' = "$LAST_SIGNED_OFF_BY" && echo 323 echo "$SIGNOFF" 324 }` 325else 326 ADD_SIGNOFF= 327fi 328{ 329echo"$SUBJECT" 330iftest -s"$dotest/msg-clean" 331then 332echo 333cat"$dotest/msg-clean" 334fi 335iftest''!="$ADD_SIGNOFF" 336then 337echo"$ADD_SIGNOFF" 338fi 339} >"$dotest/final-commit" 340;; 341*) 342case"$resolved$interactive"in 343 tt) 344# This is used only for interactive view option. 345 git-diff-index -p --cached HEAD >"$dotest/patch" 346;; 347esac 348esac 349 350 resume= 351iftest"$interactive"= t 352then 353test -t0|| 354 die "cannot be interactive without stdin connected to a terminal." 355 action=again 356whiletest"$action"= again 357do 358echo"Commit Body is:" 359echo"--------------------------" 360cat"$dotest/final-commit" 361echo"--------------------------" 362printf"Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " 363read reply 364case"$reply"in 365[yY]*) action=yes;; 366[aA]*) action=yes interactive= ;; 367[nN]*) action=skip ;; 368[eE]*)"${VISUAL:-${EDITOR:-vi}}""$dotest/final-commit" 369 action=again ;; 370[vV]*) action=again 371 LESS=-S${PAGER:-less}"$dotest/patch";; 372*) action=again ;; 373esac 374done 375else 376 action=yes 377fi 378 379iftest$action= skip 380then 381 go_next 382continue 383fi 384 385iftest -x"$GIT_DIR"/hooks/applypatch-msg 386then 387"$GIT_DIR"/hooks/applypatch-msg"$dotest/final-commit"|| 388 stop_here $this 389fi 390 391echo 392echo"Applying '$SUBJECT'" 393echo 394 395case"$resolved"in 396'') 397 git-apply$binary--index$ws"$dotest/patch" 398 apply_status=$? 399;; 400 t) 401# Resolved means the user did all the hard work, and 402# we do not have to do any patch application. Just 403# trust what the user has in the index file and the 404# working tree. 405 resolved= 406 changed="$(git-diff-index --cached --name-only HEAD)" 407iftest''="$changed" 408then 409echo"No changes - did you forget to use 'git add'?" 410 stop_here_user_resolve $this 411fi 412 unmerged=$(git-ls-files -u) 413iftest -n"$unmerged" 414then 415echo"You still have unmerged paths in your index" 416echo"did you forget to use 'git add'?" 417 stop_here_user_resolve $this 418fi 419 apply_status=0 420iftest -d"$GIT_DIR/rr-cache" 421then 422 git rerere 423fi 424;; 425esac 426 427iftest$apply_status=1&&test"$threeway"= t 428then 429if(fall_back_3way) 430then 431# Applying the patch to an earlier tree and merging the 432# result may have produced the same tree as ours. 433 changed="$(git-diff-index --cached --name-only HEAD)" 434iftest''="$changed" 435then 436echo No changes -- Patch already applied. 437 go_next 438continue 439fi 440# clear apply_status -- we have successfully merged. 441 apply_status=0 442fi 443fi 444iftest$apply_status!=0 445then 446echo Patch failed at$msgnum. 447 stop_here_user_resolve $this 448fi 449 450iftest -x"$GIT_DIR"/hooks/pre-applypatch 451then 452"$GIT_DIR"/hooks/pre-applypatch|| stop_here $this 453fi 454 455 tree=$(git-write-tree)&& 456echo Wrote tree $tree&& 457 parent=$(git-rev-parse --verify HEAD)&& 458 commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit")&& 459echo Committed:$commit&& 460 git-update-ref -m"$GIT_REFLOG_ACTION:$SUBJECT" HEAD $commit $parent|| 461 stop_here $this 462 463iftest -x"$GIT_DIR"/hooks/post-applypatch 464then 465"$GIT_DIR"/hooks/post-applypatch 466fi 467 468 go_next 469done 470 471rm-fr"$dotest"