read-cache: add post-index-change hook
Add a post-index-change hook that is invoked after the index is written in do_write_locked_index(). This hook is meant primarily for notification, and cannot affect the outcome of git commands that trigger the index write. The hook is passed a flag to indicate whether the working directory was updated or not and a flag indicating if a skip-worktree bit could have changed. These flags enable the hook to optimize its response to the index change notification. Signed-off-by: Ben Peart <benpeart@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Ben Peart committed
Feb 15, 2019 at 12:59 UTC
1956ecd0ab26dea9c3ed6b9afe334101d9d12f60
7 files changed
+182
-3
Documentation/githooks.txt
+18
@@ -492,6 +492,24 @@ This hook is invoked by `git-p4 submit`. It takes no parameters and nothing
492
from standard input. Exiting with non-zero status from this script prevent
493
`git-p4 submit` from launching. Run `git-p4 submit --help` for details.
494
495
+post-index-change
496
+~~~~~~~~~~~~~~~~~
497
+
498
+This hook is invoked when the index is written in read-cache.c
499
+do_write_locked_index.
500
+
501
+The first parameter passed to the hook is the indicator for the
502
+working directory being updated. "1" meaning working directory
503
+was updated or "0" when the working directory was not updated.
504
+
505
+The second parameter passed to the hook is the indicator for whether
506
+or not the index was updated and the skip-worktree bit could have
507
+changed. "1" meaning skip-worktree bits could have been updated
508
+and "0" meaning they were not.
509
+
510
+Only one parameter should be set to "1" when the hook runs. The hook
511
+running passing "1", "1" should not be possible.
512
+
513
GIT
514
---
515
Part of the linkgit:git[1] suite
builtin/reset.c
+1
@@ -380,6 +380,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
380
int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
381
if (read_from_tree(&pathspec, &oid, intent_to_add))
382
return 1;
383
+ the_index.updated_skipworktree = 1;
384
if (!quiet && get_git_work_tree()) {
385
uint64_t t_begin, t_delta_in_ms;
386
builtin/update-index.c
+2
@@ -1071,6 +1071,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
1071
if (entries < 0)
1072
die("cache corrupted");
1073
1074
+ the_index.updated_skipworktree = 1;
1075
+
1076
/*
1077
* Custom copy of parse_options() because we want to handle
1078
* filename arguments as they come.
cache.h
+3
-1
@@ -338,7 +338,9 @@ struct index_state {
338
struct cache_time timestamp;
339
unsigned name_hash_initialized : 1,
340
initialized : 1,
341
- drop_cache_tree : 1;
341
+ drop_cache_tree : 1,
342
+ updated_workdir : 1,
343
+ updated_skipworktree : 1;
344
struct hashmap name_hash;
345
struct hashmap dir_hash;
346
struct object_id oid;
read-cache.c
+12
-2
@@ -17,6 +17,7 @@
17
#include "commit.h"
18
#include "blob.h"
19
#include "resolve-undo.h"
20
+#include "run-command.h"
21
#include "strbuf.h"
22
#include "varint.h"
23
#include "split-index.h"
@@ -2999,8 +3000,17 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
3000
if (ret)
3001
return ret;
3002
if (flags & COMMIT_LOCK)
3002
- return commit_locked_index(lock);
3003
- return close_lock_file_gently(lock);
3003
+ ret = commit_locked_index(lock);
3004
+ else
3005
+ ret = close_lock_file_gently(lock);
3006
+
3007
+ run_hook_le(NULL, "post-index-change",
3008
+ istate->updated_workdir ? "1" : "0",
3009
+ istate->updated_skipworktree ? "1" : "0", NULL);
3010
+ istate->updated_workdir = 0;
3011
+ istate->updated_skipworktree = 0;
3012
+
3013
+ return ret;
3014
}
3015
3016
static int write_split_index(struct index_state *istate,
t/t7113-post-index-change-hook.sh
new
+144
@@ -0,0 +1,144 @@
1
+#!/bin/sh
2
+
3
+test_description='post index change hook'
4
+
5
+. ./test-lib.sh
6
+
7
+test_expect_success 'setup' '
8
+ mkdir -p dir1 &&
9
+ touch dir1/file1.txt &&
10
+ echo testing >dir1/file2.txt &&
11
+ git add . &&
12
+ git commit -m "initial"
13
+'
14
+
15
+test_expect_success 'test status, add, commit, others trigger hook without flags set' '
16
+ mkdir -p .git/hooks &&
17
+ write_script .git/hooks/post-index-change <<-\EOF &&
18
+ if test "$1" -eq 1; then
19
+ echo "Invalid combination of flags passed to hook; updated_workdir is set." >testfailure
20
+ exit 1
21
+ fi
22
+ if test "$2" -eq 1; then
23
+ echo "Invalid combination of flags passed to hook; updated_skipworktree is set." >testfailure
24
+ exit 1
25
+ fi
26
+ if test -f ".git/index.lock"; then
27
+ echo ".git/index.lock exists" >testfailure
28
+ exit 3
29
+ fi
30
+ if ! test -f ".git/index"; then
31
+ echo ".git/index does not exist" >testfailure
32
+ exit 3
33
+ fi
34
+ echo "success" >testsuccess
35
+ EOF
36
+ mkdir -p dir2 &&
37
+ touch dir2/file1.txt &&
38
+ touch dir2/file2.txt &&
39
+ : force index to be dirty &&
40
+ test-tool chmtime +60 dir1/file1.txt &&
41
+ git status &&
42
+ test_path_is_file testsuccess && rm -f testsuccess &&
43
+ test_path_is_missing testfailure &&
44
+ git add . &&
45
+ test_path_is_file testsuccess && rm -f testsuccess &&
46
+ test_path_is_missing testfailure &&
47
+ git commit -m "second" &&
48
+ test_path_is_file testsuccess && rm -f testsuccess &&
49
+ test_path_is_missing testfailure &&
50
+ git checkout -- dir1/file1.txt &&
51
+ test_path_is_file testsuccess && rm -f testsuccess &&
52
+ test_path_is_missing testfailure &&
53
+ git update-index &&
54
+ test_path_is_missing testsuccess &&
55
+ test_path_is_missing testfailure &&
56
+ git reset --soft &&
57
+ test_path_is_missing testsuccess &&
58
+ test_path_is_missing testfailure
59
+'
60
+
61
+test_expect_success 'test checkout and reset trigger the hook' '
62
+ write_script .git/hooks/post-index-change <<-\EOF &&
63
+ if test "$1" -eq 1 && test "$2" -eq 1; then
64
+ echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
65
+ exit 1
66
+ fi
67
+ if test "$1" -eq 0 && test "$2" -eq 0; then
68
+ echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
69
+ exit 2
70
+ fi
71
+ if test "$1" -eq 1; then
72
+ if test -f ".git/index.lock"; then
73
+ echo "updated_workdir set but .git/index.lock exists" >testfailure
74
+ exit 3
75
+ fi
76
+ if ! test -f ".git/index"; then
77
+ echo "updated_workdir set but .git/index does not exist" >testfailure
78
+ exit 3
79
+ fi
80
+ else
81
+ echo "update_workdir should be set for checkout" >testfailure
82
+ exit 4
83
+ fi
84
+ echo "success" >testsuccess
85
+ EOF
86
+ : force index to be dirty &&
87
+ test-tool chmtime +60 dir1/file1.txt &&
88
+ git checkout master &&
89
+ test_path_is_file testsuccess && rm -f testsuccess &&
90
+ test_path_is_missing testfailure &&
91
+ test-tool chmtime +60 dir1/file1.txt &&
92
+ git checkout HEAD &&
93
+ test_path_is_file testsuccess && rm -f testsuccess &&
94
+ test_path_is_missing testfailure &&
95
+ test-tool chmtime +60 dir1/file1.txt &&
96
+ git reset --hard &&
97
+ test_path_is_file testsuccess && rm -f testsuccess &&
98
+ test_path_is_missing testfailure &&
99
+ git checkout -B test &&
100
+ test_path_is_file testsuccess && rm -f testsuccess &&
101
+ test_path_is_missing testfailure
102
+'
103
+
104
+test_expect_success 'test reset --mixed and update-index triggers the hook' '
105
+ write_script .git/hooks/post-index-change <<-\EOF &&
106
+ if test "$1" -eq 1 && test "$2" -eq 1; then
107
+ echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
108
+ exit 1
109
+ fi
110
+ if test "$1" -eq 0 && test "$2" -eq 0; then
111
+ echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
112
+ exit 2
113
+ fi
114
+ if test "$2" -eq 1; then
115
+ if test -f ".git/index.lock"; then
116
+ echo "updated_skipworktree set but .git/index.lock exists" >testfailure
117
+ exit 3
118
+ fi
119
+ if ! test -f ".git/index"; then
120
+ echo "updated_skipworktree set but .git/index does not exist" >testfailure
121
+ exit 3
122
+ fi
123
+ else
124
+ echo "updated_skipworktree should be set for reset --mixed and update-index" >testfailure
125
+ exit 4
126
+ fi
127
+ echo "success" >testsuccess
128
+ EOF
129
+ : force index to be dirty &&
130
+ test-tool chmtime +60 dir1/file1.txt &&
131
+ git reset --mixed --quiet HEAD~1 &&
132
+ test_path_is_file testsuccess && rm -f testsuccess &&
133
+ test_path_is_missing testfailure &&
134
+ git hash-object -w --stdin <dir1/file2.txt >expect &&
135
+ git update-index --cacheinfo 100644 "$(cat expect)" dir1/file1.txt &&
136
+ test_path_is_file testsuccess && rm -f testsuccess &&
137
+ test_path_is_missing testfailure &&
138
+ git update-index --skip-worktree dir1/file2.txt &&
139
+ git update-index --remove dir1/file2.txt &&
140
+ test_path_is_file testsuccess && rm -f testsuccess &&
141
+ test_path_is_missing testfailure
142
+'
143
+
144
+test_done
unpack-trees.c
+2
@@ -1637,6 +1637,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
1637
WRITE_TREE_SILENT |
1638
WRITE_TREE_REPAIR);
1639
}
1640
+
1641
+ o->result.updated_workdir = 1;
1642
discard_index(o->dst_index);
1643
*o->dst_index = o->result;
1644
} else {