backflip-from-tsv; New script to handle a table of backports.

This script processes a spreadsheet page exported as TSV,
and generates the needed backflip commands.

Change-Id: Ie35bad3c662f07d79ab5ed95c2d4adcd7cf1fd51
diff --git a/backflip-from-tsv b/backflip-from-tsv
new file mode 100755
index 0000000..1889472
--- /dev/null
+++ b/backflip-from-tsv
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+# This script is a helper to process backports in batch from the main
+# spreadsheet.
+
+# Typical use: export the spreadsheet page in TSV (tab-separated
+# values), then call this script with the TSV as std input.
+# The script prints the list of backflip commands needed to perform
+# all the backports described in the TSV file.
+#
+# For simple backports (with no dependency), it's as easy as calling
+# backflip with the revision number as parameter.
+
+# When several commits have to be stacked, the script parses the
+# 'Stack with' column and build the stack of commits to be backported
+# together. The dependencies need not be all on the same line, nor in
+# order. The script builds the union of the stacks describing one
+# backport.
+
+# Simple backports are generated as they are read, stacks are
+# processed at the end of the script, once all the TSV table has been
+# parsed.
+
+lineno=0
+declare -A stack
+verbose=
+
+trap "rm -f /tmp/backflip-stack.keys.$$ /tmp/backflip-stack.$$" EXIT
+
+usage()
+{
+    echo Usage: $0 [-v] '<' TSV-table
+    exit 1
+}
+
+if [ $# -gt 1 ]
+then
+    usage
+fi
+
+if [ x"$1" = x-v ]
+then
+    verbose=1
+else
+    if [ x"$1" != x ]
+    then
+	usage
+    fi
+fi
+
+while read line
+do
+    lineno=$(( lineno = lineno + 1 ))
+    # We replace the tab separator by a comma in the sed command at
+    # the bottom of the loop. This is needed to use IFS.
+    saved_IFS=$IFS
+    IFS=','
+
+    set $line
+    IFS=$saved_IFS
+
+    svn=$1
+    fsf=$2
+    linaro=$3
+    summary=$4
+    target=$5
+    type=$6
+    stackwith=$7
+    notes=$8
+    status=$9
+    owner=${10}
+    author=${11}
+    #echo svn $svn fsf $fsf linaro $linaro
+
+    case $svn in
+	Colors*|revs)
+	    [ $verbose ] && echo "skip line $lineno"
+	    continue
+	    ;;
+	"")
+	    [ $verbose ] && echo "skip line $lineno with empty 1st elem"
+	    continue
+	    ;;
+	[0-9][0-9][0-9][0-9][0-9][0-9])
+	    [ $verbose ] && echo "Handling line $svn"
+	    if [ x"$fsf" != x ]; then
+		[ $verbose ] && echo "$svn will come from branch merge, skipping fsf=$fsf"
+		continue
+	    fi
+	    case $linaro in
+		"")
+		    [ $verbose ] && echo "$svn needs to be backported, alone"
+		    echo backflip $svn
+		    ;;
+		auto)
+		    [$verbose ] && echo "$svn backported automatically, skipping"
+		    continue
+		    ;;
+		no*)
+		    [ $verbose ] && echo "$svn not wanted, skipping"
+		    continue
+		    ;;
+		stack)
+		    [ $verbose ] && echo "$svn should be stacked with $stackwith"
+		    # Sort stack, use a tmp file to avoid shell
+		    # sub-processes.
+		    for rev in $stackwith
+		    do
+			echo $rev
+		    done | sort > /tmp/backflip-stack.$$
+		    while read rev
+		    do
+			# The stacks we build are ordered (increasing
+			# revisions), but there may be overlaps. We'll
+			# merge them later.
+
+			# revisions in the 'svn' column are sorted, so
+			# if $rev < $svn, it means that this stack has
+			# already been started. Append to the existing
+			# stack.
+			if [ $rev -lt $svn ]
+			then
+			    stack[${rev}]="${stack[${rev}]} ${svn}"
+			else
+			    stack[${svn}]="${stack[${svn}]} $rev"
+			fi
+		    done < /tmp/backflip-stack.$$
+		    continue # FIXME
+		    ;;
+		on-going|"on going")
+		    [ $verbose ] && echo "$svn backport on-going"
+		    continue
+		    ;;
+		"via merge")
+		    echo "ERROR: rev $svn should already be handled"
+		    continue
+		    ;;
+		*)
+		    [ $verbose ] && echo "$svn type $linaro already backported, skipping"
+		    continue
+		    ;;
+	    esac
+	    ;;
+	*)
+	    echo "Unsupported line $svn"
+	    ;;
+    esac
+done < <(sed 's/	/,/g')
+
+# Sort the keys, to process them in order, so that we can build the
+# union of the stacks in case of overlap
+for key in ${!stack[@]}
+do
+    echo ${key}
+done | sort > /tmp/backflip-stack.keys.$$
+
+for key in `cat /tmp/backflip-stack.keys.$$`
+do
+    backport=
+    # If the stack of $key is empty, it means it has already been
+    # processed as part of a previous stack: ignore it.
+    if [ x"${stack[${key}]}" != x ]
+    then
+	backport="${key}"
+	for rev in ${stack[${key}]}
+	do
+	    backport="${backport} ${rev} ${stack[${rev}]}"
+	    stack[${rev}]=
+	done
+	[ $verbose ] && echo stack: ${backport}
+	echo backflip ${backport}
+    fi
+done