Statistics
| Branch: | Tag: | Revision:

root / devel / review @ aa1d552d

History | View | Annotate | Download (4.5 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 $sob = 0;
42
  while (<>) {
43
    if ($sob == 0 and m/^Signed-off-by:/) {
44
      $sob = 1;
45

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

    
51
    print;
52
  }
53

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

    
60
replace_users() {
61
  local msgfile="$1"
62

    
63
  if perl -i -e '
64
  use strict;
65
  use warnings;
66

    
67
  my $error = 0;
68
  my $strict;
69

    
70
  sub map_username {
71
    my ($name) = @_;
72

    
73
    return $name unless $name;
74

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

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

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

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

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

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

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

    
101
    return $name;
102
  }
103

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

    
114
        # Map names
115
        $_ = map_username($_);
116

    
117
        $_;
118
      } split(m/,/, $1);
119

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

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

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

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

    
149
  return 0
150
}
151

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

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

    
167
  $editor $args "$filename"
168
}
169

    
170
commit_editor() {
171
  local msgfile="$1"
172

    
173
  local tmpf=$(mktemp)
174
  trap "rm -f $tmpf" EXIT
175

    
176
  cp "$msgfile" "$tmpf"
177

    
178
  while :
179
  do
180
    add_reviewed_by "$tmpf"
181

    
182
    run_editor "$tmpf"
183

    
184
    replace_users "$tmpf" && break
185
  done
186

    
187
  cp "$tmpf" "$msgfile"
188
}
189

    
190
copy_commit() {
191
  local rev="$1" target_branch="$2"
192

    
193
  echo "Copying commit $rev ..."
194

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

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

    
205
main() {
206
  local range target_branch
207

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

    
225
  git checkout "$target_branch"
226
  local old_head=$(git rev-parse HEAD)
227

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

    
233
  git log "$old_head..$target_branch"
234
}
235

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