#!/bin/bash set -e -u -C # Assume all arguments except the last one are git-format-patch options. options=("${@:1:($#-1)}") # Assume the last argument is the one operand for git-format-patch: the revision # set. revisions=${@:(-1)} # Fetch the array of revisions (with abbreviated hashes) in increasing order. rev_list=($(git log --reverse --format=tformat:%h "$revisions")) # Create temporary directory. tmpd=$(mktemp -d) trap 'rm -r -- "$tmpd"' EXIT # If the Notes section of a patch contains "context:-U3", "context:-U8" etc, or # "context:-W" lines, return those lines. The "git notes show" output is # expected on stdin. filter_context_opts() { sed -n -r -e 's/^context:(-U[0-9]+|-W)$/\1/p' } # In the directory given by $1, expand * into an array, then print the element # with subscript $2 to stdout. The printed element will be qualified with the # containing directory $1. get_nth_file() { local dir=$1 local ofs=$2 local flist=("$dir"/*) printf '%s\n' ${flist["$ofs"]} } # Collect all the context options used over the series into an array. The # default "-U3" context option will be forcibly generated. all_context_opts=($( ( for rev in "${rev_list[@]}"; do git notes show "$rev" 2>/dev/null || true done \ | filter_context_opts printf '%s\n' -U3 ) \ | sort -u )) # For each found context option, format the entire series, into a dedicated # subdirectory, using the caller-specified options and revision set. (Format a # cover letter as well.) Unfortunately, this is necessary because the "/m" part # in "[PATCH v3 n/m]" depends on *all* patches being formatted in one go. for context_opt in "${all_context_opts[@]}"; do mkdir -- "$tmpd/context$context_opt" git format-patch --notes --cover-letter --numbered --stat=1000 \ --stat-graph-width=20 --output-directory "$tmpd/context$context_opt" \ "$context_opt" "${options[@]}" "$revisions" >/dev/null done # Iterate over the cover letter and all the revisions. For each, determine the # context option. (For the cover letter, and for revisions without a # "context:..." line in the Notes section, assume -U3. If a revision has # multiple "context:..." lines in the Notes section, pick the first one.) Once # the context option has been determined, *positionally* look up the formatted # patch email that corresponds to the cover letter or the revision, in the # subdirectory that corresponds to the context option. patchnr=0 for rev in cover_letter "${rev_list[@]}"; do if test cover_letter = "$rev"; then context_opt= else context_opt=$( git notes show "$rev" 2>/dev/null \ | filter_context_opts \ | head -1 ) fi if test -z "$context_opt"; then context_opt=-U3 fi file=$(get_nth_file "$tmpd/context$context_opt" "$patchnr") cat -- "$file" if test $patchnr -gt 0 && test $patchnr -lt ${#rev_list[@]}; then printf '\n' fi patchnr=$(($patchnr + 1)) done