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