calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / .git-hooks / pre-commit
blob51c1de7516722e1346d05e00b7f6c40808edb4f8
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 @bad_renames = ();
188 my $clang_format = ClangFormat::find();
190 ## Check if ClangFormat has get_excludelist or the old
191 ## get_blacklist
192 my $excluded_list_names;
193 eval { ClangFormat::get_excludelist() };
194 if ($@) { $excluded_list_names = ClangFormat::get_blacklist(); }
195 else { $excluded_list_names = ClangFormat::get_excludelist(); }
197 # Get a list of renamed files.
198 my %renames; # key is target pathname, value is source pathname
199 open (IN, "git diff-index --cached --find-renames --diff-filter=R --name-status $h |")
200 || die "Cannot run git diff.";
201 while (my $line = <IN>)
203 chomp $line;
204 $line =~ /^[^\t]+\t([^\t]+)\t([^\t]+)$/ || die "Unexpected response line: $line";
205 $renames{$2} = $1;
208 # Get a list of non-deleted changed files.
209 open (FILES, "git diff-index --cached --diff-filter=AM --name-only $h |") || die "Cannot run git diff.";
210 while (my $filename = <FILES>)
212 chomp $filename;
213 if ($filename =~ /\.($src)$/ and !exists($excluded_list_names->{$filename}))
215 if (!defined($clang_format))
217 my $version = ClangFormat::get_wanted_version();
218 my $opt_lo = ClangFormat::get_own_directory();
220 print("\nWARNING: Commit touches new (non-excluded) files, but no clang-format"
221 . " ${version}\n");
222 print(" found (via CLANG_FORMAT or PATH env vars, or in ${opt_lo}).\n\n");
224 my $platform = "linux64";
225 my $download = "wget";
226 if ($^O eq "cygwin")
228 $platform = "win.exe";
230 elsif ($^O eq "darwin")
232 $platform = "mac";
233 $download = "curl -O";
236 print("To get a suitable binary, please do:\n\n");
237 print("mkdir -p $opt_lo\n");
238 print("cd $opt_lo\n");
239 print("$download https://dev-www.libreoffice.org/bin/clang-format-$version-$platform\n");
240 print("cp clang-format-$version-$platform clang-format\n");
241 print("chmod +x clang-format\n\n");
243 print("(Or read solenv/clang-format/README on how to build it yourself.\n");
244 return;
246 if (!ClangFormat::check_style($clang_format, $filename))
248 if (defined($renames{$filename}))
250 push @bad_renames, $filename;
252 else
254 push @bad_names, $filename;
260 # Enforce style.
261 if (scalar @bad_names || scalar @bad_renames)
263 my $autostyle = `git config libreoffice.autostyle`;
264 chomp $autostyle;
265 if ($autostyle ne "true" or scalar @bad_renames)
267 print("\nThe above differences were found between the code to commit \n");
268 print("and the clang-format rules.\n");
269 if (scalar @bad_names)
271 print("You can apply these changes with:\n");
272 print("\n$clang_format -i " . join(" ", @bad_names) . "\n\n");
274 if (scalar @bad_renames)
276 print("\nATTENTION: Of files detected as renamed by git, the following ones are\n");
277 print("not clang-format'ed and are not listed in the excludelist. If they are\n");
278 print("renames of previously excluded files, they should be added to the\n");
279 print("excludelist:\n\n");
280 foreach my $name (@bad_renames)
282 if (exists($excluded_list_names->{$renames{$name}}))
284 print("* $name got renamed from $renames{$name},\n");
285 print(" which is even still listed in the excludelist!\n");
287 else
289 print("* $name\n");
292 print("\n");
294 print("Aborting commit. Apply changes and commit again or skip checking\n");
295 print("with --no-verify (not recommended).\n");
296 exit(1);
298 else
300 # 'git config libreoffice.autostyle true' was invoked to run
301 # clang-format automatically.
302 print("\nThe above differences were found between the code to commit \n");
303 print("and the clang-format rules. Fixing these now automatically.\n");
304 print("Running '$clang_format -i " . join(" ", @bad_names) . "' for you...\n");
305 system("$clang_format -i " . join(" ", @bad_names));
306 # TODO this stages all local modifications, staging originally
307 # unstaged hunks.
308 system("git add " . join(" ", @bad_names));
309 print("Done.\n");
314 sub check_submodules($)
316 my ($h) = @_;
318 my $toplevel = `git rev-parse --show-toplevel`;
319 chomp $toplevel;
321 # trick to get a list of submodules - directly read from the .gitmodules
322 open(SUBMODULES, "git config --file '$toplevel'/.gitmodules --get-regexp path | awk '{ print \$2 }' |" ) || die "Cannot run git config on the .gitmodules.";
323 while (<SUBMODULES>)
325 chomp;
327 my $ignore = `git config submodule.$_.ignore`;
328 chomp $ignore;
329 if ($ignore eq 'all')
331 print <<EOM;
332 Error: Your git configuration has submodule.$_.ignore set to 'all'.
334 This is dangerous and can lead to accidentally pushing unwanted changes to
335 submodules.
337 To fix it, please do:
339 git config --unset submodule.$_.ignore
342 exit(1);
345 my $diff = `git diff --cached --name-only -z $h -- $_`;
346 chomp $diff;
347 if ($diff ne '')
349 print <<EOM;
350 Error: You are trying to commit changes to submodule $_ from the main repo.
352 Please do not do that, commit only to the submodule, the git hook on the
353 server will make sure the appropriate change is mirrored in the main repo.
355 To remove the change, you can do:
357 git submodule update $_
359 If it fails with 'error: Server does not allow request for unadvertised object',
360 run the following:
362 git submodule sync
363 git submodule update $_
366 exit(1);
371 # Do the work :-)
373 # Initial commit: diff against an empty tree object
374 my $against="4b825dc642cb6eb9a060e54bf8d69288fbee4904";
375 if ( system( "git rev-parse --verify HEAD >/dev/null 2>&1" ) == 0 )
377 $against="HEAD"
380 # If you want to allow non-ascii filenames set this variable to true.
381 my $allownonascii=`git config hooks.allownonascii`;
382 chomp $allownonascii;
384 # Cross platform projects tend to avoid non-ascii filenames; prevent
385 # them from being added to the repository. We exploit the fact that the
386 # printable range starts at the space character and ends with tilde.
387 if ( $allownonascii ne "true" &&
388 # Note that the use of brackets around a tr range is ok here, (it's
389 # even required, for portability to Solaris 10's /usr/bin/tr), since
390 # the square bracket bytes happen to fall in the designated range.
391 `git diff --cached --name-only --diff-filter=A -z $against | \
392 LC_ALL=C tr -d '[ -~]\\0'` ne "" )
394 print <<EOM;
395 Error: Attempt to add a non-ascii file name.
397 This can cause problems if you want to work
398 with people on other platforms.
400 To be portable it is advisable to rename the file ...
402 If you know what you are doing you can disable this
403 check using:
405 git config hooks.allownonascii true
408 exit( 1 );
411 # Block large files.
412 open( FILES, "git diff --cached --name-only --diff-filter=A -z $against |" ) || die "Cannot run git diff-index.";
413 while (<FILES>)
415 if (/\.ui$/) # .ui files can get large
417 continue;
419 if (/\.xsl$/) # XSLT
421 continue;
423 my $size = `git cat-file -s :$_`;
424 # For now let's say large is 500KB
425 my $limit = 500;
426 if ($size > $limit * 1024)
428 print "Error: Attempt to add a large file: $_, pleasy try to fit into $limit KB.\n";
429 exit( 1 );
433 # fix whitespace in code
434 check_whitespaces( $against);
436 # fix style in code
437 check_style($against);
439 # catch missing author info
440 check_author();
442 # catch commits to the submodules
443 check_submodules($against);
445 # all OK
446 exit( 0 );
447 # vi:set shiftwidth=4 expandtab: