mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-21 18:22:53 +01:00
Add a git hook script that can be manually setup to run some checks on every push
Right now it just catches arcanist noisy tags, and include a script to automatically clean these. Follow up on http://lists.llvm.org/pipermail/llvm-dev/2019-December/137848.html Differential Revision: https://reviews.llvm.org/D80978
This commit is contained in:
parent
25f513ca38
commit
7c337c9c89
@ -528,6 +528,17 @@ that include merges, so the `git rebase` step above is required.
|
||||
|
||||
Please ask for help if you're having trouble with your particular git workflow.
|
||||
|
||||
Git pre-push hook
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
We include an optional pre-push hook that run some sanity checks on the revisions
|
||||
you are about to push and ask confirmation if you push multiple commits at once.
|
||||
You can set it up (on Unix systems) by running from the repository root:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
% ln -sf ../../llvm/utils/git/pre-push.py .git/hooks/pre-push
|
||||
|
||||
Bisecting commits
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -183,6 +183,13 @@ that you close the review manually. In the web UI, under "Leap Into Action" put
|
||||
the git revision number in the Comment, set the Action to "Close Revision" and
|
||||
click Submit. Note the review must have been Accepted first.
|
||||
|
||||
Arcanist also adds extra tags that are mostly noise in the commit message, for
|
||||
this reason avoid using `arc land` and push commits to master directly with git
|
||||
after removing tags other than "Reviewed by" and "Differential Revision".
|
||||
You can run `llvm/utils/git/arcfilter.sh` to clean the commit message of the
|
||||
current "HEAD" commit automatically. You can also setup a git hook to catch this
|
||||
for you (see `Getting Started <GettingStarted.html#git-pre-push-hook>`).
|
||||
|
||||
|
||||
Committing someone's change from Phabricator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -217,12 +224,6 @@ the following:
|
||||
ninja check-$whatever # Rerun the appropriate tests if needed.
|
||||
git push https://github.com/llvm/llvm-project.git HEAD:master
|
||||
|
||||
Or
|
||||
|
||||
::
|
||||
|
||||
arc land D<Revision>
|
||||
|
||||
|
||||
Abandoning a change
|
||||
-------------------
|
||||
|
7
utils/git/arcfilter.sh
Executable file
7
utils/git/arcfilter.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Remove the phabricator tags from the commit at HEAD
|
||||
git log -1 --pretty=%B | \
|
||||
sed 's/^Summary://' | \
|
||||
awk '/Reviewers:|Subscribers:/{p=1} /Reviewed By:|Differential Revision:/{p=0} !p && !/^Summary:$/ {sub(/^Summary: /,"");print}' | \
|
||||
git commit --amend --date=now -F -
|
221
utils/git/pre-push.py
Executable file
221
utils/git/pre-push.py
Executable file
@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ======- pre-push - LLVM Git Help Integration ---------*- python -*--========#
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ==------------------------------------------------------------------------==#
|
||||
|
||||
"""
|
||||
pre-push git hook integration
|
||||
=============================
|
||||
|
||||
This script is intended to be setup as a pre-push hook, from the root of the
|
||||
repo run:
|
||||
|
||||
ln -sf ../../llvm/utils/git/pre-push.py .git/hooks/pre-push
|
||||
|
||||
From the git doc:
|
||||
|
||||
The pre-push hook runs during git push, after the remote refs have been
|
||||
updated but before any objects have been transferred. It receives the name
|
||||
and location of the remote as parameters, and a list of to-be-updated refs
|
||||
through stdin. You can use it to validate a set of ref updates before a push
|
||||
occurs (a non-zero exit code will abort the push).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import getpass
|
||||
from shlex import quote
|
||||
|
||||
VERBOSE = False
|
||||
QUIET = False
|
||||
dev_null_fd = None
|
||||
z40 = '0000000000000000000000000000000000000000'
|
||||
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
|
||||
def log(*args, **kwargs):
|
||||
if QUIET:
|
||||
return
|
||||
print(*args, **kwargs)
|
||||
|
||||
|
||||
def log_verbose(*args, **kwargs):
|
||||
if not VERBOSE:
|
||||
return
|
||||
print(*args, **kwargs)
|
||||
|
||||
|
||||
def die(msg):
|
||||
eprint(msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def ask_confirm(prompt):
|
||||
while True:
|
||||
query = input('%s (y/N): ' % (prompt))
|
||||
if query.lower() not in ['y', 'n', '']:
|
||||
print('Expect y or n!')
|
||||
continue
|
||||
return query.lower() == 'y'
|
||||
|
||||
|
||||
def get_dev_null():
|
||||
"""Lazily create a /dev/null fd for use in shell()"""
|
||||
global dev_null_fd
|
||||
if dev_null_fd is None:
|
||||
dev_null_fd = open(os.devnull, 'w')
|
||||
return dev_null_fd
|
||||
|
||||
|
||||
def shell(cmd, strip=True, cwd=None, stdin=None, die_on_failure=True,
|
||||
ignore_errors=False, text=True, print_raw_stderr=False):
|
||||
# Escape args when logging for easy repro.
|
||||
quoted_cmd = [quote(arg) for arg in cmd]
|
||||
cwd_msg = ''
|
||||
if cwd:
|
||||
cwd_msg = ' in %s' % cwd
|
||||
log_verbose('Running%s: %s' % (cwd_msg, ' '.join(quoted_cmd)))
|
||||
|
||||
err_pipe = subprocess.PIPE
|
||||
if ignore_errors:
|
||||
# Silence errors if requested.
|
||||
err_pipe = get_dev_null()
|
||||
|
||||
start = time.time()
|
||||
p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=err_pipe,
|
||||
stdin=subprocess.PIPE,
|
||||
universal_newlines=text)
|
||||
stdout, stderr = p.communicate(input=stdin)
|
||||
elapsed = time.time() - start
|
||||
|
||||
log_verbose('Command took %0.1fs' % elapsed)
|
||||
|
||||
if p.returncode == 0 or ignore_errors:
|
||||
if stderr and not ignore_errors:
|
||||
if not print_raw_stderr:
|
||||
eprint('`%s` printed to stderr:' % ' '.join(quoted_cmd))
|
||||
eprint(stderr.rstrip())
|
||||
if strip:
|
||||
if text:
|
||||
stdout = stdout.rstrip('\r\n')
|
||||
else:
|
||||
stdout = stdout.rstrip(b'\r\n')
|
||||
if VERBOSE:
|
||||
for l in stdout.splitlines():
|
||||
log_verbose('STDOUT: %s' % l)
|
||||
return stdout
|
||||
err_msg = '`%s` returned %s' % (' '.join(quoted_cmd), p.returncode)
|
||||
eprint(err_msg)
|
||||
if stderr:
|
||||
eprint(stderr.rstrip())
|
||||
if die_on_failure:
|
||||
sys.exit(2)
|
||||
raise RuntimeError(err_msg)
|
||||
|
||||
|
||||
def git(*cmd, **kwargs):
|
||||
return shell(['git'] + list(cmd), **kwargs)
|
||||
|
||||
|
||||
def get_revs_to_push(range):
|
||||
commits = git('rev-list', range).splitlines()
|
||||
# Reverse the order so we print the oldest commit first
|
||||
commits.reverse()
|
||||
return commits
|
||||
|
||||
|
||||
def handle_push(args, local_ref, local_sha, remote_ref, remote_sha):
|
||||
'''Check a single push request (which can include multiple revisions)'''
|
||||
log_verbose('Handle push, reproduce with '
|
||||
'`echo %s %s %s %s | pre-push.py %s %s'
|
||||
% (local_ref, local_sha, remote_ref, remote_sha, args.remote,
|
||||
args.url))
|
||||
# Handle request to delete
|
||||
if local_sha == z40:
|
||||
if not ask_confirm('Are you sure you want to delete "%s" on remote "%s"?' % (remote_ref, args.url)):
|
||||
die("Aborting")
|
||||
return
|
||||
|
||||
# Push a new branch
|
||||
if remote_sha == z40:
|
||||
if not ask_confirm('Are you sure you want to push a new branch/tag "%s" on remote "%s"?' % (remote_ref, args.url)):
|
||||
die("Aborting")
|
||||
range=local_sha
|
||||
return
|
||||
else:
|
||||
# Update to existing branch, examine new commits
|
||||
range='%s..%s' % (remote_sha, local_sha)
|
||||
# Check that the remote commit exists, otherwise let git proceed
|
||||
if "commit" not in git('cat-file','-t', remote_sha, ignore_errors=True):
|
||||
return
|
||||
|
||||
revs = get_revs_to_push(range)
|
||||
if not revs:
|
||||
# This can happen if someone is force pushing an older revision to a branch
|
||||
return
|
||||
|
||||
# Print the revision about to be pushed commits
|
||||
print('Pushing to "%s" on remote "%s"' % (remote_ref, args.url))
|
||||
for sha in revs:
|
||||
print(' - ' + git('show', '--oneline', '--quiet', sha))
|
||||
|
||||
if len(revs) > 1:
|
||||
if not ask_confirm('Are you sure you want to push %d commits?' % len(revs)):
|
||||
die('Aborting')
|
||||
|
||||
|
||||
for sha in revs:
|
||||
msg = git('log', '--format=%B', '-n1', sha)
|
||||
if 'Differential Revision' not in msg:
|
||||
continue
|
||||
for line in msg.splitlines():
|
||||
for tag in ['Summary', 'Reviewers', 'Subscribers', 'Tags']:
|
||||
if line.startswith(tag + ':'):
|
||||
eprint('Please remove arcanist tags from the commit message (found "%s" tag in %s)' % (tag, sha[:12]))
|
||||
if len(revs) == 1:
|
||||
eprint('Try running: llvm/utils/git/arcfilter.sh')
|
||||
die('Aborting (force push by adding "--no-verify")')
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not shutil.which('git'):
|
||||
die('error: cannot find git command')
|
||||
|
||||
argv = sys.argv[1:]
|
||||
p = argparse.ArgumentParser(
|
||||
prog='pre-push', formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=__doc__)
|
||||
verbosity_group = p.add_mutually_exclusive_group()
|
||||
verbosity_group.add_argument('-q', '--quiet', action='store_true',
|
||||
help='print less information')
|
||||
verbosity_group.add_argument('-v', '--verbose', action='store_true',
|
||||
help='print more information')
|
||||
|
||||
p.add_argument('remote', type=str, help='Name of the remote')
|
||||
p.add_argument('url', type=str, help='URL for the remote')
|
||||
|
||||
args = p.parse_args(argv)
|
||||
VERBOSE = args.verbose
|
||||
QUIET = args.quiet
|
||||
|
||||
lines = sys.stdin.readlines()
|
||||
sys.stdin = open('/dev/tty', 'r')
|
||||
for line in lines:
|
||||
local_ref, local_sha, remote_ref, remote_sha = line.split()
|
||||
handle_push(args, local_ref, local_sha, remote_ref, remote_sha)
|
Loading…
Reference in New Issue
Block a user