1#!/bin/sh 2# 3# 4. git-sh-setup|| die "Not a git archive" 5 6usage () { 7echo>&2"usage:$0[--signoff] [--dotest=<dir>] [--utf8] [--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 --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 --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= 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-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# cat does the right thing for us, including '-' to mean 165# standard input. 166cat"$@"| 167 git-mailsplit -d$prec"$dotest/">"$dotest/last"|| { 168rm-fr"$dotest" 169exit1 170} 171 172# -s, -u and -k flags are kept for the resuming session after 173# a patch failure. 174# -3 and -i can and must be given when resuming. 175echo"$sign">"$dotest/sign" 176echo"$utf8">"$dotest/utf8" 177echo"$keep">"$dotest/keep" 178echo1>"$dotest/next" 179fi 180 181case"$resolved"in 182'') 183 files=$(git-diff-index --cached --name-only HEAD)||exit 184if["$files"];then 185echo"Dirty index: cannot apply patches (dirty:$files)">&2 186exit1 187fi 188esac 189 190iftest"$(cat "$dotest/utf8")"= t 191then 192 utf8=-u 193fi 194iftest"$(cat "$dotest/keep")"= t 195then 196 keep=-k 197fi 198iftest"$(cat "$dotest/sign")"= t 199then 200 SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e ' 201 s/>.*/>/ 202 s/^/Signed-off-by: /' 203 ` 204else 205 SIGNOFF= 206fi 207 208last=`cat "$dotest/last"` 209this=`cat "$dotest/next"` 210iftest"$skip"= t 211then 212 this=`expr "$this" + 1` 213fi 214 215iftest"$this"-gt"$last" 216then 217echo Nothing to do. 218rm-fr"$dotest" 219exit 220fi 221 222whiletest"$this"-le"$last" 223do 224 msgnum=`printf "%0${prec}d"$this` 225 next=`expr "$this" + 1` 226test -f"$dotest/$msgnum"|| { 227 go_next 228continue 229} 230 231# If we are not resuming, parse and extract the patch information 232# into separate files: 233# - info records the authorship and title 234# - msg is the rest of commit log message 235# - patch is the patch body. 236# 237# When we are resuming, these files are either already prepared 238# by the user, or the user can tell us to do so by --resolved flag. 239case"$resume"in 240'') 241 git-mailinfo$keep $utf8"$dotest/msg""$dotest/patch" \ 242<"$dotest/$msgnum">"$dotest/info"|| 243 stop_here $this 244 git-stripspace<"$dotest/msg">"$dotest/msg-clean" 245;; 246esac 247 248 GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")" 249 GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")" 250 GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")" 251export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE 252 253 SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")" 254case"$keep_subject"in-k) SUBJECT="[PATCH]$SUBJECT";;esac 255 256case"$resume"in 257'') 258iftest''!="$SIGNOFF" 259then 260 LAST_SIGNED_OFF_BY=` 261 sed -ne '/^Signed-off-by: /p' \ 262 "$dotest/msg-clean" | 263 tail -n 1 264 ` 265 ADD_SIGNOFF=` 266 test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || { 267 test '' = "$LAST_SIGNED_OFF_BY" && echo 268 echo "$SIGNOFF" 269 }` 270else 271 ADD_SIGNOFF= 272fi 273{ 274echo"$SUBJECT" 275iftest -s"$dotest/msg-clean" 276then 277echo 278cat"$dotest/msg-clean" 279fi 280iftest''!="$ADD_SIGNOFF" 281then 282echo"$ADD_SIGNOFF" 283fi 284} >"$dotest/final-commit" 285;; 286*) 287case"$resolved,$interactive"in 288 tt) 289# This is used only for interactive view option. 290 git-diff-index -p --cached HEAD >"$dotest/patch" 291;; 292esac 293esac 294 295 resume= 296iftest"$interactive"= t 297then 298test -t0|| 299 die "cannot be interactive without stdin connected to a terminal." 300 action=again 301whiletest"$action"= again 302do 303echo"Commit Body is:" 304echo"--------------------------" 305cat"$dotest/final-commit" 306echo"--------------------------" 307echo -n"Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " 308read reply 309case"$reply"in 310[yY]*) action=yes;; 311[aA]*) action=yes interactive= ;; 312[nN]*) action=skip ;; 313[eE]*)"${VISUAL:-${EDITOR:-vi}}""$dotest/final-commit" 314 action=again ;; 315[vV]*) action=again 316 LESS=-S${PAGER:-less}"$dotest/patch";; 317*) action=again ;; 318esac 319done 320else 321 action=yes 322fi 323 324iftest$action= skip 325then 326 go_next 327continue 328fi 329 330iftest -x"$GIT_DIR"/hooks/applypatch-msg 331then 332"$GIT_DIR"/hooks/applypatch-msg"$dotest/final-commit"|| 333 stop_here $this 334fi 335 336echo 337echo"Applying '$SUBJECT'" 338echo 339 340case"$resolved"in 341'') 342 git-apply --index"$dotest/patch" 343 apply_status=$? 344;; 345 t) 346# Resolved means the user did all the hard work, and 347# we do not have to do any patch application. Just 348# trust what the user has in the index file and the 349# working tree. 350 resolved= 351 apply_status=0 352;; 353esac 354 355iftest$apply_status=1&&test"$threeway"= t 356then 357if(fall_back_3way) 358then 359# Applying the patch to an earlier tree and merging the 360# result may have produced the same tree as ours. 361 changed="$(git-diff-index --cached --name-only -z HEAD)" 362iftest''="$changed" 363then 364echo No changes -- Patch already applied. 365 go_next 366continue 367fi 368# clear apply_status -- we have successfully merged. 369 apply_status=0 370fi 371fi 372iftest$apply_status!=0 373then 374echo Patch failed at$msgnum. 375 stop_here $this 376fi 377 378iftest -x"$GIT_DIR"/hooks/pre-applypatch 379then 380"$GIT_DIR"/hooks/pre-applypatch|| stop_here $this 381fi 382 383 tree=$(git-write-tree)&& 384echo Wrote tree $tree&& 385 parent=$(git-rev-parse --verify HEAD)&& 386 commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit")&& 387echo Committed:$commit&& 388 git-update-ref HEAD $commit $parent|| 389 stop_here $this 390 391iftest -x"$GIT_DIR"/hooks/post-applypatch 392then 393"$GIT_DIR"/hooks/post-applypatch 394fi 395 396 go_next 397done 398 399rm-fr"$dotest"