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