1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/scripts/patch-rework.sh Sun Jan 31 17:07:02 2010 +0100
1.3 @@ -0,0 +1,153 @@
1.4 +#!/bin/sh
1.5 +
1.6 +# Get our required options
1.7 +base="$1"
1.8 +src="$2"
1.9 +dst="$3"
1.10 +shift 3
1.11 +
1.12 +# The remainder is for diff
1.13 +diff="$@"
1.14 +
1.15 +# This function checks that the files listed in the file in "$1"
1.16 +# do exist, at the given depth-stripping level (aka diff -p#)
1.17 +do_check_files_at_depth() {
1.18 + local flist="$1"
1.19 + local depth="$2"
1.20 + local ok=0 # 0: OK, !0: KO
1.21 +
1.22 + exec 6<&0
1.23 + exec 7<"${flist}"
1.24 +
1.25 + while read -u7 f; do
1.26 + f="$( echo "${f}" |sed -r -e "s:^([^/]+/){${depth}}::;" )"
1.27 + [ -f "${f}" ] || ok=1
1.28 + done
1.29 +
1.30 + exec 7<&-
1.31 + exec <&6
1.32 +
1.33 + return ${ok}
1.34 +}
1.35 +
1.36 +mkdir -p "${dst}"
1.37 +dst="$( cd "${dst}"; pwd )"
1.38 +
1.39 +# Iterate through patches
1.40 +for p in "${src}/"*.patch; do
1.41 + pname="$( basename "${p}" )"
1.42 +
1.43 + printf "Handling patch '${pname}'...\n"
1.44 +
1.45 + printf " creating reference..."
1.46 + cp -a "${base}" "${base}.orig"
1.47 + printf " done\n"
1.48 +
1.49 + printf " retrieving patch comment..."
1.50 + comment="$( awk '
1.51 +BEGIN { mark=0; }
1.52 +$0~/^diff --/ { nextfile; }
1.53 +$1=="---" { mark=1; next; }
1.54 +$1=="+++" && mark==1 { nextfile; }
1.55 +{ mark=0; print; }
1.56 +' "${p}" )"
1.57 + printf " done\n"
1.58 +
1.59 + printf " creating patched file list..."
1.60 + diffstat -f 4 -r 2 -u -p 0 "${p}" \
1.61 + |head -n -1 \
1.62 + |awk '{ for(i=NF;i>=NF-5;i--) { $(i) = ""; } print; }' \
1.63 + |sort \
1.64 + >"diffstat.orig"
1.65 + printf " done\n"
1.66 +
1.67 + pushd "${base}" >/dev/null 2>&1
1.68 +
1.69 + # Check all files exist, up to depth 3
1.70 + printf " checking depth:"
1.71 + for((d=0;d<4;d++)); do
1.72 + printf " ${d}"
1.73 + if do_check_files_at_depth "../diffstat.orig" ${d}; then
1.74 + printf " ok, using depth '${d}'\n"
1.75 + break
1.76 + fi
1.77 + done
1.78 + if [ ${d} -ge 4 ]; then
1.79 + printf "\n"
1.80 + printf " checking depth failed\n"
1.81 + read -p " --> enter patch depth (or Ctrl-C to abort): " d
1.82 + fi
1.83 +
1.84 + # Store the original list of fiels touched by the patch,
1.85 + # removing the $d leading components
1.86 + sed -r -e "s:^([^/]+/){${d}}::;" "../diffstat.orig" >"${dst}/${pname}.diffstat.orig"
1.87 +
1.88 + # Apply the patch proper, and check it applied cleanly.
1.89 + # We can't check with --dry-run because of patches that
1.90 + # contain multiple accumulated patches onto a single file.
1.91 + printf " applying patch..."
1.92 + if ! patch -g0 -F1 -f -p${d} <"${p}" >"../patch.out" 2>&1; then
1.93 + printf " ERROR\n"
1.94 + # Revert the patch
1.95 + popd >/dev/null 2>&1
1.96 + printf " restoring '${base}'..."
1.97 + rm -f "diffstat.tmp"
1.98 + rm -rf "${base}"
1.99 + mv "${base}.orig" "${base}"
1.100 + printf " done\n\n"
1.101 + printf "There was an error while applying:\n --> ${p} <--\n"
1.102 + printf "'${base}' was restored to the state it was prior to applying this faulty patch.\n"
1.103 + printf "Here's the 'patch' command, and its output:\n"
1.104 + printf " ----8<----\n"
1.105 + printf " patch -g0 -F1 -f -p${d} <'${p}'\n"
1.106 + cat "patch.out" |(IFS=$(printf "\n"); while read line; do printf " ${line}\n"; done)
1.107 + rm -f "patch.out"
1.108 + printf " ----8<----\n"
1.109 + exit 1
1.110 + fi
1.111 + printf " done\n"
1.112 +
1.113 + printf " removing '.orig' files..."
1.114 + find . -type f -name '*.orig' -exec rm -f {} +
1.115 + printf " done\n"
1.116 +
1.117 + popd >/dev/null 2>&1
1.118 +
1.119 + printf " re-diffing the patch..."
1.120 + printf "%s\n\n" "${comment}" >"${dst}/${pname}"
1.121 + diff -durN "${base}.orig" "${base}" >>"${dst}/${pname}"
1.122 + printf " done\n"
1.123 +
1.124 + if [ -n "${diff}" ]; then
1.125 + printf " applying diff filter..."
1.126 + filterdiff -x "${diff}" "${dst}/${pname}" >"tmp-diff"
1.127 + mv "tmp-diff" "${dst}/${pname}"
1.128 + printf " done\n"
1.129 + fi
1.130 +
1.131 + printf " creating new patched file list..."
1.132 + diffstat -f 4 -r 2 -u -p 1 "${dst}/${pname}" \
1.133 + |head -n -1 \
1.134 + |awk '{ for(i=NF;i>=NF-5;i--) { $(i) = ""; } print; }' \
1.135 + |sort \
1.136 + >"${dst}/${pname}.diffstat.new"
1.137 + printf " done\n"
1.138 +
1.139 + printf " removing temporary files/dirs..."
1.140 + rm -f "patch.out"
1.141 + rm -f "diffstat.tmp"
1.142 + rm -rf "${base}.orig"
1.143 + printf " done\n"
1.144 +done
1.145 +
1.146 +# Scan all new patches to see if they touch
1.147 +# more files than the original patches
1.148 +printf "\nChecking resulting patchset:\n"
1.149 +for p in "${dst}/"*.patch; do
1.150 + pname="$( basename "${p}" )"
1.151 +
1.152 + if ! cmp "${p}.diffstat.orig" "${p}.diffstat.new" >/dev/null; then
1.153 + printf " --> '${pname}' differ in touched files <--\n"
1.154 + fi
1.155 +done
1.156 +printf " done.\n"