@@ -8,58 +8,83 @@ use IPC::Open2;
8
8
# (https://facebook.github.io/watchman/) with git to speed up detecting
9
9
# new and modified files.
10
10
#
11
- # The hook is passed a version (currently 1 ) and a time in nanoseconds
12
- # formatted as a string and outputs to stdout all files that have been
13
- # modified since the given time . Paths must be relative to the root of
14
- # the working tree and separated by a single NUL.
11
+ # The hook is passed a version (currently 2 ) and last update token
12
+ # formatted as a string and outputs to stdout a new update token and
13
+ # all files that have been modified since the update token . Paths must
14
+ # be relative to the root of the working tree and separated by a single NUL.
15
15
#
16
16
# To enable this hook, rename this file to "query-watchman" and set
17
17
# 'git config core.fsmonitor .git/hooks/query-watchman'
18
18
#
19
- my ($version , $time ) = @ARGV ;
19
+ my ($version , $last_update_token ) = @ARGV ;
20
20
21
21
# Check the hook interface version
22
-
23
- if ($version == 1) {
24
- # convert nanoseconds to seconds
25
- # subtract one second to make sure watchman will return all changes
26
- $time = int ($time / 1000000000) - 1;
27
- } else {
22
+ if ($version ne 2) {
28
23
die " Unsupported query-fsmonitor hook version '$version '.\n " .
29
24
" Falling back to scanning...\n " ;
30
25
}
31
26
32
- my $git_work_tree ;
33
- if ($^O =~ ' msys' || $^O =~ ' cygwin' ) {
34
- $git_work_tree = Win32::GetCwd();
35
- $git_work_tree =~ tr / \\/ \// ;
36
- } else {
37
- require Cwd;
38
- $git_work_tree = Cwd::cwd();
39
- }
27
+ my $git_work_tree = get_working_dir();
40
28
41
29
my $retry = 1;
42
30
31
+ my $json_pkg ;
32
+ eval {
33
+ require JSON::XS;
34
+ $json_pkg = " JSON::XS" ;
35
+ 1;
36
+ } or do {
37
+ require JSON::PP;
38
+ $json_pkg = " JSON::PP" ;
39
+ };
40
+
43
41
launch_watchman();
44
42
45
43
sub launch_watchman {
44
+ $o = watchman_query();
45
+ if (is_work_tree_watched($o )) {
46
+ output_result($o -> {clock }, @{$o -> {files }});
47
+ }
48
+ }
49
+
50
+ sub output_result {
51
+ my ($clockid , @files ) = @_ ;
52
+
53
+ binmode STDOUT , " :utf8" ;
54
+ print $clockid ;
55
+ print " \0 " ;
56
+ local $, = " \0 " ;
57
+ print @files ;
58
+ }
59
+
60
+ sub watchman_clock {
61
+ my $response = qx/ watchman clock "$git_work_tree "/ ;
62
+ die " Failed to get clock id on '$git_work_tree '.\n " .
63
+ " Falling back to scanning...\n " if $? != 0;
46
64
65
+ return $json_pkg -> new-> utf8-> decode($response );
66
+ }
67
+
68
+ sub watchman_query {
47
69
my $pid = open2(\*CHLD_OUT, \*CHLD_IN, ' watchman -j --no-pretty' )
48
- or die " open2() failed: $! \n " .
49
- " Falling back to scanning...\n " ;
70
+ or die " open2() failed: $! \n " .
71
+ " Falling back to scanning...\n " ;
50
72
51
73
# In the query expression below we're asking for names of files that
52
- # changed since $time but were not transient (ie created after
53
- # $time but no longer exist).
74
+ # changed since $last_update_token but not from the .git folder.
54
75
#
55
76
# To accomplish this, we're using the "since" generator to use the
56
77
# recency index to select candidate nodes and "fields" to limit the
57
- # output to file names only.
58
-
78
+ # output to file names only. Then we're using the "expression" term to
79
+ # further constrain the results.
80
+ if (substr ($last_update_token , 0, 1) eq " c" ) {
81
+ $last_update_token = " \" $last_update_token \" " ;
82
+ }
59
83
my $query = <<" END" ;
60
84
["query", "$git_work_tree ", {
61
- "since": $time ,
62
- "fields": ["name"]
85
+ "since": $last_update_token ,
86
+ "fields": ["name"],
87
+ "expression": ["not", ["dirname", ".git"]]
63
88
}]
64
89
END
65
90
@@ -68,24 +93,16 @@ sub launch_watchman {
68
93
my $response = do {local $/ ; <CHLD_OUT>};
69
94
70
95
die " Watchman: command returned no output.\n " .
71
- " Falling back to scanning...\n " if $response eq " " ;
96
+ " Falling back to scanning...\n " if $response eq " " ;
72
97
die " Watchman: command returned invalid output: $response \n " .
73
- " Falling back to scanning...\n " unless $response =~ / ^\{ / ;
74
-
75
- my $json_pkg ;
76
- eval {
77
- require JSON::XS;
78
- $json_pkg = " JSON::XS" ;
79
- 1;
80
- } or do {
81
- require JSON::PP;
82
- $json_pkg = " JSON::PP" ;
83
- };
84
-
85
- my $o = $json_pkg -> new-> utf8-> decode($response );
86
-
87
- if ($retry > 0 and $o -> {error } and $o -> {error } =~ m / unable to resolve root .* directory (.*) is not watched/ ) {
88
- print STDERR " Adding '$git_work_tree ' to watchman's watch list.\n " ;
98
+ " Falling back to scanning...\n " unless $response =~ / ^\{ / ;
99
+
100
+ return $json_pkg -> new-> utf8-> decode($response );
101
+ }
102
+
103
+ sub is_work_tree_watched {
104
+ my ($output ) = @_ ;
105
+ if ($retry > 0 and $output -> {error } and $output -> {error } =~ m / unable to resolve root .* directory (.*) is not watched/ ) {
89
106
$retry --;
90
107
qx/ watchman watch "$git_work_tree "/ ;
91
108
die " Failed to make watchman watch '$git_work_tree '.\n " .
@@ -95,15 +112,34 @@ sub launch_watchman {
95
112
# return the fast "everything is dirty" flag to git and do the
96
113
# Watchman query just to get it over with now so we won't pay
97
114
# the cost in git to look up each individual file.
98
- print " /\0 " ;
115
+ my $o = watchman_clock();
116
+ $error = $output -> {error };
117
+
118
+ die " Watchman: $error .\n " .
119
+ " Falling back to scanning...\n " if $error ;
120
+
121
+ output_result($o -> {clock }, (" /" ));
122
+ $last_update_token = $o -> {clock };
123
+
99
124
eval { launch_watchman() };
100
- exit 0;
125
+ return 0;
101
126
}
102
127
103
- die " Watchman: $o ->{error}.\n " .
104
- " Falling back to scanning...\n " if $o -> {error };
128
+ die " Watchman: $output ->{error}.\n " .
129
+ " Falling back to scanning...\n " if $output -> {error };
105
130
106
- binmode STDOUT , " :utf8" ;
107
- local $, = " \0 " ;
108
- print @{$o -> {files }};
131
+ return 1;
132
+ }
133
+
134
+ sub get_working_dir {
135
+ my $working_dir ;
136
+ if ($^O =~ ' msys' || $^O =~ ' cygwin' ) {
137
+ $working_dir = Win32::GetCwd();
138
+ $working_dir =~ tr / \\/ \// ;
139
+ } else {
140
+ require Cwd;
141
+ $working_dir = Cwd::cwd();
142
+ }
143
+
144
+ return $working_dir ;
109
145
}
0 commit comments