function(from, to, filter) {
      var t = this;

      function copy(statFrom, statTo) {
        if (statFrom.isDirectory()) {
          var p;
          if (statTo === null) {
            p = mkdir(to); 
          } else {
            p = Promise.resolve(); 
          }
          return p.then(() => readdir(from)
              .then(files => Promise.all(files.map(file => t.sync(from + "/" + file, to + "/" + file, filter)))));
          
        } else if (statFrom.isFile()) {
          return qx.Promise.resolve(filter ? filter(from, to) : true)
            .then(result => result && t.copyFile(from, to));
        }
        return undefined;
      }

      return new Promise((resolve, reject) => {
        var statFrom = null;
        var statTo = null;
        
        stat(from)
          .then(tmp => {
            statFrom = tmp;
            return stat(to)
              .then(tmp => statTo = tmp)
              .catch(err => {
                if (err.code !== "ENOENT") {
                  throw err;
                }
              });
          })
          .then(() => {
            if (!statTo || statFrom.isDirectory() != statTo.isDirectory()) {
              return t.deleteRecursive(to)
                .then(() => copy(statFrom, statTo));
            } else if (statFrom.isDirectory() || (statFrom.mtime.getTime() > statTo.mtime.getTime() || statFrom.size != statTo.size)) {
              return copy(statFrom, statTo);
            }
            return undefined;
          })
          .then(resolve)
          .catch(reject);
      });
    }
function(from, to, filter) {
      var t = this;

      function copy(statFrom, statTo) {
        if (statFrom.isDirectory()) {
          var p;
          if (statTo === null) {
            p = mkdir(to); 
          } else {
            p = Promise.resolve(); 
          }
          return p.then(() => readdir(from)
              .then(files => Promise.all(files.map(file => t.sync(from + "/" + file, to + "/" + file, filter)))));
          
        } else if (statFrom.isFile()) {
          return qx.Promise.resolve(filter ? filter(from, to) : true)
            .then(result => result && t.copyFile(from, to));
        }
        return undefined;
      }

      return new Promise((resolve, reject) => {
        var statFrom = null;
        var statTo = null;
        
        stat(from)
          .then(tmp => {
            statFrom = tmp;
            return stat(to)
              .then(tmp => statTo = tmp)
              .catch(err => {
                if (err.code !== "ENOENT") {
                  throw err;
                }
              });
          })
          .then(() => {
            if (!statTo || statFrom.isDirectory() != statTo.isDirectory()) {
              return t.deleteRecursive(to)
                .then(() => copy(statFrom, statTo));
            } else if (statFrom.isDirectory() || (statFrom.mtime.getTime() > statTo.mtime.getTime() || statFrom.size != statTo.size)) {
              return copy(statFrom, statTo);
            }
            return undefined;
          })
          .then(resolve)
          .catch(reject);
      });
    }
function ( from , to , filter ) { var t = this ; function copy ( statFrom , statTo ) { if ( statFrom . isDirectory ( ) ) { var p ; if ( statTo === null ) { p = mkdir ( to ) ; } else { p = Promise . resolve ( ) ; } return p . then ( ( ) => readdir ( from ) . then ( files => Promise . all ( files . map ( file => t . sync ( from + "/" + file , to + "/" + file , filter ) ) ) ) ) ; } else if ( statFrom . isFile ( ) ) { return qx . Promise . resolve ( filter ? filter ( from , to ) : true ) . then ( result => result && t . copyFile ( from , to ) ) ; } return undefined ; } return new Promise ( ( resolve , reject ) => { var statFrom = null ; var statTo = null ; stat ( from ) . then ( tmp => { statFrom = tmp ; return stat ( to ) . then ( tmp => statTo = tmp ) . catch ( err => { if ( err . code !== "ENOENT" ) { throw err ; } } ) ; } ) . then ( ( ) => { if ( ! statTo || statFrom . isDirectory ( ) != statTo . isDirectory ( ) ) { return t . deleteRecursive ( to ) . then ( ( ) => copy ( statFrom , statTo ) ) ; } else if ( statFrom . isDirectory ( ) || ( statFrom . mtime . getTime ( ) > statTo . mtime . getTime ( ) || statFrom . size != statTo . size ) ) { return copy ( statFrom , statTo ) ; } return undefined ; } ) . then ( resolve ) . catch ( reject ) ; } ) ; }
Synchronises two files or folders; files are copied from/to but only if their
modification time or size has changed.
@param from {String} path to copy from
@param to {String} path to copy to
@param filter {Function?} optional filter method to validate filenames before sync
@async
Synchronises two files or folders ; files are copied from / to but only if their modification time or size has changed .