code: function serverSave(filedata, user, cb) {
  
      var source = opts.source$[filedata.source];
      if (!source) return cb(log(user, 'save unknown source', filedata.source));
      var file$ = u.indexBy(source.files, 'path'); // redo on every save to avoid conflicts
      var filesToSave = [];
  
      var results = u.map(filedata.files, function(clientFile) {
  
        debug('save by %s, %s %s bytes', user, clientFile.path, clientFile.text.length);
  
        var serverFile = file$[clientFile.path];
        if (!serverFile) return log(user, 'save unknown file', clientFile.path);
  
        var serverText = serverFile.text || generator.serializeTextFragments(serverFile);
        if (serverText !== clientFile._oldtext) return log(user, 'save collision on', clientFile.path);
  
        // existence of file.text triggers new parseFragments() on reload
        serverFile.text = clientFile.text;
  
        filesToSave.push(serverFile); // side effect
  
        delete clientFile._oldtext;
        return clientFile;
      });
  
      // cannot src.put(filesToSave...) because src.put is async and reload() will delete file.text
      source.src.put(generator.serializeFiles(filesToSave), { commitMsg:user }, function(err) {
        if (err) return cb(err, results);
        cb(null, results);
      });
  
      // avoid double-reload after save when watching (or watching but cached without writethru)
      if (!source._watching || source.src.flush) {
        generator.reload();
      }
    }
docs: server file save (async) receives array of POSTed files from client detects collisions by comparing with clientFile._oldtext saves whatever it can before returning results