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