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)