scripts/patch-rework.sh
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Tue Aug 17 23:50:03 2010 +0200 (2010-08-17)
changeset 2087 fefd9a3f8180
child 2147 0ec4491d6496
permissions -rwxr-xr-x
libc/mingw: fix space-damage

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 dst="$( cd "${dst}"; pwd )"
    35 
    36 # Iterate through patches
    37 for p in "${src}/"*.patch; do
    38   pname="$( basename "${p}" )"
    39 
    40   printf "Handling patch '${pname}'...\n"
    41 
    42   printf "  creating reference..."
    43   cp -a "${base}" "${base}.orig"
    44   printf " done\n"
    45 
    46   printf "  retrieving patch comment..."
    47   comment="$( awk '
    48 BEGIN { mark=0; }
    49 $0~/^diff --/ { nextfile; }
    50 $1=="---" { mark=1; next; }
    51 $1=="+++" && mark==1 { nextfile; }
    52 { mark=0; print; }
    53 ' "${p}" )"
    54   printf " done\n"
    55 
    56   printf "  creating patched file list..."
    57   diffstat -f 4 -r 2 -u -p 0 "${p}"                         \
    58   |head -n -1                                               \
    59   |awk '{ for(i=NF;i>=NF-5;i--) { $(i) = ""; } print; }'    \
    60   |sort                                                     \
    61   >"diffstat.orig"
    62   printf " done\n"
    63 
    64   pushd "${base}" >/dev/null 2>&1
    65 
    66   # Check all files exist, up to depth 3
    67   printf "  checking depth:"
    68   for((d=0;d<4;d++)); do
    69     printf " ${d}"
    70     if do_check_files_at_depth "../diffstat.orig" ${d}; then
    71       printf " ok, using depth '${d}'\n"
    72       break
    73     fi
    74   done
    75   if [ ${d} -ge 4 ]; then
    76     printf "\n"
    77     printf "  checking depth failed\n"
    78     read -p "  --> enter patch depth (or Ctrl-C to abort): " d
    79   fi
    80 
    81   # Store the original list of fiels touched by the patch,
    82   # removing the $d leading components
    83   sed -r -e "s:^([^/]+/){${d}}::;" "../diffstat.orig" >"${dst}/${pname}.diffstat.orig"
    84 
    85   # Apply the patch proper, and check it applied cleanly.
    86   # We can't check with --dry-run because of patches that
    87   # contain multiple accumulated patches onto a single file.
    88   printf "  applying patch..."
    89   if ! patch -g0 -F1 -f -p${d} <"${p}" >"../patch.out" 2>&1; then
    90     printf " ERROR\n"
    91     # Revert the patch
    92     popd >/dev/null 2>&1
    93     printf "  restoring '${base}'..."
    94     rm -f "diffstat.tmp"
    95     rm -rf "${base}"
    96     mv "${base}.orig" "${base}"
    97     printf " done\n\n"
    98     printf "There was an error while applying:\n  -->  ${p}  <--\n"
    99     printf "'${base}' was restored to the state it was prior to applying this faulty patch.\n"
   100     printf "Here's the 'patch' command, and its output:\n"
   101     printf "  ----8<----\n"
   102     printf "  patch -g0 -F1 -f -p${d} <'${p}'\n"
   103     cat "patch.out" |(IFS=$(printf "\n"); while read line; do printf "  ${line}\n"; done)
   104     rm -f "patch.out"
   105     printf "  ----8<----\n"
   106     exit 1
   107   fi
   108   printf " done\n"
   109 
   110   printf "  removing '.orig' files..."
   111   find . -type f -name '*.orig' -exec rm -f {} +
   112   printf " done\n"
   113 
   114   popd >/dev/null 2>&1
   115 
   116   printf "  re-diffing the patch..."
   117   printf "%s\n\n" "${comment}" >"${dst}/${pname}"
   118   diff -durN "${base}.orig" "${base}" >>"${dst}/${pname}"
   119   printf " done\n"
   120 
   121   if [ -n "${diff}" ]; then
   122     printf "  applying diff filter..."
   123     filterdiff -x "${diff}" "${dst}/${pname}" >"tmp-diff"
   124     mv "tmp-diff" "${dst}/${pname}"
   125     printf " done\n"
   126   fi
   127 
   128   printf "  creating new patched file list..."
   129   diffstat -f 4 -r 2 -u -p 1 "${dst}/${pname}"              \
   130   |head -n -1                                               \
   131   |awk '{ for(i=NF;i>=NF-5;i--) { $(i) = ""; } print; }'    \
   132   |sort                                                     \
   133   >"${dst}/${pname}.diffstat.new"
   134   printf " done\n"
   135 
   136   printf "  removing temporary files/dirs..."
   137   rm -f "patch.out"
   138   rm -f "diffstat.tmp"
   139   rm -rf "${base}.orig"
   140   printf " done\n"
   141 done
   142 
   143 # Scan all new patches to see if they touch
   144 # more files than the original patches
   145 printf "\nChecking resulting patchset:\n"
   146 for p in "${dst}/"*.patch; do
   147   pname="$( basename "${p}" )"
   148 
   149   if ! cmp "${p}.diffstat.orig" "${p}.diffstat.new" >/dev/null; then
   150     printf "  --> '${pname}' differ in touched files <--\n"
   151   fi
   152 done
   153 printf "  done.\n"