sync.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. "use strict";
  2. var modes = require('../lib/modes');
  3. module.exports = function (local, remote) {
  4. local.fetch = fetch;
  5. local.send = send;
  6. local.readRemoteRef = remote.readRef.bind(remote);
  7. local.updateRemoteRef = remote.updateRef.bind(remote);
  8. function fetch(ref, depth, callback) {
  9. if (!callback) return fetch.bind(local, ref, depth);
  10. sync(local, remote, ref, depth, callback);
  11. }
  12. function send(ref, callback) {
  13. if (!callback) return send.bind(local, ref);
  14. sync(remote, local, ref, Infinity, callback);
  15. }
  16. };
  17. // Download remote ref with depth
  18. // Make sure to use Infinity for depth on github mounts or anything that
  19. // doesn't allow shallow clones.
  20. function sync(local, remote, ref, depth, callback) {
  21. if (typeof ref !== "string") throw new TypeError("ref must be string");
  22. if (typeof depth !== "number") throw new TypeError("depth must be number");
  23. var hasCache = {};
  24. remote.readRef(ref, function (err, hash) {
  25. if (!hash) return callback(err);
  26. importCommit(hash, depth, function (err) {
  27. if (err) return callback(err);
  28. callback(null, hash);
  29. });
  30. });
  31. // Caching has check.
  32. function check(type, hash, callback) {
  33. if (typeof type !== "string") throw new TypeError("type must be string");
  34. if (typeof hash !== "string") throw new TypeError("hash must be string");
  35. if (hasCache[hash]) return callback(null, true);
  36. local.hasHash(hash, function (err, has) {
  37. if (err) return callback(err);
  38. hasCache[hash] = has;
  39. callback(null, has);
  40. });
  41. }
  42. function importCommit(hash, depth, callback) {
  43. check("commit", hash, onCheck);
  44. function onCheck(err, has) {
  45. if (err || has) return callback(err);
  46. remote.loadAs("commit", hash, onLoad);
  47. }
  48. function onLoad(err, commit) {
  49. if (!commit) return callback(err || new Error("Missing commit " + hash));
  50. var i = 0;
  51. importTree(commit.tree, onImport);
  52. function onImport(err) {
  53. if (err) return callback(err);
  54. if (i >= commit.parents.length || depth <= 1) {
  55. return local.saveAs("commit", commit, onSave);
  56. }
  57. importCommit(commit.parents[i++], depth - 1, onImport);
  58. }
  59. }
  60. function onSave(err, newHash) {
  61. if (err) return callback(err);
  62. if (newHash !== hash) {
  63. return callback(new Error("Commit hash mismatch " + hash + " != " + newHash));
  64. }
  65. hasCache[hash] = true;
  66. callback();
  67. }
  68. }
  69. function importTree(hash, callback) {
  70. check("tree", hash, onCheck);
  71. function onCheck(err, has) {
  72. if (err || has) return callback(err);
  73. remote.loadAs("tree", hash, onLoad);
  74. }
  75. function onLoad(err, tree) {
  76. if (!tree) return callback(err || new Error("Missing tree " + hash));
  77. var i = 0;
  78. var names = Object.keys(tree);
  79. onImport();
  80. function onImport(err) {
  81. if (err) return callback(err);
  82. if (i >= names.length) {
  83. return local.saveAs("tree", tree, onSave);
  84. }
  85. var name = names[i++];
  86. var entry = tree[name];
  87. if (modes.isBlob(entry.mode)) {
  88. return importBlob(entry.hash, onImport);
  89. }
  90. if (entry.mode === modes.tree) {
  91. return importTree(entry.hash, onImport);
  92. }
  93. // Skip others.
  94. onImport();
  95. }
  96. }
  97. function onSave(err, newHash) {
  98. if (err) return callback(err);
  99. if (newHash !== hash) {
  100. return callback(new Error("Tree hash mismatch " + hash + " != " + newHash));
  101. }
  102. hasCache[hash] = true;
  103. callback();
  104. }
  105. }
  106. function importBlob(hash, callback) {
  107. check("blob", hash, onCheck);
  108. function onCheck(err, has) {
  109. if (err || has) return callback(err);
  110. remote.loadAs("blob", hash, onLoad);
  111. }
  112. function onLoad(err, blob) {
  113. if (!blob) return callback(err || new Error("Missing blob " + hash));
  114. local.saveAs("blob", blob, onSave);
  115. }
  116. function onSave(err, newHash) {
  117. if (err) return callback(err);
  118. if (newHash !== hash) {
  119. return callback(new Error("Blob hash mismatch " + hash + " != " + newHash));
  120. }
  121. hasCache[hash] = true;
  122. callback();
  123. }
  124. }
  125. }