git-fs.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. "use strict";
  2. var modes = require('./modes');
  3. var defer = require('./defer');
  4. // options.encrypt(plain) -> encrypted
  5. // options.decrypt(encrypted) -> plain
  6. // options.shouldEncrypt(path) -> boolean
  7. // options.getRootTree() => hash
  8. // options.setRootTree(hash) =>
  9. module.exports = function (repo, options) {
  10. var toWrite = {};
  11. var callbacks = [];
  12. var writing = false;
  13. return {
  14. readFile: readFile,
  15. writeFile: writeFile,
  16. readDir: readDir
  17. };
  18. function readFile(path, callback) {
  19. if (!callback) return readFile.bind(null, path);
  20. // If there is a pending write for this path, pull from the cache.
  21. if (toWrite[path]) return callback(null, toWrite[path]);
  22. // Otherwise read from the persistent storage
  23. options.getRootTree(onRootTree);
  24. function onRootTree(err, hash) {
  25. if (!hash) return callback(err);
  26. repo.pathToEntry(hash, path, onEntry);
  27. }
  28. function onEntry(err, entry) {
  29. if (!entry || !modes.isBlob(entry.mode)) return callback(err);
  30. repo.loadAs("blob", entry.hash, function (err, content) {
  31. if (!content) return callback(err);
  32. if (entry.mode === modes.sym) {
  33. content = options.decrypt(content);
  34. }
  35. callback(null, content);
  36. });
  37. }
  38. }
  39. function writeFile(path, binary, callback) {
  40. if (!callback) return writeFile.bind(null, path, binary);
  41. toWrite[path] = binary;
  42. callbacks.push(callback);
  43. defer(check);
  44. }
  45. function readDir(path, callback) {
  46. if (!callback) return readDir.bind(null, path);
  47. options.getRootTree(onRootTree);
  48. function onRootTree(err, hash) {
  49. if (!hash) return callback(err);
  50. repo.pathToEntry(hash, path, onEntry);
  51. }
  52. function onEntry(err, entry) {
  53. if (!entry || entry.mode !== modes.tree) return callback(err);
  54. repo.loadAs("tree", entry.hash, onTree);
  55. }
  56. function onTree(err, tree) {
  57. if (!tree) return callback(err);
  58. callback(null, Object.keys(tree));
  59. }
  60. }
  61. function check() {
  62. if (writing || !callbacks.length) return;
  63. writing = true;
  64. options.getRootTree(onRootTree);
  65. function onRootTree(err, hash) {
  66. if (err) return callall(err);
  67. var files = pullFiles();
  68. if (hash) files.base = hash;
  69. repo.createTree(files, onNewTree);
  70. }
  71. function onNewTree(err, hash) {
  72. if (err) return callall(err);
  73. options.setRootTree(hash, onSaveRoot);
  74. }
  75. function onSaveRoot(err) {
  76. if (err) return callall(err);
  77. writing = false;
  78. callall();
  79. defer(check);
  80. }
  81. }
  82. function pullFiles() {
  83. var files = Object.keys(toWrite).map(function (path) {
  84. var content = toWrite[path];
  85. delete toWrite[path];
  86. var mode = modes.blob;
  87. if (options.shouldEncrypt && options.shouldEncrypt(path)) {
  88. mode = modes.sym;
  89. content = options.encrypt(content);
  90. }
  91. return {
  92. path: path,
  93. mode: mode,
  94. content: content
  95. };
  96. });
  97. return files;
  98. }
  99. function callall(err) {
  100. callbacks.splice(0, callbacks.length).forEach(function (callback) {
  101. callback(err);
  102. });
  103. }
  104. };