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>] [-CNUM] <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= resolvemsg= 110git_apply_opt= 111 112while case"$#"in0)break;;esac 113do 114case"$1"in 115-d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*) 116 dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`;shift;; 117-d|--d|--do|--dot|--dote|--dotes|--dotest) 118case"$#"in1) usage ;;esac;shift 119 dotest="$1";shift;; 120 121-i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\ 122--interacti|--interactiv|--interactive) 123 interactive=t;shift;; 124 125-b|--b|--bi|--bin|--bina|--binar|--binary) 126 binary=t;shift;; 127 128-3|--3|--3w|--3wa|--3way) 129 threeway=t;shift;; 130-s|--s|--si|--sig|--sign|--signo|--signof|--signoff) 131 sign=t;shift;; 132-u|--u|--ut|--utf|--utf8) 133 utf8=t;shift;;# this is now default 134--no-u|--no-ut|--no-utf|--no-utf8) 135 utf8=;shift;; 136-k|--k|--ke|--kee|--keep) 137 keep=t;shift;; 138 139-r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved) 140 resolved=t;shift;; 141 142--sk|--ski|--skip) 143 skip=t;shift;; 144 145--whitespace=*) 146 git_apply_opt="$git_apply_opt$1";shift;; 147 148-C*) 149 git_apply_opt="$git_apply_opt$1";shift;; 150 151--resolvemsg=*) 152 resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//");shift;; 153 154--) 155shift;break;; 156-*) 157 usage ;; 158*) 159break;; 160esac 161done 162 163# If the dotest directory exists, but we have finished applying all the 164# patches in them, clear it out. 165iftest -d"$dotest"&& 166 last=$(cat "$dotest/last")&& 167 next=$(cat "$dotest/next")&& 168test$#!=0&& 169test"$next"-gt"$last" 170then 171rm-fr"$dotest" 172fi 173 174iftest -d"$dotest" 175then 176case"$#,$skip$resolved"in 1770,*t*) 178# Explicit resume command and we do not have file, so 179# we are happy. 180: ;; 1810,) 182# No file input but without resume parameters; catch 183# user error to feed us a patch from standard input 184# when there is already .dotest. This is somewhat 185# unreliable -- stdin could be /dev/null for example 186# and the caller did not intend to feed us a patch but 187# wanted to continue unattended. 188 tty -s 189;; 190*) 191 false 192;; 193esac|| 194 die "previous dotest directory$doteststill exists but mbox given." 195 resume=yes 196else 197# Make sure we are not given --skip nor --resolved 198test",$skip,$resolved,"= ,,, || 199 die "Resolve operation not in progress, we are not resuming." 200 201# Start afresh. 202mkdir-p"$dotest"||exit 203 204 git-mailsplit -d"$prec"-o"$dotest"-b --"$@">"$dotest/last"|| { 205rm-fr"$dotest" 206exit1 207} 208 209# -b, -s, -u, -k and --whitespace flags are kept for the 210# resuming session after a patch failure. 211# -3 and -i can and must be given when resuming. 212echo"$binary">"$dotest/binary" 213echo"$ws">"$dotest/whitespace" 214echo"$sign">"$dotest/sign" 215echo"$utf8">"$dotest/utf8" 216echo"$keep">"$dotest/keep" 217echo1>"$dotest/next" 218fi 219 220case"$resolved"in 221'') 222 files=$(git-diff-index --cached --name-only HEAD)||exit 223if["$files"];then 224echo"Dirty index: cannot apply patches (dirty:$files)">&2 225exit1 226fi 227esac 228 229iftest"$(cat "$dotest/binary")"= t 230then 231 binary=--allow-binary-replacement 232fi 233iftest"$(cat "$dotest/utf8")"= t 234then 235 utf8=-u 236else 237 utf8=-n 238fi 239iftest"$(cat "$dotest/keep")"= t 240then 241 keep=-k 242fi 243ws=`cat "$dotest/whitespace"` 244iftest"$(cat "$dotest/sign")"= t 245then 246 SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e ' 247 s/>.*/>/ 248 s/^/Signed-off-by: /' 249 ` 250else 251 SIGNOFF= 252fi 253 254last=`cat "$dotest/last"` 255this=`cat "$dotest/next"` 256iftest"$skip"= t 257then 258iftest -d"$GIT_DIR/rr-cache" 259then 260 git-rerereclear 261fi 262 this=`expr "$this" + 1` 263 resume= 264fi 265 266iftest"$this"-gt"$last" 267then 268echo Nothing to do. 269rm-fr"$dotest" 270exit 271fi 272 273whiletest"$this"-le"$last" 274do 275 msgnum=`printf "%0${prec}d"$this` 276 next=`expr "$this" + 1` 277test -f"$dotest/$msgnum"|| { 278 resume= 279 go_next 280continue 281} 282 283# If we are not resuming, parse and extract the patch information 284# into separate files: 285# - info records the authorship and title 286# - msg is the rest of commit log message 287# - patch is the patch body. 288# 289# When we are resuming, these files are either already prepared 290# by the user, or the user can tell us to do so by --resolved flag. 291case"$resume"in 292'') 293 git-mailinfo$keep $utf8"$dotest/msg""$dotest/patch" \ 294<"$dotest/$msgnum">"$dotest/info"|| 295 stop_here $this 296 git-stripspace<"$dotest/msg">"$dotest/msg-clean" 297;; 298esac 299 300 GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")" 301 GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")" 302 GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")" 303 304iftest -z"$GIT_AUTHOR_EMAIL" 305then 306echo"Patch does not have a valid e-mail address." 307 stop_here $this 308fi 309 310export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE 311 312 SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")" 313case"$keep_subject"in-k) SUBJECT="[PATCH]$SUBJECT";;esac 314 315case"$resume"in 316'') 317iftest''!="$SIGNOFF" 318then 319 LAST_SIGNED_OFF_BY=` 320 sed -ne '/^Signed-off-by: /p' \ 321 "$dotest/msg-clean" | 322 tail -n 1 323 ` 324 ADD_SIGNOFF=` 325 test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || { 326 test '' = "$LAST_SIGNED_OFF_BY" && echo 327 echo "$SIGNOFF" 328 }` 329else 330 ADD_SIGNOFF= 331fi 332{ 333echo"$SUBJECT" 334iftest -s"$dotest/msg-clean" 335then 336echo 337cat"$dotest/msg-clean" 338fi 339iftest''!="$ADD_SIGNOFF" 340then 341echo"$ADD_SIGNOFF" 342fi 343} >"$dotest/final-commit" 344;; 345*) 346case"$resolved$interactive"in 347 tt) 348# This is used only for interactive view option. 349 git-diff-index -p --cached HEAD >"$dotest/patch" 350;; 351esac 352esac 353 354 resume= 355iftest"$interactive"= t 356then 357test -t0|| 358 die "cannot be interactive without stdin connected to a terminal." 359 action=again 360whiletest"$action"= again 361do 362echo"Commit Body is:" 363echo"--------------------------" 364cat"$dotest/final-commit" 365echo"--------------------------" 366printf"Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " 367read reply 368case"$reply"in 369[yY]*) action=yes;; 370[aA]*) action=yes interactive= ;; 371[nN]*) action=skip ;; 372[eE]*)"${VISUAL:-${EDITOR:-vi}}""$dotest/final-commit" 373 action=again ;; 374[vV]*) action=again 375 LESS=-S${PAGER:-less}"$dotest/patch";; 376*) action=again ;; 377esac 378done 379else 380 action=yes 381fi 382 383iftest$action= skip 384then 385 go_next 386continue 387fi 388 389iftest -x"$GIT_DIR"/hooks/applypatch-msg 390then 391"$GIT_DIR"/hooks/applypatch-msg"$dotest/final-commit"|| 392 stop_here $this 393fi 394 395echo 396echo"Applying '$SUBJECT'" 397echo 398 399case"$resolved"in 400'') 401 git-apply$git_apply_opt $binary--index"$dotest/patch" 402 apply_status=$? 403;; 404 t) 405# Resolved means the user did all the hard work, and 406# we do not have to do any patch application. Just 407# trust what the user has in the index file and the 408# working tree. 409 resolved= 410 changed="$(git-diff-index --cached --name-only HEAD)" 411iftest''="$changed" 412then 413echo"No changes - did you forget to use 'git add'?" 414 stop_here_user_resolve $this 415fi 416 unmerged=$(git-ls-files -u) 417iftest -n"$unmerged" 418then 419echo"You still have unmerged paths in your index" 420echo"did you forget to use 'git add'?" 421 stop_here_user_resolve $this 422fi 423 apply_status=0 424iftest -d"$GIT_DIR/rr-cache" 425then 426 git rerere 427fi 428;; 429esac 430 431iftest$apply_status=1&&test"$threeway"= t 432then 433if(fall_back_3way) 434then 435# Applying the patch to an earlier tree and merging the 436# result may have produced the same tree as ours. 437 changed="$(git-diff-index --cached --name-only HEAD)" 438iftest''="$changed" 439then 440echo No changes -- Patch already applied. 441 go_next 442continue 443fi 444# clear apply_status -- we have successfully merged. 445 apply_status=0 446fi 447fi 448iftest$apply_status!=0 449then 450echo Patch failed at$msgnum. 451 stop_here_user_resolve $this 452fi 453 454iftest -x"$GIT_DIR"/hooks/pre-applypatch 455then 456"$GIT_DIR"/hooks/pre-applypatch|| stop_here $this 457fi 458 459 tree=$(git-write-tree)&& 460echo Wrote tree $tree&& 461 parent=$(git-rev-parse --verify HEAD)&& 462 commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit")&& 463echo Committed:$commit&& 464 git-update-ref -m"$GIT_REFLOG_ACTION:$SUBJECT" HEAD $commit $parent|| 465 stop_here $this 466 467iftest -x"$GIT_DIR"/hooks/post-applypatch 468then 469"$GIT_DIR"/hooks/post-applypatch 470fi 471 472 go_next 473done 474 475rm-fr"$dotest"