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