scripts: add a new helper script to easily rediff a patchset
author"Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Fri Nov 13 19:34:18 2009 +0100 (2009-11-13)
changeset 1624782dea79ae56
parent 1623 f935634ef900
child 1625 fde082da9813
scripts: add a new helper script to easily rediff a patchset
scripts/patch-rework.sh
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/scripts/patch-rework.sh	Fri Nov 13 19:34:18 2009 +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"