]> git.ipfire.org Git - thirdparty/git.git/commitdiff
fsmonitor-watchman: fix variable reference and remove redundant code
authorPaul Tarjan <github@paulisageek.com>
Sat, 28 Feb 2026 17:37:57 +0000 (17:37 +0000)
committerJunio C Hamano <gitster@pobox.com>
Mon, 2 Mar 2026 16:15:18 +0000 (08:15 -0800)
The is_work_tree_watched() function in fsmonitor-watchman.sample has
two bugs:

1. Wrong variable in error check: After calling watchman_clock(), the
   result is stored in $o, but the code checks $output->{error} instead
   of $o->{error}. This means errors from the clock command are silently
   ignored.

2. Double output violates protocol: When the retry path triggers (the
   directory wasn't initially watched), output_result() is called with
   the "/" flag, then launch_watchman() is called recursively which
   calls output_result() again. This outputs two clock tokens to stdout,
   but git's fsmonitor v2 protocol expects exactly one response.

Fix #1 by checking $o->{error} after watchman_clock().

Fix #2 by removing the recursive launch_watchman() call. The "/"
"everything is dirty" flag already tells git to do a full scan, and
git will call the hook again on the next invocation with a valid clock
token.

With the recursive call removed, the $retry guard is no longer needed
since it only existed to prevent infinite recursion. Remove it.

Apply the same fixes to the test helper scripts in t/t7519/.

Signed-off-by: Paul Tarjan <github@paulisageek.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
t/t7519/fsmonitor-watchman
t/t7519/fsmonitor-watchman-v2
templates/hooks/fsmonitor-watchman.sample

index 264b9daf834ec83685c9388f03b78288d605e548..bcc055c1e0945c62d62e0ce7912d4aa1688c8191 100755 (executable)
@@ -38,8 +38,6 @@ if ($^O =~ 'msys' || $^O =~ 'cygwin') {
        $git_work_tree = Cwd::cwd();
 }
 
-my $retry = 1;
-
 launch_watchman();
 
 sub launch_watchman {
@@ -92,9 +90,8 @@ sub launch_watchman {
 
        my $o = $json_pkg->new->utf8->decode($response);
 
-       if ($retry > 0 and $o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
+       if ($o->{error} and $o->{error} =~ m/unable to resolve root .* directory (.*) is not watched/) {
                print STDERR "Adding '$git_work_tree' to watchman's watch list.\n";
-               $retry--;
                qx/watchman watch "$git_work_tree"/;
                die "Failed to make watchman watch '$git_work_tree'.\n" .
                    "Falling back to scanning...\n" if $? != 0;
@@ -109,7 +106,6 @@ sub launch_watchman {
                close $fh;
 
                print "/\0";
-               eval { launch_watchman() };
                exit 0;
        }
 
index 14ed0aa42de0f291c0f696922110e70544c3dae2..368604c278d46e6ccb149928ad9c447379cde29b 100755 (executable)
@@ -29,8 +29,6 @@ if ($version ne 2) {
 
 my $git_work_tree = get_working_dir();
 
-my $retry = 1;
-
 my $json_pkg;
 eval {
        require JSON::XS;
@@ -122,8 +120,7 @@ sub watchman_query {
 sub is_work_tree_watched {
        my ($output) = @_;
        my $error = $output->{error};
-       if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
-               $retry--;
+       if ($error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
                my $response = qx/watchman watch "$git_work_tree"/;
                die "Failed to make watchman watch '$git_work_tree'.\n" .
                    "Falling back to scanning...\n" if $? != 0;
@@ -141,15 +138,12 @@ sub is_work_tree_watched {
                # Watchman query just to get it over with now so we won't pay
                # the cost in git to look up each individual file.
                my $o = watchman_clock();
-               $error = $output->{error};
+               $error = $o->{error};
 
                die "Watchman: $error.\n" .
                "Falling back to scanning...\n" if $error;
 
                output_result($o->{clock}, ("/"));
-               $last_update_token = $o->{clock};
-
-               eval { launch_watchman() };
                return 0;
        }
 
index 23e856f5deeb7f564afc22f2beed54449c2d3afb..429e0a51c12efcc3dd2cdad7e7fc1ec971c9b739 100755 (executable)
@@ -29,8 +29,6 @@ if ($version ne 2) {
 
 my $git_work_tree = get_working_dir();
 
-my $retry = 1;
-
 my $json_pkg;
 eval {
        require JSON::XS;
@@ -123,8 +121,7 @@ sub watchman_query {
 sub is_work_tree_watched {
        my ($output) = @_;
        my $error = $output->{error};
-       if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
-               $retry--;
+       if ($error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
                my $response = qx/watchman watch "$git_work_tree"/;
                die "Failed to make watchman watch '$git_work_tree'.\n" .
                    "Falling back to scanning...\n" if $? != 0;
@@ -142,15 +139,12 @@ sub is_work_tree_watched {
                # Watchman query just to get it over with now so we won't pay
                # the cost in git to look up each individual file.
                my $o = watchman_clock();
-               $error = $output->{error};
+               $error = $o->{error};
 
                die "Watchman: $error.\n" .
                "Falling back to scanning...\n" if $error;
 
                output_result($o->{clock}, ("/"));
-               $last_update_token = $o->{clock};
-
-               eval { launch_watchman() };
                return 0;
        }