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