brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.1 KiB · fb21702 Raw
221 lines · python
1#!/usr/bin/env python32#3# ======- pre-push - LLVM Git Help Integration ---------*- python -*--========#4#5# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.6# See https://llvm.org/LICENSE.txt for license information.7# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception8#9# ==------------------------------------------------------------------------==#10 11"""12pre-push git hook integration13=============================14 15This script is intended to be setup as a pre-push hook, from the root of the16repo run:17 18   ln -sf ../../llvm/utils/git/pre-push.py .git/hooks/pre-push19 20From the git doc:21 22  The pre-push hook runs during git push, after the remote refs have been23  updated but before any objects have been transferred. It receives the name24  and location of the remote as parameters, and a list of to-be-updated refs25  through stdin. You can use it to validate a set of ref updates before a push26  occurs (a non-zero exit code will abort the push).27"""28 29import argparse30import shutil31import subprocess32import sys33import time34from shlex import quote35 36VERBOSE = False37QUIET = False38dev_null_fd = None39z40 = "0000000000000000000000000000000000000000"40 41 42def eprint(*args, **kwargs):43    print(*args, file=sys.stderr, **kwargs)44 45 46def log(*args, **kwargs):47    if QUIET:48        return49    print(*args, **kwargs)50 51 52def log_verbose(*args, **kwargs):53    if not VERBOSE:54        return55    print(*args, **kwargs)56 57 58def die(msg):59    eprint(msg)60    sys.exit(1)61 62 63def ask_confirm(prompt):64    while True:65        query = input("%s (y/N): " % (prompt))66        if query.lower() not in ["y", "n", ""]:67            print("Expect y or n!")68            continue69        return query.lower() == "y"70 71 72def shell(73    cmd,74    strip=True,75    cwd=None,76    stdin=None,77    die_on_failure=True,78    ignore_errors=False,79    text=True,80    print_raw_stderr=False,81):82    # Escape args when logging for easy repro.83    quoted_cmd = [quote(arg) for arg in cmd]84    cwd_msg = ""85    if cwd:86        cwd_msg = " in %s" % cwd87    log_verbose("Running%s: %s" % (cwd_msg, " ".join(quoted_cmd)))88 89    # Silence errors if requested.90    err_pipe = subprocess.DEVNULL if ignore_errors else subprocess.PIPE91 92    start = time.time()93    p = subprocess.Popen(94        cmd,95        cwd=cwd,96        stdout=subprocess.PIPE,97        stderr=err_pipe,98        stdin=subprocess.PIPE,99        universal_newlines=text,100    )101    stdout, stderr = p.communicate(input=stdin)102    elapsed = time.time() - start103 104    log_verbose("Command took %0.1fs" % elapsed)105 106    if p.returncode == 0 or ignore_errors:107        if stderr and not ignore_errors:108            if not print_raw_stderr:109                eprint("`%s` printed to stderr:" % " ".join(quoted_cmd))110            eprint(stderr.rstrip())111        if strip:112            if text:113                stdout = stdout.rstrip("\r\n")114            else:115                stdout = stdout.rstrip(b"\r\n")116        if VERBOSE:117            for l in stdout.splitlines():118                log_verbose("STDOUT: %s" % l)119        return stdout120    err_msg = "`%s` returned %s" % (" ".join(quoted_cmd), p.returncode)121    eprint(err_msg)122    if stderr:123        eprint(stderr.rstrip())124    if die_on_failure:125        sys.exit(2)126    raise RuntimeError(err_msg)127 128 129def git(*cmd, **kwargs):130    return shell(["git"] + list(cmd), **kwargs)131 132 133def get_revs_to_push(range):134    commits = git("rev-list", range).splitlines()135    # Reverse the order so we print the oldest commit first136    commits.reverse()137    return commits138 139 140def handle_push(args, local_ref, local_sha, remote_ref, remote_sha):141    """Check a single push request (which can include multiple revisions)"""142    log_verbose(143        "Handle push, reproduce with "144        "`echo %s %s %s %s | pre-push.py %s %s"145        % (local_ref, local_sha, remote_ref, remote_sha, args.remote, args.url)146    )147    # Handle request to delete148    if local_sha == z40:149        if not ask_confirm(150            'Are you sure you want to delete "%s" on remote "%s"?'151            % (remote_ref, args.url)152        ):153            die("Aborting")154        return155 156    # Push a new branch157    if remote_sha == z40:158        if not ask_confirm(159            'Are you sure you want to push a new branch/tag "%s" on remote "%s"?'160            % (remote_ref, args.url)161        ):162            die("Aborting")163        range = local_sha164        return165    else:166        # Update to existing branch, examine new commits167        range = "%s..%s" % (remote_sha, local_sha)168        # Check that the remote commit exists, otherwise let git proceed169        if "commit" not in git("cat-file", "-t", remote_sha, ignore_errors=True):170            return171 172    revs = get_revs_to_push(range)173    if not revs:174        # This can happen if someone is force pushing an older revision to a branch175        return176 177    # Print the revision about to be pushed commits178    print('Pushing to "%s" on remote "%s"' % (remote_ref, args.url))179    for sha in revs[:10]:180        print(" - " + git("show", "--oneline", "--quiet", sha))181    if len(revs) > 10:182        print("and %d more!" % (len(revs) - 5))183 184    if len(revs) > 1:185        if not ask_confirm("Are you sure you want to push %d commits?" % len(revs)):186            die("Aborting")187 188    return189 190 191if __name__ == "__main__":192    if not shutil.which("git"):193        die("error: cannot find git command")194 195    argv = sys.argv[1:]196    p = argparse.ArgumentParser(197        prog="pre-push",198        formatter_class=argparse.RawDescriptionHelpFormatter,199        description=__doc__,200    )201    verbosity_group = p.add_mutually_exclusive_group()202    verbosity_group.add_argument(203        "-q", "--quiet", action="store_true", help="print less information"204    )205    verbosity_group.add_argument(206        "-v", "--verbose", action="store_true", help="print more information"207    )208 209    p.add_argument("remote", type=str, help="Name of the remote")210    p.add_argument("url", type=str, help="URL for the remote")211 212    args = p.parse_args(argv)213    VERBOSE = args.verbose214    QUIET = args.quiet215 216    lines = sys.stdin.readlines()217    sys.stdin = open("/dev/tty", "r")218    for line in lines:219        local_ref, local_sha, remote_ref, remote_sha = line.split()220        handle_push(args, local_ref, local_sha, remote_ref, remote_sha)221