Prepare for removal of non-const operator[] from Sequence in embeddedobj
[LibreOffice.git] / .git-hooks / pre-commit
bloba3cef10ee5f4841b70283e2956b2c3cc561ddd1a
1 #!/usr/bin/env perl
3 # A hook script to verify what is about to be committed.
4 # Called by "git commit" with no arguments. The hook should
5 # exit with non-zero status after issuing an appropriate message
6 # if it wants to stop the commit.
8 use strict;
9 use lib "solenv/clang-format";
10 #use File::Copy;
11 #use Cwd;
13 $ENV{LC_ALL} = "C";
15 sub check_whitespaces($)
17 my ($h) = @_;
18 my $src_limited = "c|cpp|cxx|h|hrc|hxx|idl|inl|java|swift|map|MK|pmk|pl|pm|sdi|sh|src|tab|ui|xcu|xml|xsl|py";
19 my $src_full = "c|cpp|cxx|h|hrc|hxx|idl|inl|java|swift|map|mk|MK|pmk|pl|pm|sdi|sh|src|tab|ui|xcu|xml|xsl|py";
21 my %modules = (basctl=>'basctl', chart2=>'chart', cui=>'cui', dbaccess=>'dba', desktop=>'dkt', editeng=>'editeng',
22 extensions=>'pcr', filter=>'flt', formula=>'for', fpicker=>'fps', framework=>'fwk', reportdesign=>'rpt',
23 sc=>'sc', sd=>'sd', sfx2=>'sfx', starmath=>'sm', svtools=>'svt', svx=>'svx', sw=>'sw', uui=>'uui',
24 vcl=>'vcl', writerperfect=>'wpt', xmlsecurity=>'xsc');
26 my $found_bad = 0;
27 my $filename;
28 my $reported_filename = "";
29 my $lineno;
30 sub bad_line
32 my ($why, $line, $file_filter) = @_;
33 if (!defined $file_filter || $filename =~ /\.($file_filter)$/)
35 if (!$found_bad)
37 print STDERR "*\n";
38 print STDERR "* You have some suspicious patch lines:\n";
39 print STDERR "*\n";
40 $found_bad = 1;
42 if ($reported_filename ne $filename)
44 print STDERR "* In $filename\n";
45 $reported_filename = $filename;
47 print STDERR "* $why (line $lineno)\n";
48 print STDERR "$filename:$lineno:$line\n";
51 open( FILES, "git-diff-index -p -M --cached $h |" ) || die "Cannot run git diff-index.";
52 while (<FILES>)
54 if (m|^diff --git a/(.*) b/\1$|)
56 $filename = $1;
57 next;
59 if (/^@@ -\S+ \+(\d+)/)
61 $lineno = $1 - 1;
62 next;
64 if (/^ /)
66 $lineno++;
67 next;
69 if (s/^\+//)
71 $lineno++;
72 chomp;
73 if (/\s$/)
75 bad_line("trailing whitespace", $_ , $src_limited);
77 if (/\r$/)
79 bad_line("DOS lineends", $_ , $src_limited);
81 if (/\s* /)
83 bad_line("indent with Tab", $_, $src_limited);
85 if (/^(?:[<>=]){7}$/)
87 bad_line("unresolved merge conflict", $src_full);
89 if (/SAL_DEBUG/)
91 bad_line("temporary debug in commit", $_, $src_limited);
93 if ((/OOXMLIMPORT/) and ($filename =~ /ooxmlexport/))
95 bad_line("OOXMLIMPORT definition used in a ooxmlexport file", $_, "cxx");
97 if ((/OOXMLEXPORT/) and ($filename =~ /ooxmlimport/))
99 bad_line("OOXMLEXPORT definition used in a ooxmlimport file", $_, "cxx");
101 if (/<property name="use[-_]markup">True<\/property>/)
103 bad_line("use font attributes instead of use-markup", $_, "ui");
105 if (/<property name="tooltip[-_]markup"/ )
107 bad_line("use tooltip-text instead of tooltip_markup", $_, "ui");
109 if (/<property name="margin[-_]left"/ )
111 bad_line("use margin-start instead of margin-left", $_, "ui");
113 if (/<property name="margin[-_]right"/ )
115 bad_line("use margin-end instead of margin-right", $_, "ui");
117 if (/<object class="GtkAlignment"/)
119 bad_line("use margin-start (etc) on child instead of a GtkAlignment", $_, "ui");
121 if (/<property name="use[-_]stock"/ )
123 bad_line("use translation context 'stock' and the English string as button label instead", $_, "ui");
125 if (/<property name="stock[-_]id"/ )
127 bad_line("use an icon-name listed at https://developer.gnome.org/gtk3/stable/gtk3-Stock-Items.html", $_, "ui");
129 if (/<property name="stock"/ )
131 bad_line("use an icon-name listed at https://developer.gnome.org/gtk3/stable/gtk3-Stock-Items.html", $_, "ui");
133 if ((/translatable="yes"/) and not(/context=/))
135 bad_line("translatable .ui file line without context", $_, "ui");
137 if ((/requires/) and (/lib="gtk+/) and not (/version="3.20/))
139 bad_line("min supported version of gtk3 is 3.20", $_, "ui");
141 if ((/<interface/) and not(/domain=/))
143 bad_line(".ui file without translation domain", $_, "ui");
145 if (/<interface domain=/)
147 foreach my $key (keys %modules) {
148 if ((rindex($filename, $key, 0) == 0) and not (/$modules{$key}/))
150 bad_line("interface domain should be '$modules{$key}'", $_, "ui");
156 if ( $found_bad)
158 exit($found_bad);
162 sub check_author()
164 my $author = `git var GIT_AUTHOR_IDENT`;
165 chomp $author;
166 if ($author =~ /^Your Name <you\@example.com>/)
168 print("ERROR: You have a suspicious author identity: '$author'\n");
169 exit(1);
173 sub check_style($)
175 if (! -e "solenv/clang-format/ClangFormat.pm")
177 # Commit happens in a submodule.
178 return;
181 require ClangFormat;
182 ClangFormat->import();
184 my ($h) = @_;
185 my $src = ClangFormat::get_extension_regex();
186 my @bad_names = ();
187 my $clang_format = ClangFormat::find();
189 ## Check if ClangFormat has get_excludelist or the old
190 ## get_blacklist
191 my $excluded_list_names;
192 eval { ClangFormat::get_excludelist() };
193 if ($@) { $excluded_list_names = ClangFormat::get_blacklist(); }
194 else { $excluded_list_names = ClangFormat::get_excludelist(); }
196 # Get a list of non-deleted changed files.
197 open (FILES, "git diff-index --cached --diff-filter=AM --name-only $h |") || die "Cannot run git diff.";
198 while (my $filename = <FILES>)
200 chomp $filename;
201 if ($filename =~ /\.($src)$/ and !exists($excluded_list_names->{$filename}))
203 if (!defined($clang_format))
205 my $version = ClangFormat::get_wanted_version();
206 my $opt_lo = ClangFormat::get_own_directory();
208 print("\nWARNING: Commit touches new (non-excluded) files, but no clang-format"
209 . " ${version}\n");
210 print(" found (via CLANG_FORMAT or PATH env vars, or in ${opt_lo}).\n\n");
212 my $platform = "linux64";
213 my $download = "wget";
214 if ($^O eq "cygwin")
216 $platform = "win.exe";
218 elsif ($^O eq "darwin")
220 $platform = "mac";
221 $download = "curl -O";
224 print("To get a suitable binary, please do:\n\n");
225 print("mkdir -p $opt_lo\n");
226 print("cd $opt_lo\n");
227 print("$download https://dev-www.libreoffice.org/bin/clang-format-$version-$platform\n");
228 print("cp clang-format-$version-$platform clang-format\n");
229 print("chmod +x clang-format\n\n");
231 print("(Or read solenv/clang-format/README on how to build it yourself.\n");
232 return;
234 if (!ClangFormat::check_style($clang_format, $filename))
236 push @bad_names, $filename;
241 # Enforce style.
242 if (scalar @bad_names)
244 my $autostyle = `git config libreoffice.autostyle`;
245 chomp $autostyle;
246 if ($autostyle ne "true")
248 print("\nThe above differences were found between the code to commit \n");
249 print("and the clang-format rules. You can apply these changes with:\n");
250 print("\n$clang_format -i " . join(" ", @bad_names) . "\n\n");
251 print("Aborting commit. Apply changes and commit again or skip checking\n");
252 print("with --no-verify (not recommended).\n");
253 exit(1);
255 else
257 # 'git config libreoffice.autostyle true' was invoked to run
258 # clang-format automatically.
259 print("\nThe above differences were found between the code to commit \n");
260 print("and the clang-format rules. Fixing these now automatically.\n");
261 print("Running '$clang_format -i " . join(" ", @bad_names) . "' for you...\n");
262 system("$clang_format -i " . join(" ", @bad_names));
263 # TODO this stages all local modifications, staging originally
264 # unstaged hunks.
265 system("git add " . join(" ", @bad_names));
266 print("Done.\n");
271 sub check_submodules($)
273 my ($h) = @_;
275 my $toplevel = `git rev-parse --show-toplevel`;
276 chomp $toplevel;
278 # trick to get a list of submodules - directly read from the .gitmodules
279 open(SUBMODULES, "git config --file '$toplevel'/.gitmodules --get-regexp path | awk '{ print \$2 }' |" ) || die "Cannot run git config on the .gitmodules.";
280 while (<SUBMODULES>)
282 chomp;
284 my $ignore = `git config submodule.$_.ignore`;
285 chomp $ignore;
286 if ($ignore eq 'all')
288 print <<EOM;
289 Error: Your git configuration has submodule.$_.ignore set to 'all'.
291 This is dangerous and can lead to accidentally pushing unwanted changes to
292 submodules.
294 To fix it, please do:
296 git config --unset submodule.$_.ignore
299 exit(1);
302 my $diff = `git diff --cached --name-only -z $h -- $_`;
303 chomp $diff;
304 if ($diff ne '')
306 print <<EOM;
307 Error: You are trying to commit changes to submodule $_ from the main repo.
309 Please do not do that, commit only to the submodule, the git hook on the
310 server will make sure the appropriate change is mirrored in the main repo.
312 To remove the change, you can do:
314 git submodule update $_
316 If it fails with 'error: Server does not allow request for unadvertised object',
317 run the following:
319 git submodule sync
320 git submodule update $_
323 exit(1);
328 # Do the work :-)
330 # Initial commit: diff against an empty tree object
331 my $against="4b825dc642cb6eb9a060e54bf8d69288fbee4904";
332 if ( system( "git rev-parse --verify HEAD >/dev/null 2>&1" ) == 0 )
334 $against="HEAD"
337 # If you want to allow non-ascii filenames set this variable to true.
338 my $allownonascii=`git config hooks.allownonascii`;
339 chomp $allownonascii;
341 # Cross platform projects tend to avoid non-ascii filenames; prevent
342 # them from being added to the repository. We exploit the fact that the
343 # printable range starts at the space character and ends with tilde.
344 if ( $allownonascii ne "true" &&
345 # Note that the use of brackets around a tr range is ok here, (it's
346 # even required, for portability to Solaris 10's /usr/bin/tr), since
347 # the square bracket bytes happen to fall in the designated range.
348 `git diff --cached --name-only --diff-filter=A -z $against | \
349 LC_ALL=C tr -d '[ -~]\\0'` ne "" )
351 print <<EOM;
352 Error: Attempt to add a non-ascii file name.
354 This can cause problems if you want to work
355 with people on other platforms.
357 To be portable it is advisable to rename the file ...
359 If you know what you are doing you can disable this
360 check using:
362 git config hooks.allownonascii true
365 exit( 1 );
368 # Block large files.
369 open( FILES, "git diff --cached --name-only --diff-filter=A -z $against |" ) || die "Cannot run git diff-index.";
370 while (<FILES>)
372 if (/\.ui$/) # .ui files can get large
374 continue;
376 if (/\.xsl$/) # XSLT
378 continue;
380 my $size = `git cat-file -s :$_`;
381 # For now let's say large is 500KB
382 my $limit = 500;
383 if ($size > $limit * 1024)
385 print "Error: Attempt to add a large file: $_, pleasy try to fit into $limit KB.\n";
386 exit( 1 );
390 # fix whitespace in code
391 check_whitespaces( $against);
393 # fix style in code
394 check_style($against);
396 # catch missing author info
397 check_author();
399 # catch commits to the submodules
400 check_submodules($against);
402 # all OK
403 exit( 0 );
404 # vi:set shiftwidth=4 expandtab: