#!/bin/bash
#
# This script is a wrapper around git-cherry-pick, which enables
# good "merging" of HISTORY, ChangeLog and binary files.
# Conflicts in those files are now rare, but the script has
# room for improvement.
rc=0
commit=$1
[[ -z $commit ]] && echo no commit specified && exit 100
[[ -f ChangeLog ]] || { echo run me from the top dir; exit 99; }

# check if commit diff does not contain a HISTORY/ChangeLog header
# it also must contain diff changes (this enables merging of bins)
if ! git show $commit | grep -q "^+++.*\(HISTORY\|ChangeLog\)" && git show $commit | grep -q "^+++"; then
  # just do it
  git-cherry-pick $commit
  exit 0
else
  git reset -q --hard # you need a clean checkout for gcp anyway
  # split the diff into parts
  git show $commit | csplit -s -n5 - '/^diff --git/' '{*}'

  rm xx00000 # commit info
  # extract history/cl or apply directly
  for diff in xx*; do
    if ! grep -q "+++.*\(HISTORY\|ChangeLog\)" $diff; then
      if ! grep -q "^Binary file" $diff; then
        file=$(sed -n '/^+++ / s,^[^/]*/,,p' $diff)
        patch -p1 < $diff
        new_rc=$?
        git add "$file"
        [[ $rc == 0 ]] && rc=$new_rc
      else
        file=$(sed -n '/^diff --git/ { s,^[^/]*/,,; s, .*$,,p} ' $diff)
        if grep -q "^deleted file" $diff; then
          git-rm "$file" ||
          echo "Warning, nonexsitent file: $file"
        else # new or updated file
          git show $commit:"$file" > "$file"
          git add "$file"
        fi
      fi
      echo
    else
      file=$(sed -n '/^+++ / s,^[^/]*/,,p' $diff)

      # maybe we're lucky and it will apply cleanly
      patch --dry-run -p1 -s < $diff &>/dev/null &&
        patch -p1 < $diff &&
        echo luckily patched $file && 
        echo &&
        continue

      echo Handling diff $diff for $file specially
      # does it have more than one chunk?
      if [[ $(grep -c "@@.*@@" $diff) != 1 ]]; then
        echo multiple history chunks - not supported
        rm -f xx*
        exit 111
      fi

      # check if it removes any lines
      if grep -q "^- " $diff; then
        echo this diff removes lines too - not supported
        rm -f xx*
        exit 112
      fi

      # numb the diff into what will be cat into the file
      # we want only the lines with +
      sed -i -e '/^+/!d' -e '/^+++/d' -e 's/^+//' $diff

      # add a date header if it is missing (multiple changes in the same day)
      # or remove it and readd it so it is in the right spot:
        # changes in the same day with individual titles can result in diff
        # picking up the old one as the new - the title would be on the last line
      #FIXME multiple titles per commit aren't supported: should delete only the first title
      sed -i '/^20/d' $diff
      CDATE=$(git show $commit --pretty=format:%ai | sed -n '1 s/ .*$//p')
      TITLE="$CDATE $(git show $commit | sed -n 's/^Author: //p')"
      sed -i "1 s,^,$TITLE\n," $diff

      # make sure the addition contains a blank last line
      sed -i "$ s,^..*$,&\n," $diff

      cat $diff $file > $file.new
      mv $file.new $file
      git add $file # needed when cherrypicking new spells
      echo
    fi
  done
fi
if [[ $rc == 0 ]]; then
  git show $commit --pretty=format:"%s%n%b%n(cherry-picked from commit %H)" | sed '/^diff --git/,$ d' |
  git commit -a -F -
else
cat << KUKU
##########################################################################
A chunk failed! Resolve the conflict manually and then commit the result.
Use this:
git show $commit \\
  --pretty=format:"%s%n%b%n(cherry-picked from commit %H)" |
sed '/^diff --git/,$ d' |
git commit -a -F -
KUKU
fi
rm -f xx*
exit $rc

