diff --git a/devel/review b/devel/review new file mode 100755 index 0000000000000000000000000000000000000000..febf80dfb53236ca659ed385bb081265c442d5ed --- /dev/null +++ b/devel/review @@ -0,0 +1,186 @@ +#!/bin/bash + +# Copyright (C) 2009 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# To set user mappings, use this command: +# git config gnt-review.johndoe 'John Doe <johndoe@domain.tld>' + +set -e + +# Get absolute path to myself +me_plain="$0" +me=$(readlink -f "$me_plain") + +add_reviewed_by() { + local msgfile="$1" + + grep -q '^Reviewed-by: ' "$msgfile" && return + + perl -i -e ' + my $sob = 0; + while (<>) { + if ($sob == 0 and m/^Signed-off-by:/) { + $sob = 1; + + } elsif ($sob == 1 and not m/^Signed-off-by:/) { + print "Reviewed-by: \n"; + $sob = -1; + } + + print; + } + + if ($sob == 1) { + print "Reviewed-by: \n"; + } + ' "$msgfile" +} + +replace_users() { + local msgfile="$1" + + perl -i -e ' + sub map_username { + my ($name) = @_; + + return $name unless $name; + + my @cmd = ("git", "config", "--get", "gnt-review.$name"); + + open(my $fh, "-|", @cmd) or die "Command \"@cmd\" failed: $!"; + my $output = do { local $/ = undef; <$fh> }; + close($fh); + + if ($? == 0) { + chomp $output; + $output =~ s/\s+/ /; + return $output; + } + + return $name; + } + + while (<>) { + if (m/^Reviewed-by:(.*)$/) { + my @names = grep { + # Ignore empty entries + !/^$/ + } map { + # Normalize whitespace + $_ =~ s/(^\s+|\s+$)//g; + $_ =~ s/\s+/ /g; + + # Map names + $_ = map_username($_); + + $_; + } split(m/,/, $1); + + foreach (sort @names) { + print "Reviewed-by: $_\n"; + } + } else { + print; + } + } + ' "$msgfile" + + if ! grep -q '^Reviewed-by: ' "$msgfile" + then + echo 'Missing Reviewed-by: line' >&2 + sleep 1 + return 1 + fi + + return 0 +} + +run_editor() { + local filename="$1" + local editor=${EDITOR:-vi} + local args + + case "$(basename "$editor")" in + vi* | *vim) + # Start edit mode at Reviewed-by: line + args='+/^Reviewed-by: +nohlsearch +startinsert!' + ;; + *) + args= + ;; + esac + + $editor $args "$filename" +} + +commit_editor() { + local msgfile="$1" + + local tmpf=$(mktemp) + trap "rm -f $tmpf" EXIT + + cp "$msgfile" "$tmpf" + + while : + do + add_reviewed_by "$tmpf" + + run_editor "$tmpf" + + replace_users "$tmpf" && break + done + + cp "$tmpf" "$msgfile" +} + +copy_commit() { + local rev="$1" target_branch="$2" + + echo "Copying commit $rev ..." + + git cherry-pick -n "$rev" + GIT_EDITOR="$me --commit-editor \"\$@\"" git commit -c "$rev" -s +} + +main() { + local range="$1" target_branch="$2" + + if [[ -z "$target_branch" || "$range" != *..* ]] + then + echo "Usage: $me_plain <from..to> <target-branch>" >&2 + exit 1 + fi + + git checkout "$target_branch" + local old_head=$(git rev-parse HEAD) + + for rev in $(git rev-list --reverse "$range") + do + copy_commit "$rev" + done + + git log "$old_head..$target_branch" +} + +if [[ "$1" == --commit-editor ]] +then + shift + commit_editor "$@" +else + main "$@" +fi