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