Node: Mac: fs.watch() not detecting changes to file from git operation

Created on 2 Feb 2016  路  8Comments  路  Source: nodejs/node

I am on Mac OS X 10.11.1 with node.js 4.1.1. I also tried with node.js 5.x:

Steps:

  • create a folder with one file in it and enable git in that folder
  • commit to master
  • create other branch, make some changes to the file and commit to other branch
  • switch between the branches while checking the output of the following script:
var fs = require("fs");

var watcher = fs.watch('<path to file>');
watcher.on('change', function(eventtype) {
    console.log(eventtype);
});

=> I get a rename event for the first time I switch to the other branch. After that, the events stop when switching branches.

fs macos

Most helpful comment

If the file gets deleted and immediately recreated, fs.watch loses it.

One workaround for this is to watch the parent directory of the file.

Something like this:

function watchFile(filepath, callback) {
    var fpath = path.resolve(filepath),
          fdir = path.dirname(fpath),
          fname = path.basename(fpath);
    fs.statSync(fdir);
    return fs.watch(fdir, {persistent: false, recursive: false}, function (event, changed_fname) {
        if (changed_fname === fname) {
            fs.stat(fpath, function (err) {
                callback && callback(null, !err, fpath);
            });
        }
    });
}

All 8 comments

fs.watch is known to have its quirks (I'm not sure why but I think it has to do with cross OS compatibility?).

chokidar is one solution from user-land.

If the file gets deleted and immediately recreated, fs.watch loses it.

One workaround for this is to watch the parent directory of the file.

Something like this:

function watchFile(filepath, callback) {
    var fpath = path.resolve(filepath),
          fdir = path.dirname(fpath),
          fname = path.basename(fpath);
    fs.statSync(fdir);
    return fs.watch(fdir, {persistent: false, recursive: false}, function (event, changed_fname) {
        if (changed_fname === fname) {
            fs.stat(fpath, function (err) {
                callback && callback(null, !err, fpath);
            });
        }
    });
}

@bpasero I think @imyller's explanation is correct. It's not a bug or issue with fs.watch.

It's just how paths and inodes work on OS X and Linux. The watch is actually watching the inode of the file and not the path of the file as far as I understand. The inode is resolved from the path of the file when the file is first watched.

When the file is deleted, the watch remains on the inode of the deleted file. When the file is recreated by git it actually gets assigned a different inode, which is why changes to this file are no longer picked up by the watch (which is still watching the old deleted inode). One can confirm this by calling stat -s filepath and comparing st_ino after creating and recreating the file.

It would be better to have a recursive watch on the root of the file tree that you're interested in as @imyller suggested. This way you'll always pick up changes to children and descendants regardless of their inodes changing. It's also less resource intensive.

Closing as this is not a bug. It may be worthwhile to have some more documentation around this, however. /cc @nodejs/documentation

@jorangreef would you like to do a PR?

@stevemao sure, working on it now.

馃憤 @jorangreef

Done: #6099

Was this page helpful?
0 / 5 - 0 ratings

Related issues

srl295 picture srl295  路  3Comments

danialkhansari picture danialkhansari  路  3Comments

mcollina picture mcollina  路  3Comments

filipesilvaa picture filipesilvaa  路  3Comments

danielstaleiny picture danielstaleiny  路  3Comments