Repository: gulpjs/vinyl-fs (js)

Issue:
Use ctime for since filter?
Hi!

When `gulp.src` searches for files with `since` option, vinyl-fs compares mtime of each file with `since` value ([here](https://github.com/gulpjs/vinyl-fs/blob/master/lib/filter-since.js#L14)). But what if a file wasn't changed? 

When I just paste file to watched directory, its mtime does not change. And `gulp.src` doesn't see this file. But ctime changes. May be `filterSince` should compare ctime too? 

Pull request description:
feat!: Consider the greater of ctime & mtime when comparing since option
Closes #226

This adds `ctime` as a property to be considered when `since` filtering. The `ctime` might be changed by some file metadata change and that should be considered a file change.

Patch:
diff --git a/lib/src/prepare.js b/lib/src/prepare.js
index 0f9a16a..715cb1e 100644
--- a/lib/src/prepare.js
+++ b/lib/src/prepare.js
@@ -6,9 +6,11 @@ function prepareRead(optResolver) {
   function normalize(file, callback) {
     var since = optResolver.resolve('since', file);
 
-    // Skip this file if since option is set and current file is too old
-    if (file.stat && file.stat.mtime <= since) {
-      return callback();
+    if (file.stat) {
+      // Skip this file if since option is set and current file is too old
+      if (Math.max(file.stat.mtime, file.stat.ctime) <= since) {
+        return callback();
+      }
     }
 
     return callback(null, file);


Test patch:
diff --git a/test/src.js b/test/src.js
index 8542fc5..eec726d 100644
--- a/test/src.js
+++ b/test/src.js
@@ -9,12 +9,15 @@ var sinon = require('sinon');
 
 var vfs = require('../');
 
+var cleanup = require('./utils/cleanup');
 var testStreams = require('./utils/test-streams');
 var testConstants = require('./utils/test-constants');
 var describeStreams = require('./utils/suite');
 
 var inputBase = testConstants.inputBase;
 var inputPath = testConstants.inputPath;
+var outputBase = testConstants.outputBase;
+var outputPath = testConstants.outputPath;
 var inputDirpath = testConstants.inputDirpath;
 var bomInputPath = testConstants.bomInputPath;
 var beBomInputPath = testConstants.beBomInputPath;
@@ -27,6 +30,8 @@ var encodedContents = testConstants.encodedContents;
 var bomContents = testConstants.bomContents;
 var contents = testConstants.contents;
 
+var clean = cleanup(outputBase);
+
 describeStreams('.src()', function (stream) {
   var from = stream.Readable.from;
   var pipeline = stream.pipeline;
@@ -35,6 +40,9 @@ describeStreams('.src()', function (stream) {
   var concatArray = streamUtils.concatArray;
   var compareContents = streamUtils.compareContents;
 
+  beforeEach(clean);
+  afterEach(clean);
+
   it('throws on invalid glob (empty)', function (done) {
     var stream;
     try {
@@ -687,6 +695,34 @@ describeStreams('.src()', function (stream) {
     );
   });
 
+  it('streams a file with ctime greater than mtime', function (done) {
+    fs.mkdirSync(outputBase);
+    fs.copyFileSync(inputPath, outputPath);
+
+    var renamedPath = path.join(outputBase, 'foo.txt');
+
+    setTimeout(function () {
+      // rename changes ctime but not mtime
+      fs.renameSync(outputPath, renamedPath);
+
+      var stat = fs.statSync(renamedPath);
+
+      expect(+stat.ctime).toBeGreaterThan(+stat.mtime);
+
+      var lastMtime = new Date(+stat.mtime);
+
+      function assert(files) {
+        expect(files.length).toEqual(1);
+        expect(files[0].path).toEqual(renamedPath);
+      }
+
+      pipeline(
+        [vfs.src(renamedPath, { since: lastMtime }), concatArray(assert)],
+        done
+      );
+    }, 250);
+  });
+
   it('streams a file with streaming contents', function (done) {
     var expectedContent = fs.readFileSync(inputPath);