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