From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21022 invoked by alias); 15 Aug 2014 15:02:20 -0000 Mailing-List: contact gdb-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-owner@sourceware.org Received: (qmail 21006 invoked by uid 89); 15 Aug 2014 15:02:19 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.4 required=5.0 tests=AWL,BAYES_00,RP_MATCHES_RCVD,SPF_HELO_PASS,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Fri, 15 Aug 2014 15:02:13 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s7FF24Le019766 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 15 Aug 2014 11:02:04 -0400 Received: from blade.nx (ovpn-116-116.ams2.redhat.com [10.36.116.116]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s7FF22Qw025099; Fri, 15 Aug 2014 11:02:03 -0400 Received: by blade.nx (Postfix, from userid 1000) id 525022640D0; Fri, 15 Aug 2014 16:02:02 +0100 (BST) Date: Fri, 15 Aug 2014 15:02:00 -0000 From: Gary Benson To: Joel Brobecker Cc: Mike Frysinger , gdb@sourceware.org, Andreas Arnez Subject: Re: ChangeLogs in commit messages Message-ID: <20140815150202.GA5674@blade.nx> References: <20140814083231.GA6283@blade.nx> <6036430.RnprRWgZmF@vapier> <20140814131206.GA12746@blade.nx> <20140814132939.GH4924@adacore.com> <20140815084819.GB30130@blade.nx> <20140815121102.GB6019@adacore.com> <20140815130913.GA1954@blade.nx> <20140815132816.GC6019@adacore.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20140815132816.GC6019@adacore.com> X-IsSubscribed: yes X-SW-Source: 2014-08/txt/msg00054.txt.bz2 Joel Brobecker wrote: > > Does anybody have any experience writing such checks? Or, does > > anybody know of any project that already uses such checks? I can > > look into doing it myself (it's a pre-receive hook, right?) > > I have loads of experience writing git hooks, but none with > this repository's implementation. > > I would typically adjust the "update" hook for that, but it looks > like the "pre-receive" hook would also work. I've put together a quick pre-receive hook (inlined below). Each received commit on the "master" branch that touches the "gdb" subdirectory gets its message checked. The check itself is fairly cursory: it splits the message using the "YYYY-MM-DD NAME " headers, checks each is preceeded by a path starting with "gdb/" and ending with "/", and checks each is followed by more "NAME " lines, blank lines, or lines starting with tab. I don't know how comprehensive we want to be here as the message should already have been checked over by the reviewer. I've never done anything server-side with git before, so there may well be things I'm missing here. I was mainly experimenting to see how difficult this all was :) Cheers, Gary -- #!/usr/bin/env python import re import subprocess import sys GIT = "/usr/bin/git" SHA1HASH = re.compile(r"^[0-9a-f]{40}$", re.IGNORECASE) name_mail = r".* <[^@]+@[^>]+>$" CL_HDR_A = re.compile(r"^\d{4}-\d{2}-\d{2} " + name_mail) CL_HDR_B = re.compile(r"^\s+" + name_mail) class MessageChecker: def __init__(self, rev): self.rev = rev def check(self, condition, msg): if not condition: print "error: %s: bad commit message" % self.rev print "error: %s" % msg sys.exit(1) def check_message(self, lines): self.check(len(lines) > 2, "message is too short") self.check(lines[1] == "", "second line should be blank") splits = [index for index in xrange(2, len(lines)) if CL_HDR_A.match(lines[index]) is not None] self.check(splits, "no ChangeLog entries found") for start, limit in zip(splits, splits[1:] + [0]): start -= 1 limit -= 1 self.check_changelog(lines[start:limit]) def check_changelog(self, lines): self.check(len(lines) > 2, "ChangeLog entry is too short") path = lines.pop(0) self.check(path.startswith("gdb/") and path.endswith("/"), "each ChangeLog entry should be preceeded by its path") lines.pop(0) # lines[1] has already been checked while lines and CL_HDR_B.match(lines[0]) is not None: lines.pop(0) self.check(lines, "only ChangeLog headers found (no data)") for line in lines: self.check(not line or line.startswith("\t"), "bad ChangeLog line %s" % repr(line)) def check_hash(hash): assert SHA1HASH.match(hash) is not None def git(*command): fp = subprocess.Popen((GIT,) + command, stdout=subprocess.PIPE) output = fp.communicate()[0] if fp.returncode: sys.exit(1) return output def git_rev_list(oldrev, newrev): check_hash(oldrev) check_hash(newrev) return git("rev-list", oldrev + ".." + newrev) def git_diff_tree(rev): check_hash(rev) return git("diff-tree", rev) def git_cat_file_commit(rev): check_hash(rev) return git("cat-file", "commit", rev) def commit_touches_gdb(rev): lines = git_diff_tree(rev).rstrip().split("\n") check = lines.pop(0) assert check == rev for line in lines: if line.split()[-1] == "gdb": return True return False def check_commit(rev): lines = git_cat_file_commit(rev).rstrip().split("\n") while lines and lines[0]: lines.pop(0) assert lines lines.pop(0) MessageChecker(rev).check_message(lines) def process_pack(oldrev, newrev, refname): if refname == "refs/heads/master": revs = git_rev_list(oldrev, newrev).rstrip().split("\n") revs.reverse() for rev in revs: if commit_touches_gdb(rev): check_commit(rev) print "%s: ok" % rev def main(): for line in sys.stdin.xreadlines(): bits = line.rstrip().split() assert len(bits) == 3 process_pack(*bits) if __name__ == "__main__": main()