]> git.ipfire.org Git - thirdparty/git.git/blame - git-applypatch.sh
GIT 0.99.9k
[thirdparty/git.git] / git-applypatch.sh
CommitLineData
853916ff
LT
1#!/bin/sh
2##
3## applypatch takes four file arguments, and uses those to
4## apply the unpacked patch (surprise surprise) that they
5## represent to the current tree.
6##
7## The arguments are:
8## $1 - file with commit message
9## $2 - file with the actual patch
a196d8d4
LT
10## $3 - "info" file with Author, email and subject
11## $4 - optional file containing signoff to add
853916ff 12##
ae2b0f15 13. git-sh-setup
4426ac70 14
ad4e9ce4
JB
15final=.dotest/final-commit
16##
17## If this file exists, we ask before applying
18##
19query_apply=.dotest/.query_apply
4426ac70
JH
20
21## We do not munge the first line of the commit message too much
22## if this file exists.
6bff6a60 23keep_subject=.dotest/.keep_subject
4426ac70 24
47f0b6d5
JH
25## We do not attempt the 3-way merge fallback unless this file exists.
26fall_back_3way=.dotest/.3way
4426ac70 27
853916ff
LT
28MSGFILE=$1
29PATCHFILE=$2
a196d8d4 30INFO=$3
4426ac70
JH
31SIGNOFF=$4
32EDIT=${VISUAL:-${EDITOR:-vi}}
ad4e9ce4 33
a567d315
JH
34export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
35export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
36export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
37export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
853916ff 38
4426ac70
JH
39if test '' != "$SIGNOFF"
40then
41 if test -f "$SIGNOFF"
42 then
43 SIGNOFF=`cat "$SIGNOFF"` || exit
44 elif case "$SIGNOFF" in yes | true | me | please) : ;; *) false ;; esac
45 then
46 SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
47 s/>.*/>/
48 s/^/Signed-off-by: /'
49 `
50 else
51 SIGNOFF=
52 fi
53 if test '' != "$SIGNOFF"
54 then
55 LAST_SIGNED_OFF_BY=`
56 sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
57 tail -n 1
58 `
a567d315
JH
59 test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
60 test '' = "$LAST_SIGNED_OFF_BY" && echo
61 echo "$SIGNOFF"
62 } >>"$MSGFILE"
4426ac70 63 fi
ad4e9ce4 64fi
4426ac70 65
6bff6a60
JH
66patch_header=
67test -f "$keep_subject" || patch_header='[PATCH] '
ad4e9ce4 68
4426ac70
JH
69{
70 echo "$patch_header$SUBJECT"
71 if test -s "$MSGFILE"
72 then
73 echo
74 cat "$MSGFILE"
75 fi
76} >"$final"
ad4e9ce4 77
4426ac70
JH
78interactive=yes
79test -f "$query_apply" || interactive=no
ad4e9ce4 80
4426ac70 81while [ "$interactive" = yes ]; do
ad4e9ce4
JB
82 echo "Commit Body is:"
83 echo "--------------------------"
4426ac70 84 cat "$final"
ad4e9ce4
JB
85 echo "--------------------------"
86 echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
87 read reply
4426ac70
JH
88 case "$reply" in
89 y|Y) interactive=no;;
ad4e9ce4 90 n|N) exit 2;; # special value to tell dotest to keep going
4426ac70
JH
91 e|E) "$EDIT" "$final";;
92 a|A) rm -f "$query_apply"
93 interactive=no ;;
ad4e9ce4
JB
94 esac
95done
96
4426ac70
JH
97if test -x "$GIT_DIR"/hooks/applypatch-msg
98then
99 "$GIT_DIR"/hooks/applypatch-msg "$final" || exit
100fi
101
853916ff 102echo
61096819 103echo Applying "'$SUBJECT'"
853916ff
LT
104echo
105
a567d315 106git-apply --index "$PATCHFILE" || {
47f0b6d5
JH
107
108 # git-apply exits with status 1 when the patch does not apply,
109 # but it die()s with other failures, most notably upon corrupt
110 # patch. In the latter case, there is no point to try applying
111 # it to another tree and do 3-way merge.
112 test $? = 1 || exit 1
113
114 test -f "$fall_back_3way" || exit 1
115
a567d315
JH
116 # Here if we know which revision the patch applies to,
117 # we create a temporary working tree and index, apply the
118 # patch, and attempt 3-way merge with the resulting tree.
47f0b6d5
JH
119
120 O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
121 rm -fr .patch-merge-*
122
123 (
124 N=10
125
126 # if the patch records the base tree...
127 sed -ne '
128 /^diff /q
129 /^applies-to: \([0-9a-f]*\)$/{
130 s//\1/p
131 q
132 }
133 ' "$PATCHFILE"
134
135 # or hoping the patch is against our recent commits...
136 git-rev-list --max-count=$N HEAD
137
138 # or hoping the patch is against known tags...
139 git-ls-remote --tags .
140 ) |
141 while read base junk
142 do
143 # Try it if we have it as a tree.
144 git-cat-file tree "$base" >/dev/null 2>&1 || continue
145
146 rm -fr .patch-merge-tmp-* &&
147 mkdir .patch-merge-tmp-dir || break
148 (
149 cd .patch-merge-tmp-dir &&
150 GIT_INDEX_FILE=../.patch-merge-tmp-index &&
151 GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
152 export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
153 git-read-tree "$base" &&
154 git-apply --index &&
155 mv ../.patch-merge-tmp-index ../.patch-merge-index &&
156 echo "$base" >../.patch-merge-base
157 ) <"$PATCHFILE" 2>/dev/null && break
158 done
159
160 test -f .patch-merge-index &&
161 his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
162 orig_tree=$(cat .patch-merge-base) &&
163 rm -fr .patch-merge-* || exit 1
164
165 echo Falling back to patching base and 3-way merge using $orig_tree...
166
167 # This is not so wrong. Depending on which base we picked,
168 # orig_tree may be wildly different from ours, but his_tree
169 # has the same set of wildly different changes in parts the
170 # patch did not touch, so resolve ends up cancelling them,
171 # saying that we reverted all those changes.
172
173 if git-merge-resolve $orig_tree -- HEAD $his_tree
174 then
175 echo Done.
176 else
177 echo Failed to merge in the changes.
178 exit 1
179 fi
a567d315 180}
4426ac70
JH
181
182if test -x "$GIT_DIR"/hooks/pre-applypatch
183then
184 "$GIT_DIR"/hooks/pre-applypatch || exit
185fi
186
50eb31d1 187tree=$(git-write-tree) || exit 1
853916ff 188echo Wrote tree $tree
bf7960eb
JH
189parent=$(git-rev-parse --verify HEAD) &&
190commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
853916ff 191echo Committed: $commit
bf7960eb 192git-update-ref HEAD $commit $parent || exit
4426ac70
JH
193
194if test -x "$GIT_DIR"/hooks/post-applypatch
195then
196 "$GIT_DIR"/hooks/post-applypatch
197fi