#!/bin/bash VERBOSITY=0 TEMP_D="" CR=$'\n' error() { echo "$@" 1>&2; } fail() { [ $# -eq 0 ] || error "$@"; exit 1; } Usage() { cat <> Cherry pick a patch into debian/patches. Useful to grab an upstream commit to the current packaging branch. options: -h | --help show help EOF } bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; } cleanup() { [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" } debug() { local level=${1}; shift; [ "${level}" -gt "${VERBOSITY}" ] && return error "${@}" } shorten() { local name="$1" len="70" while [ "${#name}" -gt "$len" ]; do name="${name%-*}" done _RET="$name" } print_commit() { local subject="$1" author="$2" bugs="$3" aname="" aname=${author% <*} echo "$subject${bugs:+ (LP: ${bugs})}" } print_bugs() { local subject="$1" author="$2" bugs="$3" aname="" echo "$bugs" } git_log_to_dch() { # call printer with subject, author and bugs as extracted # from either git format-patch output or git show output. local line="" commit="" lcommit="" bugs="" local printer="${1:-print_commit}" while :; do read line || break case "$line" in commit\ *|From\ *) if [ -n "$commit" ]; then "$printer" "$subject" "$author" "$bugs" fi commit=${line#* } commit=${commit%% *} bugs="" author="" subject="" ;; Author:\ *|From:\ *) author="${line#*: }";; LP:*) bugs="${bugs:+${bugs}, }${line#*: }";; "") [ -z "$subject" ] && read subject;; Subject:\ *) subject="${line#Subject: }" subject="${subject#\[PATCH\] }" ;; esac done if [ -n "$commit" ]; then "$printer" "$subject" "$author" "$bugs" fi } main() { local short_opts="ho:v" local long_opts="help,verbose" local getopt_out="" getopt_out=$(getopt --name "${0##*/}" \ --options "${short_opts}" --long "${long_opts}" -- "$@") && eval set -- "${getopt_out}" || { bad_Usage; return; } local cur="" next="" while [ $# -ne 0 ]; do cur="$1"; next="$2"; case "$cur" in -h|--help) Usage ; exit 0;; -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; --) shift; break;; esac shift; done [ -n "$TEMP_D" ] || TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") || { error "failed to make tempdir"; return 1; } trap cleanup EXIT [ $# -gt 0 ] || { bad_Usage "must provide commit-ish"; return; } local r="" commit_in="$1" chash="" shash="" sname="" fname="" cur_br="" cur_br=$(git rev-parse --abbrev-ref HEAD) || { error "failed to get current branch"; return 1; } chash=$(git show --quiet "--pretty=format:%H" "${commit_in}") || { error "failed git show $commit_in"; return 1; } if git merge-base --is-ancestor "$chash" HEAD; then error "current branch '$cur_br' already contains $commit_in ($chash)" return 1 fi out=$(git show --quiet "--pretty=format:%h %f" "$chash") || { error "failed git show $chash"; return 1; } shash=${out% *} sname=${out#* } longname="cpick-$shash-$sname" shorten "$longname" fname="$_RET" [ -d debian/patches ] || mkdir -p debian/patches || { error "failed to make debian/patches"; return 1; } local series="debian/patches/series" fpath="debian/patches/$fname" if [ -e "$series" ] && out=$(grep -- "-${shash}-" "$series"); then error "$chash already exists in $series" error " $out" return 1 fi if [ -e "$series" ]; then if out=$(quilt applied 2>&1); then error "there are quilt patches applied!" error "$out" return 1 fi fi git format-patch --stdout -1 "$chash" > "$fpath" || { error "failed git format-patch -1 $chash > $fpath"; return 1; } echo "$fname" >> "$series" || { error "failed to write to $series"; return 1; } quilt push "$fname" || { error "patches do not cleanly apply"; return 1; } quilt refresh && quilt pop -a || { error "failed to refresh or pop quilt"; return 1; } local message="" message=$(git_log_to_dch < "$fpath") || { error "failed getting log entry from $fpath"; return 1; } dch -i "cherry-pick $shash: $message" dch -e || { r=$?; error "dch -e exited $r"; return $r; } local commit_files="" commit_files=( "$series" "$fpath" ) git diff HEAD "${commit_files[@]}" echo -n "Commit this change? (Y/n): " read answer || fail "failed to read answer" case "$answer" in n|[Nn][oO]) exit 1;; esac bugs=$(git_log_to_dch print_bugs < "$fpath") msg="cherry pick $shash${bugs:+${CR}${CR}LP: ${bugs}}" git add "$series" "$fpath" || { error "failed to git add $series $fpath"; return 1; } git commit -m "$msg" "${commit_files[@]}" || fail "failed to commit '$msg'" git commit -m "update changelog" debian/changelog || fail "failed to commit update to debian changelog." return 0 } main "$@" # vi: ts=4 expandtab