diff --git a/tools/tools/git/git-arc.sh b/tools/tools/git/git-arc.sh --- a/tools/tools/git/git-arc.sh +++ b/tools/tools/git/git-arc.sh @@ -51,7 +51,7 @@ Commands: create [-l] [-r [,...]] [-s subscriber[,...]] [|] list | - patch [ ...] + patch -c [ ...] stage [-b branch] [|] update [-m message] [|] @@ -133,6 +133,11 @@ $ git arc patch D12345 + Apply the patch in review D12345 to the currently checked-out tree, and + commit it using the review's title, summary and author + + $ git arc patch -c D12345 + List the status of reviews for all the commits in the branch "feature": $ git arc list main..feature @@ -455,18 +460,150 @@ done } +gitarc_default_email() +{ + local addr name + + addr=$1 + name=$2 + printf "%s <%s>\n" "${name}" $(echo "$addr" | sed -e 's/\(.*\)_/\1@/') +} + +# Try to guess our way to a good author name. The DWIM is strong in this +# function, but these hueristics seem to generally produce the right results, in +# the sample of src commits I checked out. +gitarc_find_author() +{ + local addr name email + + addr="$1" + name="$2" + + # The lovely and almost, but really really annoyingly not quite, phab + # interface gives us almost enough data to create an author, but sadly not + # quite. So we play whack-a-mole with heuristics to come up with something + # one hopefully calls 'a sane set of defaults'. + + # Choice 1: It's a FreeBSD commiter. These folks have no '.' in their + # phab username/addr + case "$addr" in + *.*) ;; # external user + *) + echo "$name <$addr@FreeBSD.org>" + return + ;; + esac + + # Choice 2: We know this user. They've committed before, and they happened + # to use an email that's somewhat similar to their phab username. + email=$(git log -1 --author "$(echo $addr | tr _ .)" --pretty="%aN <%aE>") + if [ -n "$email" ]; then + echo "$email" + return + fi + + # Choice 3: We know this user. They've committed before, and they happened + # to use the same name, unless the name has the word 'user' in it. This + # might not be a good idea, since names can be somewhat common (there + # are two Andrew Turners that have contributed to FreeBSD, for example). + if (echo "${name}" | grep -w "[Uu]ser" -q); then + else + email=$(git log -1 --author "${name}" --pretty="%aN <%aE>") + if [ -n "$email" ]; then + echo "$email" + return + fi + fi + + # Choice 4: Wing it as best we can. In this scenario, we replace the last _ + # with a @, and call it the email address... + # Annoying fun fact: Phab replaces all non alpha-numerics with _, so we + # don't know if the prior _ are _ or + or any number of other characters. + # So this winging it isn't always our best hope, but will usually get + # things mostly enough right. + gitarc_default_email "${addr}" "${name}" +} + +gitarc_commit() +{ + local diff reviewid review_data authorid user_data user_addr user_name author + local tmp def_email + + diff=$1 + reviewid=$(diff2phid "$diff") + review_data=$(echo '{ + "constraints": {"phids": ["'"$reviewid"'"]} + }' | + arc_call_conduit -- differential.revision.search) + authorid=$(echo "$review_data" | jq -r '.response.data[].fields.authorPHID' ) + user_data=$(echo '{ + "constraints": {"phids": ["'"$authorid"'"]}, + "attachments": {"revisions": true} + }' | + arc call-conduit -- user.search | grep -v ^Warning: | + jq -r '.response.data[].fields') + user_addr=$(echo "$user_data" | jq -r '.username') + user_name=$(echo "$user_data" | jq -r '.realName') + author=$(gitarc_find_author "$user_addr" "$user_name") + + tmp=$(mktemp) + echo "$review_data" | jq -r '.response.data[].fields.title' > $tmp + echo >> $tmp + echo "$review_data" | jq -r '.response.data[].fields.summary' >> $tmp + echo >> $tmp + # XXX this leaves an extra newline in some cases. + reviewers=$(diff2reviewers "$diff" | sed '/^$/d' | paste -sd ',' - | sed 's/,/, /g') + if [ -n "$reviewers" ]; then + printf "Reviewed by:\t%s\n" "${reviewers}" >> "$tmp" + fi + printf "Differential Revision:\thttps://reviews.freebsd.org/%s\n" "${diff}" >> "$tmp" + git commit --author "${author}" --file "$tmp" + rm "$tmp" + + # When Phabricator creates accounts, it does so by replacing all special + # characters with _, which is many-to-1, which cannot be undone. This is + # rare enough, though that we just warn about the condition for manual + # intervention. We try to make our best guess, but for first time submitters + # there's no data to correlate against. Eg, we don't know if a_b_c.com is + # a_b@c.com or a+b@c.com. + def_email=$(gitarc_default_email "${user_addr}" "${user_name}") + if [ "${author}" = "${def_email}" ]; then + case "${author}" in + *_*) + warn "Guessed '${author}' has '_' might be wrong, double check." + ;; + esac + fi +} + gitarc__patch() { - local rev + local rev commit if [ $# -eq 0 ]; then err_usage fi + commit=false + while getopts c o; do + case "$o" in + c) + commit=true + ;; + *) + err_usage + ;; + esac + done + shift $((OPTIND-1)) + for rev in "$@"; do arc patch --skip-dependencies --nocommit --nobranch --force "$rev" echo "Applying ${rev}..." [ $? -eq 0 ] || break + if $commit; then + gitarc_commit $rev + fi done }