diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f53c6af6001c96a210853e956ef0593a1b2d2ab3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +Unless noted otherwise, the following 3-clause BSD-style license +applies to the scripts in this package: + +Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/bin/git-edit-commit b/bin/git-edit-commit new file mode 100755 index 0000000000000000000000000000000000000000..c51e0be74aea83968e8e4fe437096ab45f7d9227 --- /dev/null +++ b/bin/git-edit-commit @@ -0,0 +1,87 @@ +#!/bin/sh +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +if test "x$EDITOR" = "x$0" ; then + # The script was launched as an $EDITOR from git rebase -i. + # Modify the pick line to an edit line and just exit. + sed -e '1,$s/^pick '"$SHORT_SHA1 /edit $SHORT_SHA1 /" < "$1" >/tmp/editcommit$$ + mv /tmp/editcommit$$ "$1" + exit 0 +fi + +# Extract the SHA-1 from the command-line and validate it. +MODE=message +if test "x$1" = "x--source" ; then + MODE=source + shift +fi +if test -z "$1" ; then + echo "Usage: $0 [--source] sha-1" + echo "" + echo "git-edit-commit allows you to edit a git commit to change" + echo "the commit message, even if it is several hundred changes back." + echo "It is equivalent to 'git rebase -i', but slightly safer." + echo "" + echo "The --source option specifies that you want to edit the source" + echo "as well as the commit message. This starts a 'git rebase -i'" + echo "which you will then have to perform manually." + exit 1 +fi +SHA1="$1" +if ! git merge-base "$SHA1" HEAD >/dev/null 2>/dev/null ; then + echo "$SHA1 does not appear to be in the current branch" + exit 1 +fi + +# Get the current branch. +CURRENTBRANCH=`git branch | grep '^\*' | awk '{print $2}' -` +if test "$CURRENTBRANCH" = "(no" ; then + echo "Not on a valid branch - please check out the correct branch" + exit 1 +fi + +if test -n "`git diff HEAD`" ; then + echo "You have uncommited changes. Please commit or stash them first." >&2 + exit 1 +fi + +# Replace aliases like HEAD and HEAD^ with the actual SHA-1. +SHA1=`git rev-parse "$SHA1"` +SHORT_SHA1=`git rev-parse --short "$SHA1"` + +# Check that the change hasn't already been pushed. +COMMON=`git merge-base "$SHA1" "origin/$CURRENTBRANCH" 2>/dev/null` +if test "$COMMON" = "$SHA1" ; then + echo "$SHA1 has already been pushed - cannot edit it" + exit 1 +fi + +EDITOR="$0" +export EDITOR +export SHORT_SHA1 +if ! git rebase --preserve-merges -i "${SHA1}^" ; then + exit 1 +fi +unset EDITOR + +# If doing a source rebase, then just start it up at the right point. +if test "$MODE" = "source" ; then + echo "To make the source for your chosen commit editable, run:" + echo "" + echo " git reset HEAD^" + echo "" + exit 0 +fi + +git commit --amend + +git rebase --continue + +exit 0 diff --git a/bin/git-note-review b/bin/git-note-review new file mode 100755 index 0000000000000000000000000000000000000000..53e606521c4011665f3aa9871c268228df4e8af9 --- /dev/null +++ b/bin/git-note-review @@ -0,0 +1,115 @@ +#! /bin/sh +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +if test -z "$2"; then + cat >&2 <<EOF +Usage: $0 <user> <sha-1>... + +git-note-review adds Reviewed-by: lines to the log messages of the +named commits. Commits can be specified as either as a single sha1 or +as a sha1_excl..sha1_incl range (see git rev-list). +If any <whatever>-by: tag with the specified <user> is found to be +already present in a given commit, the commit is skipped. +EOF + exit 1 +fi + +GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) +if [ -z "$GIT_DIR" ]; then + echo >&2 "Not a git repository." + exit 1 +fi +cd "$GIT_DIR/.." + +REF=$(git symbolic-ref HEAD 2>/dev/null) +if [ -z "$REF" ]; then + echo >&2 "Not on a branch." + exit 1 +fi + +if test -n "`git diff HEAD`" ; then + echo "You have uncommited changes. Please commit or stash them first." >&2 + exit 1 +fi + +who=$1 +shift +SHA1s= +firstSHA1= +fail=false +for j in "$@"; do + if test -z "${j##*..*}"; then + if ! jj=`git rev-list $j 2> /dev/null`; then + echo "Cannot parse commit range $j." >&2 + fail=true + continue + fi + j=$jj + fi + for ii in $j; do + if ! i=`git rev-parse --short $ii 2> /dev/null`; then + echo "Cannot parse commit id $ii." >&2 + fail=true + continue + fi + if test -n "`git rev-list -1 $i ^HEAD`"; then + echo "Commit $i is not on current branch. Did you rebase it meanwhile?" >&2 + fail=true + continue + fi + if git log -1 --pretty=%b $i | egrep -q -i "^[-[:alpha:]]+-by: *$who\$"; then + echo "Commit $i already noted as reviewed by $who." + else + if test -z "$firstSHA1"; then + firstSHA1=`git rev-parse $i` + SHA1s=$i + else + firstSHA1=`git merge-base $firstSHA1 $i` + SHA1s="$SHA1s|$i" + fi + fi + done +done +if $fail; then + echo "Exiting due to fatal errors." >&2 + exit 1 +fi +if test -z "$SHA1s"; then + echo "No commits to change." >&2 + exit 0 +fi + +# This would be pretty much bullet-proof, but also a lot slower. +#if test -z "$(git rev-list -1 $firstSHA1 --not $(git for-each-ref --format='%(objectname)' refs/remotes/))"; then +# echo "$firstSHA1 has already been pushed - cannot edit it." >&2 +# exit 1 +#fi +# This should be Good Enough (TM), and it is rather fast. +if test -n "$(git rev-list -1 $firstSHA1^..@{upstream})"; then + echo "Commit `git rev-parse --short $firstSHA1` has already been pushed - cannot edit it." >&2 + exit 1 +fi + +git filter-branch --force --original refs/note-review-backup --msg-filter ' + if echo $GIT_COMMIT | egrep -q "^('"$SHA1s"')"; then + sed "s/^\\(Reviewed-by:\\) *\\(pending\\|tbd\\|TBD\\)\$/\\1 '"$who"'/;t nx +\${ +/^[-[:alpha:]]\+:/!a\\ + +a\\ +Reviewed-by: '"$who"' +} +b +:nx;n;b nx" + else + cat + fi +' $firstSHA1^..$REF || exit 1 +rm -rf $GIT_DIR/refs/note-review-backup # we have the reflog, so what diff --git a/bin/git-pasteapply b/bin/git-pasteapply new file mode 100755 index 0000000000000000000000000000000000000000..d8f90449ea2a4067d5aea9208d91fd71490c33d0 --- /dev/null +++ b/bin/git-pasteapply @@ -0,0 +1,66 @@ +#!/usr/bin/env perl +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +use strict; +use WWW::Mechanize; +use Config; + +unless (scalar @ARGV) { + print "Usage: git pasteapply [options] <number> [<number>...]\n" . + "Fetches the paste from pastebin.ca numbered <number> and applies\n" . + "Options are passed directly to git am\n" . + "\n" . + "Useful options:\n" . + " -s, --signoff Add Signed-off-by: line to the commit message\n" . + " -3, --3way When the patch does not apply cleanly, fall back on 3-way merge\n" . + " -p<n> Strip <n> elements of the paths (default 1)\n" . + " --whitespace=<nowarn,warn,fix,error,error-all>\n"; + exit 0; +} +my @pastes; +my @args; + +for (@ARGV) { + if (m/^-/) { + push @args, $_; + } else { + push @pastes, $_; + } +} + +open GIT_AM, "|-", "git", "am", @args, "-" + or die "Cannot start git-am: $!"; + +my $www = WWW::Mechanize->new(); +foreach my $paste (@pastes) { + my $url = pastebin_url($paste); + print "Applying $url\n"; + $www->get($url); + my $content = $www->content(); + $content =~ s/\r\n/\n/g; + print GIT_AM $content; +} + +close GIT_AM; +exit $?; + +sub pastebin_url($) { + my $arg = $_[0]; + return "http://pastebin.ca/raw/$3" + if ($arg =~ m,^(https?://)?(.*\.)?pastebin\.ca/([0-9]+),); + return "http://pastebin.com/download.php?i=$3" + if ($arg =~ m,^(https?://)?(.*\.)?pastebin.com/([a-zA-Z0-9]+)$,); + + return $arg if ($arg =~ m,^http://,); # Assume it's plain text... + + # No http:// prefix + return "http://pastebin.ca/raw/$arg" if ($ENV{PASTEBIN} =~ /pastebin\.ca/); + return "http://pastebin.com/download.php?i=$arg"; # Fallback, assume pastebin.com +} diff --git a/bin/git-pastebin b/bin/git-pastebin new file mode 100755 index 0000000000000000000000000000000000000000..7ee7bbbefee9db51d53cb9056443995a4e83f540 --- /dev/null +++ b/bin/git-pastebin @@ -0,0 +1,204 @@ +#!/usr/bin/env perl +# Arguments are: +# git pastebin [<commit>] +# + +# Copyright (C) Thiago Macieira <thiago@kde.org> +# Additional fixes by Giuseppe D'Angelo <dangelog@gmail.com> +# This program is based on paste2pastebin.pl, that is +# Copyright (C) Fred Blaise <chapeaurouge_at_madpenguin_dot_org> +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +use strict; +use LWP::UserAgent; +use WWW::Mechanize; + +my $description; +my $content; +my $squash = -1; +my @committish; +my @fpargs; +my @pathargs; +my $dashdash = 0; + +while (scalar @ARGV) { + my $arg = shift @ARGV; + + if ($arg eq "-d" || $arg eq "--description") { + die "Option $arg requires an argument" unless scalar @ARGV; + $description = shift @ARGV; + } elsif ($arg eq "-s" || $arg eq "--squash") { + $squash = 1; + } elsif ($arg eq "--no-squash") { + $squash = 0; + } elsif ($arg eq "--") { + $dashdash = 1; + } elsif ($arg =~ m/^-/) { + push @fpargs, $arg; + } elsif ($dashdash) { + push @pathargs, $arg; + } else { + push @committish, $arg; + } +} + +# Squash by default if description was given +$squash = $description if ($squash == -1); + +# Default revision is HEAD +push @committish, "HEAD" unless (scalar @committish); + +# Prepend -- if pathargs isn't empty +unshift @pathargs, "--" if (scalar @pathargs); + +# Try to parse this commit +my @revlist; +my $needsrevlist = 0; +open REVPARSE, "-|", "git", "rev-parse", @committish; +while (<REVPARSE>) { + chomp; + push @revlist, $_; + $needsrevlist = 1 if (m/^\^/); +} +close REVPARSE; + +if ($needsrevlist) { + # Get the revision list then + open REVLIST, "-|", "git", "rev-list", "--reverse", @revlist + or die "Cannot run git-rev-list: $!"; + @revlist = (); + while (<REVLIST>) { + chomp; + push @revlist, $_; + } + close REVLIST; +} + +# Are we squashing? +if (scalar @revlist > 1 && $squash) { + # Yes, this one is easy + open FORMATPATCH, "-|", "git", "format-patch", "--stdout", @fpargs, @committish, @pathargs + or die "Cannot run git format-patch: $!"; + + while (<FORMATPATCH>) { + $content .= $_; + } + close FORMATPATCH; + + submit($description, $content); + exit(0); +} + +# No, we're not squashing +# Iterate over the commits +for my $rev (@revlist) { + $content = ""; + $description = ""; + + # Get description + open PRETTY, "-|", "git", "log", "--pretty=format:%s%n%b", "$rev^!" + or die "Cannot get description for $rev: $!"; + while (<PRETTY>) { + $description .= $_; + } + close PRETTY; + + # Get patch + open FORMATPATCH, "-|", "git", "format-patch", "--stdout", @fpargs, "$rev^!", @pathargs + or die "Cannot get patch for $rev: $!"; + while (<FORMATPATCH>) { + $content .= $_; + } + close FORMATPATCH; + + submit($description, $content); +} + +sub submit($$) { + if ($ENV{PASTEBIN} =~ /pastebin\.ca/) { + submit_pastebinca(@_); + } elsif ($ENV{PASTEBIN} =~ /pastebin.com/) { + submit_pastebincom(@_); + } elsif (!$ENV{PASTEBIN}) { + submit_default(@_); + } else { + die "Sorry, I don't know how to talk to $ENV{PASTEBIN}." + } +} + +sub submit_default($$) { + submit_pastebincom(@_); +} + +sub submit_pastebincom($$) { + my ($description, $content) = @_; + my $paste_subdomain; + $paste_subdomain = $2 if ($ENV{PASTEBIN} =~ m,(https?://)?(.*)\.pastebin\.com,); + + my %fields = ( + paste_code => $content, + paste_subdomain => $paste_subdomain, + paste_format => 'diff', + paste_name => $ENV{PASTEBIN_NAME} + ); + + my $agent = LWP::UserAgent->new(); + my $api = 'http://pastebin.com/api_public.php'; + + my $reply = $agent->post($api, \%fields); + + die "Could not send paste: $!" unless ($reply->is_success); + + my $reply_content = $reply->decoded_content; + + # actually, pastebin.com is so dumb that it returns + # HTTP 200 OK even in case of error; the content then contains an error message... + die "Could not send paste: $reply_content" if ($reply_content =~ /^ERROR/); + + print $reply_content, "\n"; +} + +sub submit_pastebinca($$) { + my ($description, $content) = @_; + my %fields = ( + content => $content, + description => $description, + type => '34', # Unfortunately, "Diff/Patch" is missing in the quiet interface + expiry => '1 month', + name => '', + ); + my $www = WWW::Mechanize->new(); + + my $api_key = "1OIIB1/PYzkNe/azsjOFkm4iBe0414V0"; + my $pastebin_url = "http://pastebin.ca/quiet-paste.php?api=$api_key"; + my $pastebin_root = "http://pastebin.ca"; + + $www->get($pastebin_url); + die "Cannot get the pastebin form: $!" unless ($www->success()); + + $www->form_name('pasteform'); + $www->set_fields(%fields); + $www->submit_form(button => 's'); + + die "Could not send paste: $!" unless ($www->success()); + + $content = $www->content(); + if($content =~ m/^SUCCESS:(.*)/) { + print "$pastebin_root/$1\n"; + } else { + print $content; + } +} diff --git a/bin/git-qt-cherry-pick b/bin/git-qt-cherry-pick new file mode 100755 index 0000000000000000000000000000000000000000..25ac3efb19bd0ff9895f2c0858cc52130183d84b --- /dev/null +++ b/bin/git-qt-cherry-pick @@ -0,0 +1,250 @@ +#! /usr/bin/perl +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +use warnings; +use strict; + +my @repos = ( + [ "projects.pro", "qt" ], + [ "qlalr.pro", "qt5/qlalr" ], + [ "qtactiveqt.pro", "qt5/qtactiveqt" ], + [ "qtbase.pro", "qt5/qtbase" ], + [ "qtdeclarative.pro", "qt5/qtdeclarative" ], + [ "qtdoc.pro", "qt5/qtdoc" ], + [ "qtphonon.pro", "qt5/qtphonon" ], + [ "qtscript.pro", "qt5/qtscript" ], + [ "qtsvg.pro", "qt5/qtsvg" ], + [ "qttools.pro", "qt5/qttools" ], + [ "qttranslations.pro", "qt5/qttranslations" ], + [ "qtxmlpatterns.pro", "qt5/qtxmlpatterns" ], + + [ "shell/sanitize-commit", "devtools" ], + [ "git-hooks/sanitize-commit", "devscripts" ], +); + +my @mappings = ( + [ [ "qt", "qt5/qlalr" ], [ + [ 'util/qlalr//[^/]+$', 'src//[^/]+$' ], + [ 'util/qlalr', '' ] + ] ], + [ [ "qt", "qt5/qtactiveqt" ], [ + [ 'tools/activeqt', 'tools' ], + [ 'doc/src/examples/activeqt', 'doc/src/examples' ], + [ 'doc/src/development//[^/]+$', 'doc/src//[^/]+$' ] + ] ], + [ [ "qt", "qt5/qtbase" ], [ + ] ], + [ [ "qt", "qt5/qtdeclarative" ], [ + [ 'tools/qml', 'tools/qmlviewer' ] + ] ], + [ [ "qt", "qt5/qtdoc" ], [ + [ 'tools/qdoc3/test', 'doc/config' ] + ] ], + [ [ "qt", "qt5/qtphonon" ], [ + ] ], + [ [ "qt", "qt5/qtscript" ], [ + ] ], + [ [ "qt", "qt5/qtsvg" ], [ + ] ], + [ [ "qt", "qt5/qttools" ], [ + [ 'tools', 'src' ] + ] ], + [ [ "qt", "qt5/qttranslations" ], [ + ] ], + [ [ "qt", "qt5/qtxmlpatterns" ], [ + ] ], + [ [ "devtools", "devscripts" ], [ + [ 'shell/git-', 'git-bin/git-' ], + [ 'shell/git_post_commit_hook', 'git-hooks/git_post_commit_hook' ], + [ 'shell/sanitize-commit', 'git-hooks/sanitize-commit' ] + ] ] +); + +my ($cand, $inv); +sub map_name($) +{ + my ($fn) = @_; + for my $ptha (@{$$cand[1]}) { + my ($pthi, $ptho) = ($$ptha[$inv], $$ptha[1 - $inv]); + $pthi .= "(.*)" if (!($pthi =~ s,//(.*),/($1),)); + $ptho =~ s,//.*,/,; + last if ($fn =~ s/^$pthi/$ptho$1/); + } + return $fn; +} + +sub get_arg() +{ + if (@ARGV < 2) { + die $ARGV[0]." requires an argument.\n"; + } + shift @ARGV; + return $ARGV[0]; +} + +sub get_repo_arg() +{ + my $arg = $ARGV[0]; + my $val = get_arg(); + for my $rp (@repos) { + if ($$rp[1] eq $val) { + return $val; + } + if ($$rp[1] =~ /\/\Q$val\E$/) { + return $$rp[1]; + } + } + die "Fatal: Unknown repository passed to $arg.\n"; +} + +my ($src_repo, $dst_repo, $other_path) = ("", "", ""); +my @commits = (); +my $push = 0; +my $noop = 0; +while (@ARGV) { + my $arg = $ARGV[0]; + if ($arg eq '-h' or $arg eq '-help' or $arg eq '--help') { + print <<EOF ; +Usage: $0 [options] [SHA1/-range] + +Options: + -l, --pull <repo> Specify repo to pull from instead of guessing it. + -s, --push [<repo>] Push [to specified repo] instead of pulling. + -p, --path <path> Specify path of other repo instead of guessing it. + -n, --dry-run Dump patch to stdout instead of applying it. + -- Only SHA1s follow this option (use with -s without repo). + +By default, the HEAD commit of the source repo is cherry-picked. +EOF + exit 0; + } elsif ($arg eq '-l' or $arg eq '--pull') { + $src_repo = get_repo_arg(); + } elsif ($arg eq '-s' or $arg eq '--push') { + $dst_repo = get_repo_arg() if (@ARGV >= 2 && $ARGV[1] !~ /^-/); + $push = 1; + } elsif ($arg eq '-p' or $arg eq '--path') { + $other_path = get_arg(); + } elsif ($arg eq '-n' or $arg eq '--dry-run') { + $noop = 1; + } elsif ($arg eq "--") { + shift @ARGV; + push @commits, @ARGV; + last; + } elsif ($arg =~ /^-/) { + die "Unrecognized option $arg.\n"; + } else { + push @commits, $arg; + } + shift @ARGV; +} + +die "-s and -l are mutually exclusive.\n" if ($src_repo ne "" and $dst_repo ne ""); + +push @commits, "HEAD" if (!@commits); + +my $cdup = `git rev-parse --show-cdup` or exit 1; +chomp $cdup; +if (length($cdup)) { + chdir $cdup or die "Cannot enter $cdup: $!\n"; +} + +my $this_repo = ""; +for my $rp (@repos) { + if (-f $$rp[0]) { + $this_repo = $$rp[1]; + last; + } +} +die "Fatal: This Git repository is not known to this script.\n" if ($this_repo eq ""); + +if (!$push) { + $dst_repo = $this_repo; +} else { + $src_repo = $this_repo; +} + +die "Fatal: source and target repository are the same.\n" if ($src_repo eq $dst_repo); + +my @candidates = (); +for my $cand (@mappings) { + for my $inv (0, 1) { + if (("" eq $src_repo or $$cand[0][$inv] eq $src_repo) and + ("" eq $dst_repo or $$cand[0][1 - $inv] eq $dst_repo)) { + push @candidates, [ $cand, $inv ]; + last; + } + } +} +if (@candidates == 0) { + die "Fatal: found no mapping between source and target repository.\n"; +} elsif (@candidates > 1) { + die "Fatal: found multiple mappings between source and target repository. Try -l/-p.\n"; +} +($cand, $inv) = @{$candidates[0]}; +print "Porting from ".$$cand[0][$inv]." to ".$$cand[0][1 - $inv]."\n"; +if ($other_path eq "") { + my $this_path = $$cand[0][1 - ($push ^ $inv)]; + my $cnt = 1; + while ($this_path =~ /\//g) { $cnt++ } + $other_path = "../"x$cnt . $$cand[0][$push ^ $inv]; +} +print "Other path is ".$other_path."\n"; +my $cd_other = "cd \"".$other_path."\" && "; +my ($src_cd, $dst_cd) = ("", ""); +if (!$push) { + $src_cd = $cd_other; +} else { + $dst_cd = $cd_other; +} + +my $needid = -1; +for my $commit (@commits) { + my @revs; + if ($commit =~ /\.\./) { + @revs = `${src_cd}git rev-list --reverse $commit`; + } else { + @revs = ($commit); + } + for my $rev (@revs) { + my ($first, $empty, $sha1, $patch, $haveid) = (1, 0, "", "", 0); + open DIFF, $src_cd."git format-patch --stdout -1 --src-prefix=\@old\@/ --dst-prefix=\@new\@/ ".$rev." |" or die "cannot run git: $!"; + while (<DIFF>) { + if ($first) { + $first = 0; + /^From (.{40}) / and $sha1 = $1; + } elsif ($_ eq "\n" || /^[-\w]+: /) { + $haveid = 1 if (/^Change-Id:/); + $empty = 1; + } else { + if (s/^(diff --git \@old\@\/)(.*)( \@new\@\/)(.*)\n/$1.map_name($2).$3.map_name($4)."\n"/e) { + } elsif (s/^(--- \@old\@|\+\+\+ \@new\@)\/([^\n]+)/$1."\/".map_name($2)/e) { + } elsif ($_ eq "---\n") { + $patch .= "\n" if (!$empty); + $patch .= "(cherry picked from commit ".$sha1.")\n"; + if (!$haveid) { + $needid = int(grep /codereview/, `${dst_cd}git remote -v`) if ($needid < 0); + $patch .= "Change-Id: I".$sha1."\n" if ($needid); + } + } + $empty = 0; + } + $patch .= $_; + } + close DIFF or die "Aborting at ".$rev."\n"; + if ($noop) { + print $patch; + } else { + open PATCH, "| ".$dst_cd."git am -3" or die "cannot run git: $!"; + print PATCH $patch; + close PATCH or die "Aborting at ".$rev."\n"; + } + } +} +exit 0; diff --git a/bin/git-rewrite-author b/bin/git-rewrite-author new file mode 100755 index 0000000000000000000000000000000000000000..f58f93779f5ee29a7e80d709c312ef987c1d53f0 --- /dev/null +++ b/bin/git-rewrite-author @@ -0,0 +1,152 @@ +#!/usr/bin/env perl +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +use strict; +use warnings; + +use Getopt::Long; + +sub usage { + print STDERR +q{Usage: git rewrite-author --from <from> --to <to> [ -f | --force ] + [--] [<rev-list options>...] + + Replace author and committer field in some commits. + + +EXAMPLE: + + You've accidentally made some commits like this: + + Author: bjones <bjones@localhost.localdomain> + + ... where it should have been: + + Author: Bob Jones <bob.jones@example.com> + + The first bad commit is a1b2c3d4... + + Fix it up by: + + $ git rewrite-author --from "bjones <bjones@localhost.localdomain>" \ + --to "Bob Jones <bob.jones@example.com>" a1b2c3d4^..HEAD + + If you omit a range of commits, this command will scan _all_ commits + leading up to HEAD, which will take a long time for repositories with + a large history. + + WARNING: this script will change the SHA1 of every commit including + and following the first rewritten commit. You can't do this for commits + you've already pushed unless you really know what you're doing! +}; +} + +sub parse_author +{ + my $author = shift; + if ($author =~ /^(.*?) <([^>]+)>$/) { + return ($1, $2); + } + else { + die "Could not parse `$author'; expected something like \"Some Name <some.address\@example.com>\""; + } +} + + +# Return a string single-quoted for use in shell +sub sh_squote +{ + my $out = shift; + $out =~ s/'/'"'"'/g; + return "'$out'"; +} + +# Return fragment of shell code to rewrite a specific env var +sub rewrite_env +{ + my %opts = @_; + my $env = $opts{env}; + my $from = sh_squote($opts{from}); + my $to = sh_squote($opts{to}); + + my $cmd = <<EOF +if [ "\$$env" = $from ]; then + $env=$to; + export $env; + if [ \$REWROTE = 0 ]; then + echo; + echo Hit for \$GIT_COMMIT:; + REWROTE=1; + fi; + echo " $env:" $from '->' $to; +fi +EOF + ; + + $cmd =~ s/\n */ /sg; + + return $cmd; +} + +my $from; +my $to; +my $force; +if (!GetOptions( + "from=s" => \$from, + "to=s" => \$to, + "force" => \$force, +)) { + usage; + exit 2; +} + +if (!$from || !$to) { + print STDERR "Need `--from' and `--to' arguments\n"; + usage; + exit 2; +} + +my ($from_name, $from_address) = parse_author($from); +my ($to_name, $to_address) = parse_author($to); + +my $env_filter_command = "REWROTE=0; ". + rewrite_env( + env => 'GIT_AUTHOR_NAME', + from => $from_name, + to => $to_name, + ) + . "; " . + rewrite_env( + env => 'GIT_COMMITTER_NAME', + from => $from_name, + to => $to_name, + ) + . "; " . + rewrite_env( + env => 'GIT_AUTHOR_EMAIL', + from => $from_address, + to => $to_address, + ) + . "; " . + rewrite_env( + env => 'GIT_COMMITTER_EMAIL', + from => $from_address, + to => $to_address, + ) +; + +my @git_filter_branch = ("git", "filter-branch", "--env-filter", $env_filter_command); +if ($force) { + push @git_filter_branch, "--force"; +} +push @git_filter_branch, @ARGV; + +exec @git_filter_branch; + diff --git a/bin/git-split-ws b/bin/git-split-ws new file mode 100755 index 0000000000000000000000000000000000000000..5ee50fe6e68945f9d3d5adaeb28bbd30a6a4c7c7 --- /dev/null +++ b/bin/git-split-ws @@ -0,0 +1,220 @@ +#! /usr/bin/perl +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +use warnings; +use strict; + +my $invocationCommand = ''; +if ($#ARGV >= 0) { + $invocationCommand = $ARGV[0]; +} + +if ($invocationCommand ne 'commit' and $invocationCommand ne 'stash' and $invocationCommand ne '') { + print STDERR <<EOF ; +Usage: $0 [commit | stash] +Removes white space only change chunks from the HEAD commit. +If invoked with commit, WS changes are committed as a separate commit on top. +If invoked with stash, WS changes are stashed. +Otherwise it will merely reapply the WS changes to the working tree. +EOF + exit 2; +} + +sub printerr() +{ + die "cannot run git: ".$! if ($? < 0); + die "git crashed with signal ".$? if ($? & 127); + die "git exited with status ".($? >> 8) if ($?); +} + +open STATUS, "git status --porcelain |" or printerr; +while (<STATUS>) { + if (/^[^ ?!]/) { + print STDERR "Index is not clean. Aborting.\n"; + exit 1; + } +} +close STATUS or printerr; + +my $patch = ""; + +my $file = ""; +my @filehdr = ("", ""); +my $fileHdrShown = 0; + +my $chunk = 0; +my @addi = (); +my @deli = (); +my $nonws; +my $ws; +my $mixws_check = 0; +my $lineno = 0; +my $lineno2 = 0; +my @ws_files = (); +my %ws_lines = (); # hash of lists +my $braces = 0; +my $open_key = qr/\s*#\s*if|.*{/; +my $close_key = qr/\s*#\s*endif|.*}/; +my $kill_all_ws = qr/\s+((?:\"(?:\\.|[^\"])*\"|\S)+)/; # Collapse all whitespace not inside strings. +my $kill_nl_ws = qr/((?:\"(?:\\.|[^\"])*\"|\S)+)\s+/; # Collapse all non-leading whitespace not inside strings. + +sub flushChunk() +{ + my $loc_nonws = 0; + my $nlonly = 1; + my ($ai, $di) = (0, 0); + while (!$loc_nonws) { + my ($a, $d) = ("", ""); + while ($ai < @addi) { + $a = $addi[$ai++]; + $a =~ s/\s+$//; + if (length($a)) { + $nlonly = 0; + last; + } + } + while ($di < @deli) { + $d = $deli[$di++]; + $d =~ s/\s+$//; + if (length($d)) { + $nlonly = 0; + last; + } + } + last if (!length($a) && !length($d)); + + $a =~ /^$close_key/o and $braces--; + $d =~ /^$close_key/o and $braces++; + if ($braces) { + $a =~ s/$kill_nl_ws/$1/go; + $d =~ s/$kill_nl_ws/$1/go; + } else { + $a =~ s/$kill_all_ws/$1/go; + $d =~ s/$kill_all_ws/$1/go; + } + $loc_nonws = 1 if ($a ne $d); + $a =~ /^$open_key/o and $braces++; + $d =~ /^$open_key/o and $braces--; + } + while ($ai < @addi) { + my $a = $addi[$ai++]; + $a =~ /^$close_key/o and $braces--; + $a =~ /^$open_key/o and $braces++; + } + while ($di < @deli) { + my $d = $deli[$di++]; + $d =~ /^$close_key/o and $braces++; + $d =~ /^$open_key/o and $braces--; + } + if ($loc_nonws) { + $nonws = 1; + } elsif (!$nlonly) { + $ws = 1; + my $chunkhdr = "@@ -".($lineno2 - $#deli).",".($#deli + 1)." +".($lineno - $#addi).",".($#addi + 1)." @@\n"; + if (!$fileHdrShown) { + $fileHdrShown = 1; + $patch .= join("", @filehdr); + } + $patch .= $chunkhdr."-".join("-", @deli)."+".join("+", @addi); + push @ws_files, $file if (!defined($ws_lines{$file})); + push @{$ws_lines{$file}}, $lineno - $#addi; + } + @addi = @deli = (); + $chunk = 0; +} + +open DIFF, "git diff-tree --no-commit-id --diff-filter=ACMR --src-prefix=\@old\@/ --dst-prefix=\@new\@/ --full-index -r -U100000 --cc -M --root HEAD |" or printerr; +while (<DIFF>) { + if (/^-/) { + if ($mixws_check) { + if (/^--- /) { + $filehdr[0] = $_; + next; + } + push @deli, substr($_, 1); + $chunk = 1; + } + $lineno2++; + next; + } + if (/^\+/) { + if (/^\+\+\+ /) { + $filehdr[1] = $_; + next; + } + $lineno++; + $_ = substr($_, 1); + if ($mixws_check) { + push @addi, $_; + $chunk = 1; + } + } else { + flushChunk() if ($chunk); + if (/^ /) { + $lineno++; + $lineno2++; + next; + } + if (/^\@\@ -(\d+),\d+ \+(\d+)/) { + $lineno2 = $1 - 1; + $lineno = $2 - 1; + next; + } + if (/^diff /) { + if (/^diff --git \@old\@\/.+ \@new\@\/(.+)$/) { + } elsif (/^diff --cc (.+)$/) { + print STDERR "Cannot operate on merge commits.\n"; + exit 1; + } else { + print STDERR "Warning: cannot parse diff header '".$_."'\n"; + next; + } + $fileHdrShown = 0; + $file = $1; + #print "*** got file ".$file.".\n"; + my $clike = ($file =~ /\.(c|cc|cpp|c\+\+|cxx|qdoc|m|mm|h|hpp|hxx|cs|java|js|qs|qml|g|y|ypp|pl|glsl)$/i); + my $foreign = ($file =~ /\/3rdparty\//); + $mixws_check = !$foreign && $clike; + $braces = 0; + next; + } + } +} +close DIFF or printerr; +flushChunk() if ($chunk); +if (!$ws) { + print STDERR "No WS only changes, not touching this.\n"; + exit 1; +} +if (!$nonws and $ws) { + print STDERR "Entirely WS changes, not touching this.\n"; + exit 1; +} +# $patch contains the patch for appling the WS changes (-R to remove them). Now apply git-foo. +system "git reset --soft HEAD^" or printerr; +open PATCH_A, "| git apply --unidiff-zero -R --index -" or printerr; +print PATCH_A $patch; +close PATCH_A or printerr; +system "git commit -C ORIG_HEAD" or printerr; +if ($invocationCommand ne '') { + open PATCH_B, "| git apply --unidiff-zero --index -" or printerr; + print PATCH_B $patch; + close PATCH_B or printerr; + if ($invocationCommand eq 'commit') { + system "git commit -m \"Whitespace Changes\"" or printerr; + } else { + system "git stash save \"Whitespace Changes\"" or printerr; + } +} else { + open PATCH_C, "| git apply --unidiff-zero -" or printerr; + print PATCH_C $patch; + close PATCH_C or printerr; +} +exit 0; diff --git a/git-hooks/git_post_commit_hook b/git-hooks/git_post_commit_hook new file mode 100755 index 0000000000000000000000000000000000000000..cc479801f296ae38c01c13064274140e2ecea439 --- /dev/null +++ b/git-hooks/git_post_commit_hook @@ -0,0 +1,19 @@ +#! /bin/sh +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +# Usage: in every repository you want to have checked: +# cd .git/hooks +# ln -s /path/to/git_post_commit_hook post-commit +# + +sha1=${1-HEAD} # just for debugging +GIT_PUSH=${GIT_PUSH+$GIT_PUSH,}wip # this check makes totally no sense locally +export GIT_PUSH +exec sanitize-commit $sha1 strict >&2 diff --git a/git-hooks/sanitize-commit b/git-hooks/sanitize-commit new file mode 100755 index 0000000000000000000000000000000000000000..36d0e4fb4e1a6179cbc0fa319a66d6a6160998ff --- /dev/null +++ b/git-hooks/sanitize-commit @@ -0,0 +1,438 @@ +#! /usr/bin/perl -w +# Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# +# Contact: Nokia Corporation <info@qt.nokia.com> +# +# You may use this file under the terms of the 3-clause BSD license. +# See the file LICENSE from this package for details. +# + +use strict; + +if ($#ARGV < 0 or $#ARGV > 1 || ($#ARGV == 1 && $ARGV[1] !~ /^(strict|gerrit)$/)) { + print STDERR "Usage: $0 <sha1> [strict]\n"; + exit 2; +} +my $sha1 = $ARGV[0]; +my $gerrit = ($#ARGV == 1 && $ARGV[1] eq "gerrit"); +my $strict = $gerrit || ($#ARGV == 1 && $ARGV[1] eq "strict"); + +my %cfg = (); +if (defined $ENV{GIT_PUSH}) { + foreach my $c (split ",", $ENV{GIT_PUSH}) { + $cfg{$c} = 1; + } +} +my $fail = 0; +my $printed = $gerrit; +my $file = ""; +my $fail_file = "-"; +my $summary; +my ($lpfx, $elpfx) = ($gerrit ? ("", "\n") : ("*** ", "***\n")); + +sub printerr() +{ + die "cannot run git: ".$! if ($? < 0); + die "git crashed with signal ".$? if ($? & 127); + die "git exited with status ".($? >> 8) if ($?); +} + +sub complain() +{ + my ($msg, $key, $level) = @_; + my $pfx; + + if (!$printed) { + $summary =~ s/^(.{50}).{5,}$/$1\[...]/; + print "***\n*** Suspicious changes in commit ".$sha1." (".$summary."):\n"; + $printed = 1; + } + if (length($file)) { + if ($file ne $fail_file) { + print $elpfx.$lpfx.$file.":\n"; + $fail_file = $file; + } + $pfx = $lpfx." - "; + } else { + if ($file ne $fail_file) { + print $elpfx; + $fail_file = ""; + } + $pfx = $lpfx."- "; + } + $level = 0 if (!defined($level) || ($level < 0 && $strict && length($key))); + if ($level >= 0) { + $fail = $level + 1 if ($level >= $fail); + if ($gerrit) { + print $pfx.$msg."\n"; + } else { + print $pfx.$msg." (key \"".$key."\")\n"; + } + } else { + print $pfx."Hint: ".$msg."\n"; + } +} + +my $ln = 0; +my $iswip = defined($cfg{wip}); +my $revok = defined($cfg{revby}); +my $badlog = defined($cfg{log}); +my $parents = 0; +my ($badauthor, $badcommitter) = (0, 0); +my ($revert1, $revert2, $nonrevert) = (0, 0, 0); +open MSG, "git log -1 --pretty=raw ".$sha1." |" or die "cannot run git: $!"; +while (<MSG>) { + chomp; + if (!s/^ //) { + if (/^parent /) { + $parents++ ; + } elsif (/^author .*\.\(none\)/) { + $badauthor = 1; + } elsif (/^commiter .*\.\(none\)/) { + $badcommitter = 1; + } + next + } + if ($ln == 0) { + $summary = $_; + $revert1 = 1 if (/^Revert ".*"$/); + if (!$iswip && $parents < 2 && /\bWIP\b|\*{3}|^(?:squash|fixup)! |^(.)\1*$/i) { + &complain("Apparently pushing a Work In Progress", "wip", 1); + } elsif (!$iswip && !$badlog && length($_) < 7) { + &complain("Log message summary is too short", "log"); + } elsif (!$badlog && !$revert1 && length($_) > 120) { + &complain("Log message summary is excessively long", "log"); + } elsif ($parents < 2 && !$revert1 && length($_) > 70) { + &complain("Aim for shorter log message summaries", "", -1); + } + } else { + if (/^This reverts commit [[:xdigit:]]{40}\.?$/) { + $revert2 = 1; + } elsif (!/^[-\w]+:|^$/) { + $nonrevert = 1; + } + if ($ln == 1) { + if (!$badlog && $_ ne "") { + &complain("2nd line of log message is not empty", "log"); + } + } else { + if (!$revok && /^Reviewed-by: *(pending|TBD)?$/i) { + &complain("Bogus or empty Reviewed-by header", "revby", 1); + $revok = 1; + } + } + } + $ln++; +} +close MSG; +printerr; + +if ($revert1 && $revert2 && !$nonrevert) { + &complain("Revert without justification", "revert", 1); +} +# These need to be delayed, because at the time they are found the subject is not known yet. +if ($badauthor) { + &complain("Bogus author email", "email", 1); +} +if ($badcommitter) { + &complain("Bogus committer email", "email", 1); +} + +my $chunk = 0; +my @addi = (); +my @deli = (); +my $nonws; +my $ws; +my $mixws_check = 0; +my $lineno = 0; +my @ws_files = (); +my %ws_lines = (); # hash of lists +my $braces = 0; +my $open_key = qr/\s*#\s*if|.*{/; +my $close_key = qr/\s*#\s*endif|.*}/; +my $kill_all_ws = qr/\s+((?:\"(?:\\.|[^\"])*\"|\S)+)/; # Collapse all whitespace not inside strings. +my $kill_nl_ws = qr/((?:\"(?:\\.|[^\"])*\"|\S)+)\s+/; # Collapse all non-leading whitespace not inside strings. + +sub flushChunk() +{ + my $loc_nonws = 0; + my $nlonly = 1; + my ($ai, $di) = (0, 0); + while (!$loc_nonws) { + my ($a, $d) = ("", ""); + while ($ai < @addi) { + $a = $addi[$ai++]; + $a =~ s/\s+$//; + if (length($a)) { + $nlonly = 0; + last; + } + } + while ($di < @deli) { + $d = $deli[$di++]; + $d =~ s/\s+$//; + if (length($d)) { + $nlonly = 0; + last; + } + } + last if (!length($a) && !length($d)); + + $a =~ /^$close_key/o and $braces--; + $d =~ /^$close_key/o and $braces++; + if ($braces) { + $a =~ s/$kill_nl_ws/$1/go; + $d =~ s/$kill_nl_ws/$1/go; + } else { + $a =~ s/$kill_all_ws/$1/go; + $d =~ s/$kill_all_ws/$1/go; + } + $loc_nonws = 1 if ($a ne $d); + $a =~ /^$open_key/o and $braces++; + $d =~ /^$open_key/o and $braces--; + } + while ($ai < @addi) { + my $a = $addi[$ai++]; + $a =~ /^$close_key/o and $braces--; + $a =~ /^$open_key/o and $braces++; + } + while ($di < @deli) { + my $d = $deli[$di++]; + $d =~ /^$close_key/o and $braces++; + $d =~ /^$open_key/o and $braces--; + } + if ($loc_nonws) { + $nonws = 1; + } elsif (!$nlonly) { + $ws = 1; + push @ws_files, $file if (!defined($ws_lines{$file})); + push @{$ws_lines{$file}}, $lineno - $#addi; + } + @addi = @deli = (); + $chunk = 0; +} + +sub formatSize($) +{ + my $sz = shift; + if ($sz >= 10 * 1024 * 1024) { + return int($sz / (1024 * 1024))."MiB"; + } elsif ($sz >= 10 * 1024) { + return int($sz / 1024)."KiB"; + } else { + return int($sz)."B"; + } +} + +sub isExe($) +{ + my $sha = shift; + my $type = `git cat-file -p $sha | file -b -`; + return $type =~ /^(ELF|PE32) /; +} + +my @style_fails = (); + +sub styleFail($) +{ + my $why = shift; + push @style_fails, $lineno.": ".$why; +} + +sub flushFile() +{ + if (@style_fails) { + &complain("Style issues", "style", -1); + for my $sf (@style_fails) { + print $lpfx." ".$sf."\n"; + } + @style_fails = (); + } +} + +my $merge; +my $new_file; +my $maybe_bin; +my $is_submodule; +my $clike; +my $size; +my $check_gen = 0; +my $crlf_fail; +my $conflict_fail; +my $tabs_check; +my $ws_check; +my $tsv_check; +my $ctlkw_check; +open DIFF, "git diff-tree --no-commit-id --diff-filter=ACMR --src-prefix=\@old\@/ --dst-prefix=\@new\@/ --full-index -r -U100000 --cc -C -l1000 --root ".$sha1." |" or die "cannot run git: $!"; +while (<DIFF>) { + if (/^-/) { + if ($mixws_check) { + /^--- / and next; + push @deli, substr($_, 1); + $chunk = 1; + } + next; + } + if ($lineno < 50 && $check_gen) { + if (/All changes made in this file will be lost|This file is automatically generated|DO NOT EDIT|DO NOT delete this file|[Gg]enerated by|uicgenerated|produced by gperf/) { + &complain("Adding generated file", "generated") if ($new_file && !defined($cfg{generated})); + $ws_check = 0; + $check_gen = 0; + } + } + if (/^\+/) { + if (/^\+\+\+ /) { + # This indicates a text file; binary files have "Binary files ... and ... differ" instead. + $maybe_bin = 0; + if ($file =~ /\.(prl|la|pc|ilk)$/i) { + &complain("Adding build artifact", "generated") if ($new_file && !defined($cfg{generated})); + $ws_check = 0; + } else { + $check_gen = 1; + } + next; + } + $lineno++; + if ($merge) { + # Consider only lines which are new relative to both parents, i.e., were added during the merge. + s/^\+\+// or next; + } else { + $_ = substr($_, 1); + if ($mixws_check) { + push @addi, $_; + $chunk = 1; + } + } + if (!$crlf_fail && /\r\n$/) { + $crlf_fail = 1; + &complain("CRLF line endings", "crlf"); + } + if (!$conflict_fail && /^(?:[<>=]){7}( |$)/) { + &complain("Unresolved merge conflict", "conflict"); + $conflict_fail = 1; + } + if ($ws_check) { + if ($tsv_check) { + styleFail("Mixing spaces with TABs") if (/^ +\t|\t +/); + } else { + styleFail("Space indent followed by a TAB character") if (/^ +\t/); + styleFail("TAB character in non-leading whitespace") if (/\S *\t/); + styleFail("Trailing whitespace") if (/[ \t]\r?\n$/); + if ($tabs_check) { + styleFail("Leading tabs") if (/^\t+/); + if ($ctlkw_check) { + styleFail("Flow control keywords must be followed by a single space") + if (/\b(if|for|foreach|Q_FOREACH|while|do|switch)(| +)\(/); + } + } + } + } + } else { + flushChunk() if ($chunk); + if (/^ /) { + $lineno++ if (!$merge || !/^ -/); + next; + } + if ($merge ? /^\@\@\@ -\S+ -\S+ \+(\d+)/ : /^\@\@ -\S+ \+(\d+)/) { + $lineno = $1 - 1; + next; + } + if (/^diff /) { + flushFile(); + if (/^diff --git \@old\@\/.+ \@new\@\/(.+)$/) { + $merge = 0; + } elsif (/^diff --cc (.+)$/) { + $merge = 1; + } else { + print STDERR "Warning: cannot parse diff header '".$_."'\n"; + next; + } + $file = $1; + #print "*** got file ".$file.".\n"; + $clike = ($file =~ /\.(c|cc|cpp|c\+\+|cxx|qdoc|m|mm|h|hpp|hxx|cs|java|js|qs|qml|g|y|ypp|pl|glsl)$/i); + my $foreign = ($file =~ /\/3rdparty\//); + $new_file = 0; + $maybe_bin = 0; + $is_submodule = 0; + $crlf_fail = defined($cfg{crlf}); + $mixws_check = !$merge && !$foreign && $clike && !defined($cfg{mixws}); + $ws_check = !defined($cfg{style}) && !$foreign && ($file !~ /\.(ts|diff|patch)$|^\.gitmodules$/); + $tsv_check = $ws_check && ($file =~ /((^|\/)objects\.map$|\.tsv$)/); + $tabs_check = $ws_check && !$tsv_check && ($file !~ /((^|\/)Makefile\b|\.def$)/); + $ctlkw_check = $tabs_check && $clike; + $conflict_fail = defined($cfg{conflict}); + $braces = 0; + $check_gen = 0; + next; + } + if ($maybe_bin && /^Binary files /) { + if ($new_file) { + if (!defined($cfg{generated}) && ($file =~ /\.(obj|o|lib|a|dll|so|exe|exp|qm|pdb|idb|suo)$/i || isExe($maybe_bin))) { + &complain("Adding build artifact", "generated"); + } + } else { + if (!defined($cfg{giant}) && $size > (2 << 20)) { + &complain("Changing huge binary file (".formatSize($size)." > 2MiB)", "giant", 1); + } + } + next; + } + if ($_ eq "new file mode 160000\n") { + $is_submodule = 1; + next; + } + if (!$is_submodule && /^index ([\w,]+)\.\.(\w+)(?! 160000)( |$)/) { + my ($old_trees, $new_tree) = ($1, $2); + #print "*** got index $old_trees $new_tree.\n"; + $size = `git cat-file -s $new_tree`; + my $issrc = $clike || ($file =~ /\.(s|asm|pas|l|m4|bat|cmd|sh|py|qdoc(conf)?)$/i); + if ($old_trees =~ /^0{40}(,0{40})*$/) { + #print "*** adding ".$file.".\n"; + if (!$conflict_fail && $file =~ /\.(BACKUP|BASE|LOCAL|REMOTE)\.[^\/]+$/) { + &complain("Adding temporary file from merge conflict resolution", "conflict", 1); + $conflict_fail = 1; + } + if (!defined($cfg{alien}) && $file =~ /(\.(sln|vcproj|vcxproj|pro\.user)|(^|\/)(Makefile\.am|CMakeLists\.txt))$/i) { + &complain("Warning: Adding alien build system file", "alien"); + } + if ($size > (2 << 20)) { + if (!defined($cfg{giant})) { + &complain("Adding huge file (".formatSize($size)." > 2MiB)", "giant", 1); + } + } elsif ($size > 50000 && !$issrc && !defined($cfg{size})) { + &complain("Warning: Adding big file (".formatSize($size)." > 50kB)", "size"); + } + $size = 0; + $new_file = 1; + } elsif ($size > 20000 && !$issrc && !defined($cfg{size})) { + my $old_size = 0; + for my $old_tree (split(/,/, $old_trees)) { + my $osz = `git cat-file -s $old_tree`; + $old_size = $osz if ($osz > $old_size); + } + if ($size > $old_size * 3 / 2) { + &complain("Warning: Increasing file size by more than 50% (". + formatSize($old_size)." => ".formatSize($size).")", "size"); + } + } + $maybe_bin = $new_tree; + next; + } + } +} +close DIFF; +printerr; +flushFile(); +if ($mixws_check) { + flushChunk() if ($chunk); + if ($nonws and $ws) { + $file = ""; + &complain("Mixing whitespace-only changes with other changes", "mixws", -1); + for my $fn (@ws_files) { + print $lpfx." WS-only in ".$fn.": ".join(", ", @{$ws_lines{$fn}})."\n"; + } + } +} + +exit ($gerrit ? (!$fail ? 11 : (10 - $fail)) : $fail)