Statistics
| Branch: | Tag: | Revision:

root / devel / review @ ab6536ba

History | View | Annotate | Download (4.6 kB)

1
#!/bin/bash
2

    
3
# Copyright (C) 2009 Google Inc.
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
# General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
# 02110-1301, USA.
19

    
20
# To set user mappings, use this command:
21
#   git config gnt-review.johndoe 'John Doe <johndoe@example.com>'
22

    
23
# To disable strict mode (enabled by default):
24
#   git config gnt-review.strict false
25

    
26
# To enable strict mode:
27
#   git config gnt-review.strict true
28

    
29
set -e
30

    
31
# Get absolute path to myself
32
me_plain="$0"
33
me=$(readlink -f "$me_plain")
34

    
35
add_reviewed_by() {
36
  local msgfile="$1"
37

    
38
  grep -q '^Reviewed-by: ' "$msgfile" && return
39

    
40
  perl -i -e '
41
  my $reviewer = $ENV{"REVIEWER"};
42
  defined($reviewer) or $reviewer = "";
43
  my $sob = 0;
44
  while (<>) {
45
    if ($sob == 0 and m/^Signed-off-by:/) {
46
      $sob = 1;
47

    
48
    } elsif ($sob == 1 and not m/^Signed-off-by:/) {
49
      print "Reviewed-by: $reviewer\n";
50
      $sob = -1;
51
    }
52

    
53
    print;
54
  }
55

    
56
  if ($sob == 1) {
57
    print "Reviewed-by: $reviewer\n";
58
  }
59
  ' "$msgfile"
60
}
61

    
62
replace_users() {
63
  local msgfile="$1"
64

    
65
  if perl -i -e '
66
  use strict;
67
  use warnings;
68

    
69
  my $error = 0;
70
  my $strict;
71

    
72
  sub map_username {
73
    my ($name) = @_;
74

    
75
    return $name unless $name;
76

    
77
    my @cmd = ("git", "config", "--get", "gnt-review.$name");
78

    
79
    open(my $fh, "-|", @cmd) or die "Command \"@cmd\" failed: $!";
80
    my $output = do { local $/ = undef; <$fh> };
81
    close($fh);
82

    
83
    if ($? == 0) {
84
      chomp $output;
85
      $output =~ s/\s+/ /;
86
      return $output;
87
    }
88

    
89
    unless (defined $strict) {
90
      @cmd = ("git", "config", "--get", "--bool", "gnt-review.strict");
91

    
92
      open($fh, "-|", @cmd) or die "Command \"@cmd\" failed: $!";
93
      $output = do { local $/ = undef; <$fh> };
94
      close($fh);
95

    
96
      $strict = ($? != 0 or not $output or $output !~ m/^false$/);
97
    }
98

    
99
    if ($strict and $name !~ m/^.+<.+\@.+>$/) {
100
      $error = 1;
101
    }
102

    
103
    return $name;
104
  }
105

    
106
  while (<>) {
107
    if (m/^Reviewed-by:(.*)$/) {
108
      my @names = grep {
109
        # Ignore empty entries
110
        !/^$/
111
      } map {
112
        # Normalize whitespace
113
        $_ =~ s/(^\s+|\s+$)//g;
114
        $_ =~ s/\s+/ /g;
115

    
116
        # Map names
117
        $_ = map_username($_);
118

    
119
        $_;
120
      } split(m/,/, $1);
121

    
122
      # Get unique names
123
      my %saw;
124
      @names = grep(!$saw{$_}++, @names);
125
      undef %saw;
126

    
127
      foreach (sort @names) {
128
        print "Reviewed-by: $_\n";
129
      }
130
    } else {
131
      print;
132
    }
133
  }
134

    
135
  exit($error? 33 : 0);
136
  ' "$msgfile"
137
  then
138
    :
139
  else
140
    [[ "$?" == 33 ]] && return 1
141
    exit 1
142
  fi
143

    
144
  if ! grep -q '^Reviewed-by: ' "$msgfile"
145
  then
146
    echo 'Missing Reviewed-by: line' >&2
147
    sleep 1
148
    return 1
149
  fi
150

    
151
  return 0
152
}
153

    
154
run_editor() {
155
  local filename="$1"
156
  local editor=${EDITOR:-vi}
157
  local args
158

    
159
  case "$(basename "$editor")" in
160
    vi* | *vim)
161
      # Start edit mode at Reviewed-by: line
162
      args='+/^Reviewed-by: +nohlsearch +startinsert!'
163
    ;;
164
    *)
165
      args=
166
    ;;
167
  esac
168

    
169
  $editor $args "$filename"
170
}
171

    
172
commit_editor() {
173
  local msgfile="$1"
174

    
175
  local tmpf=$(mktemp)
176
  trap "rm -f $tmpf" EXIT
177

    
178
  cp "$msgfile" "$tmpf"
179

    
180
  while :
181
  do
182
    add_reviewed_by "$tmpf"
183

    
184
    run_editor "$tmpf"
185

    
186
    replace_users "$tmpf" && break
187
  done
188

    
189
  cp "$tmpf" "$msgfile"
190
}
191

    
192
copy_commit() {
193
  local rev="$1" target_branch="$2"
194

    
195
  echo "Copying commit $rev ..."
196

    
197
  git cherry-pick -n "$rev"
198
  GIT_EDITOR="$me --commit-editor \"\$@\"" git commit -c "$rev" -s
199
}
200

    
201
usage() {
202
  echo "Usage: $me_plain [from..to] <target-branch>" >&2
203
  echo "  If not passed from..to defaults to target-branch..HEAD" >&2
204
  exit 1
205
}
206

    
207
main() {
208
  local range target_branch
209

    
210
  case "$#" in
211
  1)
212
    target_branch="$1"
213
    range="$target_branch..$(git rev-parse HEAD)"
214
  ;;
215
  2)
216
    range="$1"
217
    target_branch="$2"
218
    if [[ "$range" != *..* ]]; then
219
      usage
220
    fi
221
  ;;
222
  *)
223
    usage
224
  ;;
225
  esac
226

    
227
  git checkout "$target_branch"
228
  local old_head=$(git rev-parse HEAD)
229

    
230
  for rev in $(git rev-list --reverse "$range")
231
  do
232
    copy_commit "$rev"
233
  done
234

    
235
  git log "$old_head..$target_branch"
236
}
237

    
238
if [[ "$1" == --commit-editor ]]
239
then
240
  shift
241
  commit_editor "$@"
242
else
243
  main "$@"
244
fi