index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. var spawn = require("child_process").spawn;
  2. var fs = require("fs");
  3. var sgf = function(filter, callback) {
  4. if (typeof filter == "function") {
  5. callback = filter;
  6. filter = "ACDMRTUXB";
  7. }
  8. sgf.getHead(function(err, head) {
  9. if (err) {
  10. callback(err);
  11. } else {
  12. var command = "git -c core.quotepath=false diff-index --cached --name-status";
  13. if (filter.indexOf('R') !== -1) {
  14. command += " -M";
  15. }
  16. command += " --diff-filter=" + filter + " " + head;
  17. run(command, function(err, stdout, stderr) {
  18. if (err || stderr) {
  19. callback(err || new Error(stderr));
  20. } else {
  21. callback(null, stdoutToResultsObject(stdout));
  22. }
  23. });
  24. }
  25. });
  26. }
  27. sgf.cwd = process.cwd();
  28. sgf.debug = false;
  29. sgf.includeContent = false;
  30. // sgf.firstHead = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
  31. sgf.getHead = function(callback) {
  32. run("git rev-parse --verify HEAD", function(err, stdout, stderr) {
  33. if (err && err.message.indexOf("fatal: Needed a single revision")!==-1) {
  34. // callback(null, sgf.firstHead);
  35. run("git hash-object -t tree /dev/null", function(err, stdout, stderr) {
  36. if (err || stderr) {
  37. callback(err || new Error("STDERR: " + stderr));
  38. } else {
  39. stdout = stdout.replace("\n", "");
  40. callback(null, stdout);
  41. }
  42. });
  43. } else if (err || stderr) {
  44. callback(err || new Error("STDERR: " + stderr));
  45. } else {
  46. stdout = stdout.replace("\n", "");
  47. callback(null, stdout);
  48. }
  49. });
  50. }
  51. sgf.readFile = function(filename, options, callback) {
  52. fs.readFile(sgf.cwd + "/" + filename, options, callback);
  53. }
  54. module.exports = sgf;
  55. /** ======================================== HELPERS ======================================== **/
  56. var run = function(command, callback) {
  57. if (sgf.debug) {
  58. console.log("RUNNING: " + command);
  59. }
  60. var bits = command.split(" ");
  61. var args = bits.slice(1);
  62. var cmd = spawn(bits[0], args, {
  63. cwd: module.exports.cwd
  64. });
  65. var stdout = "";
  66. var stderr = "";
  67. cmd.stdout.on('data', function(data){
  68. stdout+=data.toString();
  69. });
  70. cmd.stderr.on('data', function(data){
  71. stderr+=data.toString();
  72. });
  73. cmd.on("close", function(code){
  74. var err = null;
  75. if(code!==0){
  76. err = new Error(stderr);
  77. }
  78. callback(err,stdout,stderr);
  79. });
  80. }
  81. var codeToStatus = function(code) {
  82. /* =======================================================================================================
  83. ** PER docs at https://git-scm.com/docs/git-diff-index
  84. ** Possible status letters are:
  85. ** A: addition of a file
  86. ** C: copy of a file into a new one
  87. ** D: deletion of a file
  88. ** M: modification of the contents or mode of a file
  89. ** R: renaming of a file
  90. ** T: change in the type of the file
  91. ** U: file is unmerged (you must complete the merge before it can be committed)
  92. ** X: "unknown" change type (most probably a bug, please report it)
  93. **
  94. ** Status letters C and R are always followed by a score
  95. ** (denoting the percentage of similarity between the source and target of the move or copy).
  96. ** Status letter M may be followed by a score (denoting the percentage of dissimilarity) for file rewrites.
  97. ** ======================================================================================================= */
  98. var map = {
  99. "A": "Added",
  100. "C": "Copied",
  101. "D": "Deleted",
  102. "M": "Modified",
  103. "R": "Renamed",
  104. "T": "Type-Change",
  105. "U": "Unmerged",
  106. "X": "Unknown",
  107. "B": "Broken"
  108. }
  109. return map[code.charAt(0)];
  110. }
  111. var stdoutToResultsObject = function(stdout) {
  112. var results = [];
  113. var lines = stdout.split("\n");
  114. var iLines = lines.length;
  115. var files_with_errors = 0;
  116. while (iLines--) {
  117. var line = lines[iLines];
  118. if (line != "") {
  119. var parts = line.split("\t");
  120. var result = {
  121. filename: parts[2] || parts[1],
  122. status: codeToStatus(parts[0])
  123. }
  124. if (sgf.includeContent) {
  125. try {
  126. result.content = fs.readFileSync(sgf.cwd + "/" + result.filename, {
  127. encoding: "utf8"
  128. });
  129. } catch (err) {
  130. result.err = err;
  131. }
  132. }
  133. results.push(result);
  134. }
  135. }
  136. return results;
  137. }