Writing a Pre-Push Hook in Git to Grep All Files for Regex Want to Reject Push If Regex Not Found

How to check that git commits contain string pattern in message?

In a pre-receive or update hook, you are given—on stdin, or as arguments—a reference name, the old value of the reference, and the new value of the reference. The update hook gets one reference, as arguments. You can make a single decision about the single reference update based on those three arguments: to allow it, or to prevent it. The pre-receive hook gets all the proposed updates, one per line, on its standard input, and you must read all of standard input before making your one decision: to allow all of the proposed updates to pass through to the next step—which includes running the update hook, one name at a time—or to deny the update as a whole.

In all cases, each reference uses its full, spelled-out format. Be sure to use all of it, so that you do not mistake tag X, refs/tags/X, for the different reference refs/heads/X that names branch X. Similarly, do not confuse branch name refs/heads/experiment/master with refs/heads/master, which would happen if you simply stripped off everything up through (i.e., including) the final /. You may even get a request to update a reference that is neither a branch name nor a tag name, such as a refs/replace/ name-space item, or refs/notes/commits.

Similarly, the old value and new value are two complete Git hash IDs, except that exactly one—never both—may be the special all-zeros "null hash". A null hash indicates that the reference is either being created (old value null, new value non-null) or destroyed (old value non-null, new value null).

Looking at commits

If you wish to look at all the incoming commits' messages, you must iterate over each commit in $oldhash..$newhash, except that you must handle "reference being created" or "reference being destroyed" differently. When a reference is being created, it's sometimes impossible to know which commit(s), if any are coming in because of that new reference. Consider for instance the case where your pre-receive hook gets:

012...789 aaa...aaa refs/heads/br1
345...eee 321...def refs/heads/br2
000...000 999...999 refs/tags/v2.1

Now, refs/tags/v2.1 is being created (its old value is all-zeros). Its new value is 999..., which is presumably either an annotated tag object or a commit object. But what if 999... reaches a commit whose parent is 888... and 888... was not in the repository before? Then the newly created tag is perhaps the source of commit 888.... On the other hand, the two branch names br1 and br2 are being changed as well. What if one of them reaches 888... as well? (E.g., perhaps that's the grandparent commit of aaa....)

The nearest you can get to "commits introduced by new reference" is to use git rev-list <hash> --not --all to get a list of commits reachable by the hash ID, that are not already reachable from all existing references. That would count new commit 888... as reachable from the tag, even if it's also going to be reachable from br1 (provided you allow the update to br1 of course).

For your case, this may be fine: if you reject the commit for having a bad message on br1, you can also reject the commit for having a bad message on v1.2, meaning you reject the commit twice, but so what? Or it may not be fine, in which case, you must decide how you wish to handle it. Just remember that your options are to read all updates in a pre-receive hook, and allow them all to proceed or reject them all; or to check each update one at a time in an update hook, and allow that one update to proceed, or to reject it. In the pre-receive hook, since you have a global view of all updates, you can write fancy logic. In an update hook, you have a narrow view, and you cannot write fancy logic: you must keep it simple. Each has its advantages and disadvantages.

In any case, once you have an update, you can tell which commits will be reachable after the update that are not yet reachable:

git rev-list $newvalue ^$oldvalue | ...

and you can tell which commits were reachable from the old reference value that will no longer be reachable:

git rev-list $oldvalue ^$newvalue | ...

(the latter are commits that are being removed via force-push). In the ... section of the code, you can read each commit hash and do whatever you like with it, such as:

... | while read hash; do
if git log --format=%B $hash | grep "$expr" >/dev/null; then
# grep found a pattern in the body printed by %B
else
# grep did not find the pattern
fi
done

Can clang-format tell me if formatting changes are necessary?

Starting with clang-format-10, you can use the --dry-run and -Werror command line options. They will cause ClangFormat to output any formatting violations to stdout and return a non-zero exit status if any input file was not correctly formatted.

$ clang-format --dry-run --Werror foo.cpp
foo.cpp:129:23: error: code should be clang-formatted [-Wclang-format-violations]
if (rc <= 0) {
$ echo $?
1

Originally from my website here: https://rigtorp.se/notes/clang-format/



Related Topics



Leave a reply



Submit