Skip to content

Commit 3c8d3ad

Browse files
rjarrygitster
authored andcommitted
send-email: export patch counters in validate environment
When sending patch series (with a cover-letter or not) sendemail-validate is called with every email/patch file independently from the others. When one of the patches depends on a previous one, it may not be possible to use this hook in a meaningful way. A hook that wants to check some property of the whole series needs to know which patch is the final one. Expose the current and total number of patches to the hook via the GIT_SENDEMAIL_PATCH_COUNTER and GIT_SENDEMAIL_PATCH_TOTAL environment variables so that both incremental and global validation is possible. Sharing any other state between successive invocations of the validate hook must be done via external means. For example, by storing it in a git config sendemail.validateWorktree entry. Add a sample script with placeholder validations and update tests to check that the counters are properly exported. Suggested-by: Phillip Wood <[email protected]> Signed-off-by: Robin Jarry <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9857273 commit 3c8d3ad

File tree

4 files changed

+146
-1
lines changed

4 files changed

+146
-1
lines changed

Documentation/githooks.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,28 @@ the name of the file that holds the e-mail to be sent. Exiting with a
600600
non-zero status causes `git send-email` to abort before sending any
601601
e-mails.
602602

603+
The following environment variables are set when executing the hook.
604+
605+
`GIT_SENDEMAIL_FILE_COUNTER`::
606+
A 1-based counter incremented by one for every file holding an e-mail
607+
to be sent (excluding any FIFOs). This counter does not follow the
608+
patch series counter scheme. It will always start at 1 and will end at
609+
GIT_SENDEMAIL_FILE_TOTAL.
610+
611+
`GIT_SENDEMAIL_FILE_TOTAL`::
612+
The total number of files that will be sent (excluding any FIFOs). This
613+
counter does not follow the patch series counter scheme. It will always
614+
be equal to the number of files being sent, whether there is a cover
615+
letter or not.
616+
617+
These variables may for instance be used to validate patch series.
618+
619+
The sample `sendemail-validate` hook that comes with Git checks that all sent
620+
patches (excluding the cover letter) can be applied on top of the upstream
621+
repository default branch without conflicts. Some placeholders are left for
622+
additional validation steps to be performed after all patches of a given series
623+
have been applied.
624+
603625
fsmonitor-watchman
604626
~~~~~~~~~~~~~~~~~~
605627

