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