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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
#!/bin/bash
#
# (c) 2012 - 2016 PrydeWorX
# Sven Eden, PrydeWorX - Bardowick, Germany
# yamakuzure@users.sourceforge.net
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# History and ChangePWX_ERR_LOG:
# Version Date Maintainer Change(s)
# 0.0.1 2012-12-29 sed, PrydeWorX First Design.
# 0.0.2 2013-01-08 sed, PrydeWorX First working version.
# 0.1.0 2013-01-10 sed, PrydeWorX Initial private release.
# 0.1.1 2013-06-01 sed, PrydeWorX Use functions from pwx_git_funcs.sh.
# 0.2.0 2014-09-02 sed, PrydeWorX Use GNU enhanced getopt for command
# line parsing.
# 0.3.0 2016-11-18 sed, PrydeWorX Added option -T|--theirs to force
# merge errors to be resolved by
# throwing away all local changes.
# 0.3.1 2017-03-22 sed, PrydeWorX Show full part of a patch that is to be
# deleted after manually fixing merge
# conflicts.
# Show tried command if we fail with
# full rejects.
# 0.4.0 2017-04-24 sed, PrydeWorX Remove some remote-is-right-automatisms
# 0.5.0 2017-07-04 sed, PrydeWorX If normal processing fails, use
# check_tree.pl to generate diffs for the
# specific commit, and apply them after
# letting the user have a look.
# Common functions
PROGDIR="$(readlink -f $(dirname $0))"
source ${PROGDIR}/pwx_git_funcs.sh
# Version, please keep this current
VERSION="0.5.0"
# Global values to be filled in:
PATCH_DIR="${PROGDIR}/patches"
TAG_TO_USE=""
# Editor to edit individual patches when needed
PWX_EDIT=/usr/bin/kate
# The options for the git am command:
GIT_AM_OPTS="--committer-date-is-author-date"
# Options and the corresponding help text
OPT_SHORT=hi:T
OPT_LONG=help,input:,theirs
HELP_TEXT="Usage: $0 [OPTIONS] <source dir> <tag>
Take all commit patches from the input directory and
apply them to the local tree.
They are assumed to be from tag <tag> of some source
tree.
OPTIONS:
-h|--help Show this help and exit.
-i|--input <path> : Path to where to patches are.
The default is to read from the
subdirectory 'patches' of the
current directory.
Notes:
- When the script succeeds, it adds a line to the commit
- file \"${PWX_COMMIT_FILE}\" of the form:
<tag>-merged <last used commit>
"
# =========================================
# === Use getopt (GNU enhanced version) ===
# =========================================
# Check the version first, so we do not run into trouble
getopt --test > /dev/null
if [[ $? -ne 4 ]]; then
echo "ERROR: getopt is not the GNU enhanced version."
exit 1
fi
# Store the output so we can check for errors.
OPT_PARSED=$(getopt --options $OPT_SHORT --longoptions $OPT_LONG --name "$0" -- "$@")
if [[ $? -ne 0 ]]; then
# getopt has already complained about wrong arguments to stdout
exit 2
fi
# Use eval with "$OPT_PARSED" to properly handle the quoting
eval set -- "$OPT_PARSED"
# --------------------
# --- Handle input ---
# --------------------
while true; do
case "$1" in
-h|--help)
echo "$HELP_TEXT"
exit 0
;;
-i|--input)
PATCH_DIR="$2"
shift 2
;;
--)
shift
break
;;
*)
echo "Something went mysteriously wrong."
exit 3
;;
esac
done
# At this point we must have <source dir> and <tag> left
if [[ $# -ne 2 ]]; then
echo "$HELP_TEXT"
exit 4
fi
# So these must be it:
SOURCE_DIR="$1"
TAG_TO_USE="$2"
if [[ ! -d "$SOURCE_DIR" ]]; then
echo "$SOURCE_DIR does not exist"
echo
echo "$HELP_TEXT"
exit 5
fi
# ===========================================
# === The PATCH_DIR directory must exist. ===
# ===========================================
if [[ -n "$PATCH_DIR" ]]; then
if [[ ! -d "$PATCH_DIR" ]]; then
echo "ERROR: $PATCH_DIR does not exist"
exit 4
fi
else
echo "You have set the patch directory to be"
echo "an empty string. Where should I find the"
echo "patches, then?"
exit 5
fi
# =============================================================
# === We need two file lists. ===
# === A) The list of root files. ===
# === These have not been used to generate commit ===
# === patches and must be ignored by 'git am'. ===
# === B) The patches to work with. ===
# =============================================================
echo -n "Building file lists ..."
# --- File list a) Root files
declare -a root_files=( $(find ./ -mindepth 1 -maxdepth 1 -type f \
-not -name '*~' -and \
-not -name '*.diff' -and \
-not -name '*.orig' -and \
-not -name '*.bak' -printf "%f ") )
# --- File list b) Patch files
# --- Here we might find patches that failed for single files. Those
# --- must not clutter the list, they wouldn't apply anyway.
declare -a patch_files=( $(find "$PATCH_DIR"/ -mindepth 1 -maxdepth 1 -type f \
-name '????-*.patch' -and -not -name '*-failed_patch_for-*' \
-printf "%f\n" | sort -n) )
echo " done"
# --- Add an error log file to the list of temp files
PWX_ERR_LOG="/tmp/pwx_git_applier_$$.log"
add_temp "$PWX_ERR_LOG"
touch $PWX_ERR_LOG || die "Unable to create $PWX_ERR_LOG"
# ===================================================
# === Build a basic exclude string to begin with. ===
# ===================================================
basic_excludes=""
for e in "${root_files[@]}" ; do
if [[ "" = "$(echo -n "$basic_excludes" | \
grep -P "^\sexclude=$e")" ]]; then
basic_excludes+=" --exclude=$e"
fi
done
# ============================================
# === Main loop over the found patch files ===
# ============================================
for p in "${patch_files[@]}" ; do
# For further processing the number and the full path
# are needed.
pnum=${p%%-*}
psrc="${PATCH_DIR}/${p}"
# We start with normal 3-way-patching
GIT_USE_TWP="-3 "
# ====================================================
# === Step 1) Reset the exclude list of root files ===
# ====================================================
excludes="$basic_excludes"
# ==============================================
# === Step 2) Start applying the patch as is ===
# ==============================================
echo -n "Applying $p ..."
git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc 1>/dev/null 2>$PWX_ERR_LOG
res=$?
echo " done [$res]"
# ===========================================================
# === Step 3) Look for reasons to not use 3-way patching ===
# === Symptom : "could not build fake ancestor" ===
# === Reason : No common root can be built ===
# === Resolution: Do not use "-3" option ===
# ===========================================================
if [[ 0 -ne $res ]] && \
[[ $(grep -c "could not build fake ancestor" $PWX_ERR_LOG) -gt 0 ]];
then
echo -n "Trying again without 3-way-patching ..."
GIT_USE_TWP=""
git am --abort 1>/dev/null 2>&1
git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc 1>/dev/null 2>$PWX_ERR_LOG
res=$?
echo " done [$res]"
fi
# ====================================================================
# === Step 4) Look for more files to exclude ===
# === Symptom : "error: <file>: does not exist in index" ===
# === Reason : The file to patch isn't there ===
# === Resolution: Exclude the offending file(s) ===
# ====================================================================
if [[ 0 -ne $res ]] && \
[[ $(grep -c "does not exist in index" $PWX_ERR_LOG) -gt 0 ]];
then
declare -a nff_files=( $( \
grep "does not exist in index" $PWX_ERR_LOG | \
cut -d ':' -f 2) )
for nff in "${nff_files[@]}" ; do
echo "Excluding $nff ..."
excludes+=" --exclude=$nff"
# A special an evil case is a rename copy of something non-existent.
# git am then needs *two* excludes, one for the (non-existing)
# source and one for the still not existing target.
nff_tgt="$(grep -A 1 "copy from $nff" $psrc | \
grep "copy to " | \
cut -d ' ' -f 3)"
if [[ "x" != "x$nff_tgt" ]]; then
echo "Excluding $nff_tgt ..."
excludes+=" --exclude=$nff_tgt"
fi
done
echo -n "Trying again without non-existing files ..."
git am --abort 1>/dev/null 2>&1
git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc 1>/dev/null 2>$PWX_ERR_LOG
res=$?
echo " done [$res]"
unset nff_files
fi
# ====================================================================
# === Step 5) If this still doesn't work, let check_tree.pl check ===
# === out the commit and build diffs for the touched files ===
# === against the tree. Then let the user have a chance to ===
# === decide what to apply before continuing. ===
# ====================================================================
if [[ 0 -ne $res ]] && \
[[ $(grep -c "patch does not apply" $PWX_ERR_LOG) -gt 0 ]];
then
res=0
xCommit="$(head -n 1 $psrc | cut -d ' ' -f 2)"
echo "git am failed to apply the patch automatically:"
echo -e "\n--------"
cat $PWX_ERR_LOG
echo -e "--------\n"
echo "Building patch diffs for commit $xCommit ..."
# We need to know which files are relevant:
xFiles=""
xPatches=""
for pF in $(grep "^diff \--git" $psrc | cut -d ' ' -f 3,4 | \
while read a b ; do \
echo -e "$a\n$b" | cut -d '/' -f 2- ; \
done | sort -u) ; do
xFiles+="-f $pF "
xPatches+="${PROGDIR}/patches/${pF//\//_}.patch "
done
# If we have our lists, do it:
if [[ "x" != "x$xFiles" ]] && [[ "x" != "x$xPatches" ]]; then
$(PROGDIR)/check_tree.pl -c $xCommit $xFiles "$SOURCE_DIR"
# Let's see which patches got built
xResult=""
for xP in $xPatches ; do
echo -n "Checkin $xP : "
if [[ -f "$xP" ]]; then
echo "present"
xResult+="$xP "
else
echo "missing"
fi
done
# So, if no patches have been found, this whole thing
# can be skipped.
if [[ "x" = "x$xResult" ]]; then
echo "No relevant patches found."
echo -n "Skipping $psrc"
git am --skip
else
# Okay, something to do found.
echo "Please edit/delete the diffs as they are"
echo "and then close $PWX_EDIT to continue"
echo " ... to skip, just delete all patches"
$PWX_EDIT $xResult 1>/dev/null 2>&1
# Apply patch-by-patch and add to git.
have_patch=0
for xP in $xResult ; do
while [[ -f "$xP" ]]; do
have_patch=1
echo "Applying $xP ..."
patch -p 1 -i $xP
if [[ 0 -ne $? ]]; then
echo "Something is wrong with $xP"
$PWX_EDIT $xP 1>/dev/null 2>&1
else
rm -f $xP
fi
done
done
if [[ 0 -eq $have_patch ]]; then
echo "All patches deleted."
git am --skip
else
git add man src
git status
echo "Patch: $psrc"
echo -e -n "\nDoes this look in order to you? [Y/n]"
read answer
if [[ "xn" = "x$answer" ]]; then
echo "Okay, then see what you can do"
exit 0
fi
echo -n "Finishing $psrc ..."
git am --continue 1>/dev/null 2>$PWX_ERR_LOG
res=$?
echo " done [$res]"
fi
fi
else
echo "ERROR: Could not get file list from $psrc"
echo
echo "You have to do this by hand. Sorry."
exit 1
fi
fi
# ===========================================
# === Step 6) Exit if we couldn't help it ===
# ===========================================
if [[ $res -ne 0 ]]; then
echo -e "\n ==> $psrc can not be applied"
echo " ==> The command that failed was:"
echo "git am $GIT_USE_TWP$GIT_AM_OPTS$excludes < $psrc"
echo -e " ==> The Error message is:\n"
cat $PWX_ERR_LOG
echo -e "\nPlease try to apply the remaining parts by hand"
echo "and then finish with 'git am --continue'"
cleanup
exit $res
fi
# ==================================
# === Step 7) Clean up behind us ===
# ==================================
# The patch is applied or irrelevant, remove it.
rm -f $psrc
# Remove merge debris
find -iname '*.orig' | xargs rm -f
# Remove temp files
cleanup
done
|