#!/bin/bash
_ask() {
local A=""
while true; do
read -p $'\x1b\x5b1;34m'"$1 [y/n]? "$'\x1b\x5b0m' A || continue
[ "${A,,}" = "y" ] && return 0
[ "${A,,}" = "n" ] && return 1
done
}
_colordiff() {
if which colordiff &>/dev/null; then
colordiff
else
sed -E 's/^([-<].*)$/'$'\x1b\x5b''0;31m\1'$'\x1b\x5b''0m/ ; s/^([\+>].*)$/'$'\x1b\x5b''0;32m\1'$'\x1b\x5b''0m/ ; s/^([@0-9].*)$/'$'\x1b\x5b''0;36m\1'$'\x1b\x5b''0m/'
fi
}
WORKDIR=".svnstash.wip"
STASHDIR=".svnstash"
if [ ! -d '.svn' ]; then
echo "Must be called from svn repo root." >&2
exit 1
fi
if [ -d "$WORKDIR" -a -d "$STASHDIR" ]; then
echo "Partial stash detected. Aborting." >&2
exit 1
elif [ -d "$WORKDIR" ]; then
if ! _ask "Partial stash detected. Start from scratch"; then
exit 1
fi
rm -rf "$WORKDIR" || exit 1
elif [ -d "$STASHDIR" ]; then
grep --color=never --no-filename '^+++ ' "$STASHDIR/"*.patch | cut -b 5- | sort | uniq -c
if ! _ask "Unstash all hunks"; then
exit 0
fi
for H in "$STASHDIR/"*; do
if ! patch -p0 --forward -i "$H" --batch --fuzz=0 --unified --no-backup-if-mismatch --reject-file=-; then
if ! _ask "Retry with merging"; then
exit 1
fi
patch -p0 --forward -i "$H" --batch --merge --unified --no-backup-if-mismatch --reject-file=-
if [ "$?" -gt 1 ]; then # TODO:
echo "Failed." >&2
exit 1
fi
fi
rm "$H"
done
rm -rfv "$STASHDIR"
echo "Done." >&2
exit 0
fi
mkdir "$WORKDIR" || exit 1
STASH_NO=0
svn status --ignore-externals "." > "$WORKDIR/svnstatus" || exit 1
exec 3<"$WORKDIR/svnstatus"
while IFS= read -r -u 3 SVNSTATUS; do
ST="${SVNSTATUS:0:7}"
FN="${SVNSTATUS:8}"
FNH=`shasum <<<"$FN" | cut -d ' ' -f 1`
if [ "$ST" = ' ' ]; then
continue
elif [ "$ST" != 'M ' ]; then
echo "Ignoring $FN" >&2
continue
fi
HUNKNO=0
DIFFHEAD=""
HUNKDESC=""
HUNK=""
_end_hunk() {
printf '%s' "$DIFFHEAD"
printf '%s\n%s' "$HUNKDESC" "$HUNK" | _colordiff "diff"
if _ask "Stash this hunk"; then
STASH_NO=$(( $STASH_NO + 1 ))
HH=`printf "%s/%s.%s.%03d.patch" "$WORKDIR" "${FN##*/}" "$FNH" "$HUNKNO"`
printf -- '--- /dev/null\n+++ %s\n' "$FN" > "$HH"
printf '%s\n%s' "$HUNKDESC" "$HUNK" >> "$HH"
fi
}
svn diff --internal-diff --extensions --unified "$FN" > "$WORKDIR/svndiff.$FNH" || continue
exec 4<"$WORKDIR/svndiff.$FNH"
while IFS= read -r -u 4 SVNDIFF; do
DF="${SVNDIFF:0:1}"
if [ "$DF" = '@' ]; then
HUNKNO=$(( $HUNKNO + 1 ))
if [ -n "$HUNKDESC" ]; then
_end_hunk
fi
HUNKDESC="$SVNDIFF"
HUNK=""
elif [ -z "$HUNKDESC" ]; then
DIFFHEAD="$DIFFHEAD$SVNDIFF"$'\n'
else
HUNK="$HUNK$SVNDIFF"$'\n'
fi
done
exec 4<&-
if [ -n "$HUNKDESC" ]; then
HUNKNO=$(( $HUNKNO + 1 ))
_end_hunk
fi
done
exec 3<&-
if [ "$STASH_NO" -eq 0 ]; then
echo "Nothing to do." >&2
rm -rf "$WORKDIR"
exit 0
fi
if ! _ask "Stash $STASH_NO hunks"; then
rm -rf "$WORKDIR"
exit 0
fi
mkdir -v "$STASHDIR" || exit 1
PREVF=""
for H in "$WORKDIR/"*.patch; do
F=`head -n 2 "$H" | tail -n 1 | sed 's/^+++ //'`
if [ "$F" != "$PREVF" ]; then
PREVF="$F"
STASH_COUNT=0
fi
HEAD=`head -n 3 "$H" | tail -n 1`
HEAD="${HEAD#@@ -}"
HEAD="${HEAD% @@}"
FROM="${HEAD% \+*}"
TO="${HEAD#* \+}"
FROM_START="${FROM%,*}"
FROM_COUNT="${FROM#*,}"
TO_START="${TO%,*}"
TO_COUNT="${TO#*,}"
NEW_START=$(( $TO_START - $STASH_COUNT ))
STASH_COUNT=$(( $STASH_COUNT + $TO_COUNT - $FROM_COUNT ))
HH="$STASHDIR/${H##*/}"
sed "s/^@.*/@@ -$NEW_START,$FROM_COUNT +$NEW_START,$TO_COUNT @@/" "$H" > "$HH"
if patch -p0 --reverse -i "$HH" --batch --fuzz=0 --unified --no-backup-if-mismatch --reject-file=-; then
sed "s/^@.*/@@ -$TO_START,$FROM_COUNT +$TO_START,$TO_COUNT @@/" "$H" > "$HH"
rm "$H"
else
rm "$HH"
exit 1
fi
done
rm -rf "$WORKDIR"
echo "Done." >&2
exit 0