git-send-email.perl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,11 +795,26 @@ sub is_format_patch_arg {
795795
@files = handle_backup_files(@files);
796796

797797
if ($validate) {
798+
# FIFOs can only be read once, exclude them from validation.
799+
my @real_files = ();
798800
foreach my $f (@files) {
799801
unless (-p $f) {
800-
validate_patch($f, $target_xfer_encoding);
802+
push(@real_files, $f);
801803
}
802804
}
805+
806+
# Run the loop once again to avoid gaps in the counter due to FIFO
807+
# arguments provided by the user.
808+
my $num = 1;
809+
my $num_files = scalar @real_files;
810+
$ENV{GIT_SENDEMAIL_FILE_TOTAL} = "$num_files";
811+
foreach my $r (@real_files) {
812+
$ENV{GIT_SENDEMAIL_FILE_COUNTER} = "$num";
813+
validate_patch($r, $target_xfer_encoding);
814+
$num += 1;
815+
}
816+
delete $ENV{GIT_SENDEMAIL_FILE_COUNTER};
817+
delete $ENV{GIT_SENDEMAIL_FILE_TOTAL};
803818
}
804819

805820
if (@files) {

t/t9001-send-email.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2326,6 +2326,37 @@ test_expect_success $PREREQ 'invoke hook' '
23262326
)
23272327
'
23282328

2329+
expected_file_counter_output () {
2330+
total=$1
2331+
count=0
2332+
while test $count -ne $total
2333+
do
2334+
count=$((count + 1)) &&
2335+
echo "$count/$total" || return
2336+
done
2337+
}
2338+
2339+
test_expect_success $PREREQ '--validate hook allows counting of messages' '
2340+
test_when_finished "rm -rf my-hooks.log" &&
2341+
test_config core.hooksPath "my-hooks" &&
2342+
mkdir -p my-hooks &&
2343+
2344+
write_script my-hooks/sendemail-validate <<-\EOF &&
2345+
num=$GIT_SENDEMAIL_FILE_COUNTER &&
2346+
tot=$GIT_SENDEMAIL_FILE_TOTAL &&
2347+
echo "$num/$tot" >>my-hooks.log || exit 1
2348+
EOF
2349+
2350+
>my-hooks.log &&
2351+
expected_file_counter_output 4 >expect &&
2352+
git send-email \
2353+
--from="Example <[email protected]>" \
2354+
2355+
--smtp-server="$(pwd)/fake.sendmail" \
2356+
--validate -3 --cover-letter --force &&
2357+
test_cmp expect my-hooks.log
2358+
'
2359+
23292360
test_expect_success $PREREQ 'test that send-email works outside a repo' '
23302361
nongit git send-email \
23312362
--from="Example <[email protected]>" \
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/sh
2+
3+
# An example hook script to validate a patch (and/or patch series) before
4+
# sending it via email.
5+
#
6+
# The hook should exit with non-zero status after issuing an appropriate
7+
# message if it wants to prevent the email(s) from being sent.
8+
#
9+
# To enable this hook, rename this file to "sendemail-validate".
10+
#
11+
# By default, it will only check that the patch(es) can be applied on top of
12+
# the default upstream branch without conflicts in a secondary worktree. After
13+
# validation (successful or not) of the last patch of a series, the worktree
14+
# will be deleted.
15+
#
16+
# The following config variables can be set to change the default remote and
17+
# remote ref that are used to apply the patches against:
18+
#
19+
# sendemail.validateRemote (default: origin)
20+
# sendemail.validateRemoteRef (default: HEAD)
21+
#
22+
# Replace the TODO placeholders with appropriate checks according to your
23+
# needs.
24+
25+
validate_cover_letter () {
26+
file="$1"
27+
# TODO: Replace with appropriate checks (e.g. spell checking).
28+
true
29+
}
30+
31+
validate_patch () {
32+
file="$1"
33+
# Ensure that the patch applies without conflicts.
34+
git am -3 "$file" || return
35+
# TODO: Replace with appropriate checks for this patch
36+
# (e.g. checkpatch.pl).
37+
true
38+
}
39+
40+
validate_series () {
41+
# TODO: Replace with appropriate checks for the whole series
42+
# (e.g. quick build, coding style checks, etc.).
43+
true
44+
}
45+
46+
# main -------------------------------------------------------------------------
47+
48+
if test "$GIT_SENDEMAIL_FILE_COUNTER" = 1
49+
then
50+
remote=$(git config --default origin --get sendemail.validateRemote) &&
51+
ref=$(git config --default HEAD --get sendemail.validateRemoteRef) &&
52+
worktree=$(mktemp --tmpdir -d sendemail-validate.XXXXXXX) &&
53+
git worktree add -fd --checkout "$worktree" "refs/remotes/$remote/$ref" &&
54+
git config --replace-all sendemail.validateWorktree "$worktree"
55+
else
56+
worktree=$(git config --get sendemail.validateWorktree)
57+
fi || {
58+
echo "sendemail-validate: error: failed to prepare worktree" >&2
59+
exit 1
60+
}
61+
62+
unset GIT_DIR GIT_WORK_TREE
63+
cd "$worktree" &&
64+
65+
if grep -q "^diff --git " "$1"
66+
then
67+
validate_patch "$1"
68+
else
69+
validate_cover_letter "$1"
70+
fi &&
71+
72+
if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL"
73+
then
74+
git config --unset-all sendemail.validateWorktree &&
75+
trap 'git worktree remove -ff "$worktree"' EXIT &&
76+
validate_series
77+
fi

0 commit comments

Comments
 (0)