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= ws= 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--whitespace=*) 137 ws=$1;shift;; 138 139--) 140shift;break;; 141-*) 142 usage ;; 143*) 144break;; 145esac 146done 147 148# If the dotest directory exists, but we have finished applying all the 149# patches in them, clear it out. 150iftest -d"$dotest"&& 151 last=$(cat "$dotest/last")&& 152 next=$(cat "$dotest/next")&& 153test$#!=0&& 154test"$next"-gt"$last" 155then 156rm-fr"$dotest" 157fi 158 159iftest -d"$dotest" 160then 161test",$#,"=",0,"|| 162 die "previous dotest directory$doteststill exists but mbox given." 163 resume=yes 164else 165# Make sure we are not given --skip nor --resolved 166test",$skip,$resolved,"= ,,, || 167 die "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 $this 377fi 378 apply_status=0 379;; 380esac 381 382iftest$apply_status=1&&test"$threeway"= t 383then 384if(fall_back_3way) 385then 386# Applying the patch to an earlier tree and merging the 387# result may have produced the same tree as ours. 388 changed="$(git-diff-index --cached --name-only HEAD)" 389iftest''="$changed" 390then 391echo No changes -- Patch already applied. 392 go_next 393continue 394fi 395# clear apply_status -- we have successfully merged. 396 apply_status=0 397fi 398fi 399iftest$apply_status!=0 400then 401echo Patch failed at$msgnum. 402 stop_here $this 403fi 404 405iftest -x"$GIT_DIR"/hooks/pre-applypatch 406then 407"$GIT_DIR"/hooks/pre-applypatch|| stop_here $this 408fi 409 410 tree=$(git-write-tree)&& 411echo Wrote tree $tree&& 412 parent=$(git-rev-parse --verify HEAD)&& 413 commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit")&& 414echo Committed:$commit&& 415 git-update-ref HEAD $commit $parent|| 416 stop_here $this 417 418iftest -x"$GIT_DIR"/hooks/post-applypatch 419then 420"$GIT_DIR"/hooks/post-applypatch 421fi 422 423 go_next 424done 425 426rm-fr"$dotest"