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