guard / listen

The Listen gem listens to file modifications and notifies you about the changes.
https://rubygems.org/gems/listen
MIT License
1.92k stars 246 forks source link

Shouldn't ignore prevent symlink errors? #363

Closed lisad closed 8 years ago

lisad commented 8 years ago

I've read the helpful documentation about how symlinks cause warnings about "directory already being watched". This is noise in my guard reporting, so I investigated how to avoid the warnings. Having considered all the options, I think symlinks are pretty useful in projects (e.g. we avoid duplicating assets in our directory by symlinking those assets between the modules that need those assets, so if we change our logo it changes on the Web and in the client).

The 'ignore' feature seemed perfect, except for the clear documentation that it doesn't work, because "events are only filtered after they are fired". Why wouldn't it be considered a useful feature in 'ignore' for 'ignore' to act at an earlier level? Or that 'ignore' would filter this duplicate directory warning as well as filtering events after they are fired?

e2 commented 8 years ago

Summary: the warnings mean you need to rethink the project - mostly because of OSX limitations.

("Earlier level" here would pretty much mean hacking OSX internals - probably impossible).

Ignoring (ignore) works by just ignoring existing events.

Events themselves come from the operating system, so Guard can't really control that. (Except of course in polling, but polling is usually slow).

Basically the problem is OSX. OSX adapter (rb-fsevent) watches directories recursively, period.

So ignore works by ignoring events to avoid calling plugins/watches/blocks. directories OTOH lets you choose which directories to watch recursively.

But, if you want to watch the project root directory, on OSX you're forced to watch everything.

Some details here:

https://github.com/guard/listen/issues/342 https://github.com/guard/listen/issues/280 https://github.com/guard/listen/issues/279

lisad commented 8 years ago

I get it so far, I see why the architecture must see events and then filter them later, and thank you for the explanation. But wouldn't it work to have the warning suppressed if the "ignore" feature is turned on for the directory triggering the warning?

e2 commented 8 years ago

In case it helps, first see this: https://github.com/guard/guard/issues/811#event-513189131

The answer is "no", because of ambiguity.

If you have a/b/foo symlinked to c/d/bar, and you change a/b/foo, it's unclear what event to report:

  1. a/b/foo changed?
  2. c/d/bar changed?
  3. both changed?

And what if there are symlinks between 'a' and 'c'? Listen would have to be either inconsistent or incredibly intelligent to figure out what to report. And realpath doesn't help, because it's counter to what users expect.

Listen monitors files and not paths, while users expect paths, so Listen does some magic to "preserve" what the user watched.

There are also "filesystem loops", which could hang Listen with an infinite loop.

The best way of thinking is: "the operating system watches directory recursively (blame Apple), and Listen translates multiple events into a single 'reasonable' event'.

The reason Listen can't be smarter with symlinks is because of current OSX implementation limits.

The 'ignore' feature is a high-level one, to avoid temp files, log files, backup files - and to optimize things when polling is used.

The warning is there to protect novice users from getting stuck with extremely frustrating and hard to debug problems.

The project should be reorganized to avoid the warning, because philosophically it's the right thing to do. It's not that symlinks are "bad", it's just that watching the same physical directories multiple times makes no sense.

Yes, it's convenient to use symlinks for editing and organizing a project, but it doesn't make sense to WATCH symlinked directories. And the OSX adapter simply doesn't allow NOT WATCHING subdirectories, because it's recursive.

e2 commented 8 years ago

I'm planning to solve this with: https://github.com/guard/listen/issues/381

Basically, once Listen has an internal database of symlinks, you should be able to watch symlinks properly.