scripts/patch-rework.sh
author "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>
Sun Jan 17 23:06:02 2010 +0100 (2010-01-17)
changeset 1740 c57458bb354d
child 2147 0ec4491d6496
permissions -rwxr-xr-x
configure: do not require hg when configuring in an hg clone

When configuring in an hg clone, we need hg to compute the version string.
It can happen that users do not have Mercurial (eg. if they got a snapshot
rather that they did a full clone). In this case, we can still run, of
course, so simply fill the version string with a sufficiently explicit
value, that does not require hg. The date is a good candidate.
     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"