scripts/patch-rework.sh
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Thu Oct 07 23:45:25 2010 +0200 (2010-10-07)
changeset 2147 0ec4491d6496
parent 1624 782dea79ae56
child 2224 fc58927f24b8
permissions -rwxr-xr-x
scripts: update/fix patch-rework

Signed-off-by: "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
     1 #!/bin/sh
     2 
     3 # Get our required options
     4 base="$1"
     5 src="$2"
     6 dst="$3"
     7 shift 3
     8 
     9 # The remainder is for diff
    10 diff="$@"
    11 
    12 # This function checks that the files listed in the file in "$1"
    13 # do exist, at the given depth-stripping level (aka diff -p#)
    14 do_check_files_at_depth() {
    15   local flist="$1"
    16   local depth="$2"
    17   local ok=0  # 0: OK,  !0: KO
    18 
    19   exec 6<&0
    20   exec 7<"${flist}"
    21 
    22   while read -u7 f; do
    23     f="$( echo "${f}" |sed -r -e "s:^([^/]+/){${depth}}::;" )"
    24     [ -f "${f}" ] || ok=1
    25   done
    26 
    27   exec 7<&-
    28   exec <&6
    29 
    30   return ${ok}
    31 }
    32 
    33 mkdir -p "${dst}"
    34 base="${base%%/}"
    35 src="$( cd "${src}"; pwd )"
    36 dst="$( cd "${dst}"; pwd )"
    37 
    38 # Iterate through patches
    39 for p in "${src}/"*.patch; do
    40   pname="$( basename "${p}" )"
    41 
    42   printf "Handling patch '${pname}'...\n"
    43 
    44   printf "  creating reference..."
    45   cp -a "${base}" "${base}.orig"
    46   printf " done\n"
    47 
    48   printf "  retrieving patch comment..."
    49   comment="$( awk '
    50 BEGIN { mark=0; }
    51 $0~/^diff --/ { nextfile; }
    52 $1=="---" { mark=1; next; }
    53 $1=="+++" && mark==1 { nextfile; }
    54 { mark=0; print; }
    55 ' "${p}" )"
    56   printf " done\n"
    57 
    58   printf "  creating patched file list..."
    59   diffstat -f 4 -r 2 -u -p 0 "${p}"                         \
    60   |head -n -1                                               \
    61   |awk '{ for(i=NF;i>=NF-5;i--) { $(i) = ""; } print; }'    \
    62   |sort                                                     \
    63   >"diffstat.orig"
    64   printf " done\n"
    65 
    66   pushd "${base}" >/dev/null 2>&1
    67 
    68   # Check all files exist, up to depth 3
    69   printf "  checking depth:"
    70   for((d=0;d<4;d++)); do
    71     printf " ${d}"
    72     if do_check_files_at_depth "../diffstat.orig" ${d}; then
    73       printf " ok, using depth '${d}'\n"
    74       break
    75     fi
    76   done
    77   if [ ${d} -ge 4 ]; then
    78     printf "\n"
    79     printf "  checking depth failed\n"
    80     read -p "  --> enter patch depth (or Ctrl-C to abort): " d
    81   fi
    82 
    83   # Store the original list of fiels touched by the patch,
    84   # removing the $d leading components
    85   sed -r -e "s:^([^/]+/){${d}}::;" "../diffstat.orig" >"${dst}/${pname}.diffstat.orig"
    86 
    87   # Apply the patch proper, and check it applied cleanly.
    88   # We can't check with --dry-run because of patches that
    89   # contain multiple accumulated patches onto a single file.
    90   printf "  applying patch..."
    91   if ! patch -g0 -F1 -f -p${d} <"${p}" >"../patch.out" 2>&1; then
    92     printf " ERROR\n\n"
    93     popd >/dev/null 2>&1
    94     printf "There was an error while applying:\n  -->  ${p}  <--\n"
    95     printf "'${base}' was restored to the state it was prior to applying this faulty patch.\n"
    96     printf "Here's the 'patch' command, and its output:\n"
    97     printf "  ----8<----\n"
    98     printf "  patch -g0 -F1 -f -p${d} <'${p}'\n"
    99     sed -r -e 's/^/  /;' "patch.out"
   100     printf "  ----8<----\n"
   101     exit 1
   102   fi
   103   printf " done\n"
   104 
   105   printf "  removing '.orig' files..."
   106   find . -type f -name '*.orig' -exec rm -f {} +
   107   printf " done\n"
   108 
   109   popd >/dev/null 2>&1
   110 
   111   printf "  re-diffing the patch..."
   112   printf "%s\n\n" "${comment}" >"${dst}/${pname}"
   113   diff -durN "${base}.orig" "${base}" >>"${dst}/${pname}"
   114   printf " done\n"
   115 
   116   if [ -n "${diff}" ]; then
   117     printf "  applying diff filter..."
   118     filterdiff -x "${diff}" "${dst}/${pname}" >"tmp-diff"
   119     mv "tmp-diff" "${dst}/${pname}"
   120     printf " done\n"
   121   fi
   122 
   123   printf "  creating new patched file list..."
   124   diffstat -f 4 -r 2 -u -p 1 "${dst}/${pname}"              \
   125   |head -n -1                                               \
   126   |awk '{ for(i=NF;i>=NF-5;i--) { $(i) = ""; } print; }'    \
   127   |sort                                                     \
   128   >"${dst}/${pname}.diffstat.new"
   129   printf " done\n"
   130 
   131   printf "  removing temporary files/dirs..."
   132   rm -f "patch.out"
   133   rm -f "diffstat.tmp"
   134   rm -f "diffstat.orig"
   135   rm -rf "${base}.orig"
   136   printf " done\n"
   137 done
   138 
   139 # Scan all new patches to see if they touch
   140 # more files than the original patches
   141 printf "\nChecking resulting patchset:\n"
   142 for p in "${dst}/"*.patch; do
   143   pname="$( basename "${p}" )"
   144 
   145   if ! cmp "${p}.diffstat.orig" "${p}.diffstat.new" >/dev/null; then
   146     printf "  --> '${pname}' differ in touched files <--\n"
   147   else
   148     rm -f "${p}.diffstat.orig" "${p}.diffstat.new"
   149   fi
   150 done
   151 printf "  done.\n"