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