| #!/bin/bash |
| set -e |
| get_patch_id_commit_id_pairs() { |
| git log "$@" --pretty=tformat:'COMMITm@g1c %H %al %as %f%n%B' | awk 'function p(){if(h){print(d?d:c?c:g?g:(a":"t":"s)),h}};$1=="COMMITm@g1c"{p();h=$2;a=$3;t=$4;s=$5;c=d=g="";next};$1=="Differential"&&$2=="Revision:"{d=$3;sub("http.*/","",d);next};$1=="Change-Id:"{c=$1$2;next};$1=="git-svn-id:"{g=$2;next};END{p()}' |
| } |
| |
| if [ -z "$1" ]; then |
| echo "Usage: `basename "$0"` branch_to_auto_pick_from" |
| exit |
| fi |
| FROM_BRANCH="$1" |
| TO_BRANCH="$(git rev-parse --abbrev-ref HEAD)" |
| if [ -z "$TO_BRANCH" ]; then |
| echo "Please git checkout to a branch first" |
| exit 1 |
| fi |
| |
| echo "Computing merge base from $FROM_BRANCH to $TO_BRANCH ..." |
| MERGE_BASE="$(git merge-base --all "$FROM_BRANCH" "$TO_BRANCH")" |
| if [ -z "$MERGE_BASE" ]; then |
| echo "Failed to get merge base" |
| exit 1 |
| elif [ $(wc -l <<< "$MERGE_BASE") -gt 1 ]; then |
| echo "Git tree with cross merge not supported" |
| exit 1 |
| fi |
| echo "Merge base is $(get_patch_id_commit_id_pairs $MERGE_BASE^..$MERGE_BASE)" |
| |
| echo -n "Getting patch list on $FROM_BRANCH ... " |
| declare -A MAP_FROM_PATCHES |
| REV_LIST_FROM_PATCHES= |
| while read c d; do |
| if [ -n "$(exec 2>/dev/null; echo ${MAP_FROM_PATCHES[$c]})" ]; then |
| echo "Duplicate patch name found for $c $d ${MAP_FROM_PATCHES[$c]}" |
| d="$d ${MAP_FROM_PATCHES[$c]}" |
| fi |
| MAP_FROM_PATCHES[$c]="$d" |
| REV_LIST_FROM_PATCHES="$c |
| $REV_LIST_FROM_PATCHES" |
| done < <(get_patch_id_commit_id_pairs "$MERGE_BASE..$FROM_BRANCH") |
| echo "${#MAP_FROM_PATCHES[@]} patches" |
| |
| echo -n "Getting patch list on $TO_BRANCH ... " |
| declare -A MAP_TO_PATCHES |
| declare -A MAP_TO_PATCHES_DUP |
| while read c d; do |
| if [ -n "$(exec 2>/dev/null; echo ${MAP_TO_PATCHES[$c]})" ]; then |
| echo "Duplicate patch name found for $c $d ${MAP_TO_PATCHES[$c]}" |
| d="$d ${MAP_TO_PATCHES[$c]}" |
| MAP_TO_PATCHES_DUP[$c]="$d" |
| fi |
| MAP_TO_PATCHES[$c]="$d" |
| done < <(get_patch_id_commit_id_pairs "$MERGE_BASE..$TO_BRANCH") |
| echo "${#MAP_TO_PATCHES[@]} patches" |
| for d in ${MAP_TO_PATCHES_DUP[@]}; do |
| HASH="$(git diff --binary $d^..$d | sha1sum | cut -d ' ' -f 1)" |
| eval "HAS_$HASH=true" |
| done |
| |
| echo "Computing patches to cherry-pick ..." |
| PATCHES_TO_PICK= |
| while read c; do |
| if [ -z "$c" ]; then |
| continue |
| fi |
| if [ -z "$(exec 2>/dev/null; echo ${MAP_TO_PATCHES[$c]})" ] || [ ${#MAP_FROM_PATCHES[$c]} -ne ${#MAP_TO_PATCHES[$c]} ]; then |
| echo -ne "$c\t" |
| if [[ "${MAP_FROM_PATCHES[$c]}" == *" "* ]]; then |
| if [ -z "$(exec 2>/dev/null; echo ${MAP_TO_PATCHES_DUP[$c]})" ]; then |
| for d in ${MAP_TO_PATCHES[$c]}; do |
| HASH="$(git diff --binary $d^..$d | sha1sum | cut -d ' ' -f 1)" |
| eval "HAS_$HASH=true" |
| done |
| fi |
| for d in ${MAP_FROM_PATCHES[$c]}; do |
| if [ -z "$(eval echo "\${VISITED_$d}")" ]; then |
| break |
| fi |
| done |
| if [ -n "$(eval echo "\${VISITED_$d}")" ]; then |
| echo "All patches visited for $c: ${MAP_FROM_PATCHES[$c]}" |
| exit 1 |
| fi |
| eval "VISITED_$d=true" |
| HASH="$(git diff --binary $d^..$d | sha1sum | cut -d ' ' -f 1)" |
| if [ -n "$(eval echo "\${HAS_$HASH}")" ]; then |
| echo "$d skipped" |
| continue |
| fi |
| c="$c-$HASH" |
| MAP_FROM_PATCHES[$c]="$d" |
| fi |
| echo "${MAP_FROM_PATCHES[$c]}" |
| PATCHES_TO_PICK="$PATCHES_TO_PICK |
| $c" |
| fi |
| done <<< "$REV_LIST_FROM_PATCHES" |
| while read c; do |
| if [ -z "$c" ]; then |
| continue |
| fi |
| d="${MAP_FROM_PATCHES[$c]}" |
| echo "Cherry-picking $d for $c ..." |
| git cherry-pick "$d" |
| done <<< "$PATCHES_TO_PICK" |
| |
| echo "Done" |