Code coverage report for ggit/src/blame.js

Statements: 100% (48 / 48)      Branches: 100% (6 / 6)      Functions: 100% (7 / 7)      Lines: 100% (48 / 48)      Ignored: none     

All files » ggit/src/ » blame.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 1161 1 1 1 1   1 45 45 45 45                     1 1                               1 1     1 44 44     1 22 22     1 1                                   1 1   1 1 44 44   44     1     1 1 1   1 1   1   1 1     1 2   2 1     1 1   1   1 1     1  
require('lazy-ass');
var check = require('check-types');
var exec = require('./exec');
var path = require('path');
var fs = require('fs');
 
function linesToBlameInfo(lines) {
  la(check.array(lines), 'expected lines', lines);
  la(lines.length > 3, 'few lines in output', lines);
  la(/^author/.test(lines[1]), 'cannot find author line in', lines);
  return {
    commit: lines[0].split(' ')[0],
    author: lines[1].replace('author ', ''),
    committer: lines[5].replace('committer ', ''),
    summary: lines[9].replace('summary ', ''),
    filename: lines[10].replace('filename ', ''), // wrt repo root
    line: lines[11].trim()
  };
}
 
// from git blame --porcelain single line string output to object
function toBlameInfo(blamePorcelainOutput) {
  la(check.unemptyString(blamePorcelainOutput),
    'expected string output', blamePorcelainOutput);
  /*
    full-commit-id 73 79 1
    author Joe
    author-mail <joe@email.com>
    author-time 1401904426
    author-tz -0400
    committer Andy
    committer-mail <andy@email.com>
    committer-time 1401904426
    committer-tz -0400
    summary Blah blah blah
    filename path/to/file/from/repo/root
    // current line from file
  */
  var lines = blamePorcelainOutput.trim().split('\n');
  return linesToBlameInfo(lines);
}
 
function isUncommittedLine(line) {
  la(check.unemptyString(line), 'expected commit id line', line);
  return /^00000/.test(line);
}
 
function hasPreviousCommitId(lines) {
  la(check.array(lines), 'expected string lines', lines);
  return /^previous/.test(lines[10]);
}
 
function toBlameInfoFile(blamePorcelainOutput) {
  la(check.unemptyString(blamePorcelainOutput),
    'expected string output', blamePorcelainOutput);
  /*
    each 12 lines will have information about the commit, something like
 
    6e65f8ec5ed63cac92ed130b1246d9c23223c04e 1 1 6
    author Gleb Bahmutov
    author-mail <gleb.bahmutov@gmail.com>
    author-time 1410319209
    author-tz -0400
    committer Gleb Bahmutov
    committer-mail <gleb.bahmutov@gmail.com>
    committer-time 1410319209
    committer-tz -0400
    summary adding blame feature
    filename test/blame.js
      var blame = require('../index').blame;
  */
  var lines = blamePorcelainOutput.trim().split('\n');
  la(lines.length > 3, 'few lines in output', blamePorcelainOutput);
 
  var perLineInfo = [], lineInfo;
  while (lines.length > 0) {
    var linesPerSourceLine = isUncommittedLine(lines[0]) || hasPreviousCommitId(lines) ? 13 : 12;
    lineInfo = lines.splice(0, linesPerSourceLine);
    // console.log(lineInfo);
    perLineInfo.push(linesToBlameInfo(lineInfo));
  };
 
  return perLineInfo;
}
 
function blameOneLine(filename, lineNumber) {
  la(check.unemptyString(filename), 'missing filename');
  la(check.positiveNumber(lineNumber),
    'file', filename, 'missing line number (starting with 1)', lineNumber);
  var fullFilename = path.resolve(filename);
  la(fs.existsSync(filename), 'file', fullFilename, 'not found, based on', filename);
 
  console.log('who to blame for', fullFilename, lineNumber);
  // http://git-scm.com/docs/git-blame
  var cmd = 'git blame --porcelain -L ' + lineNumber + ',' + lineNumber + ' ' + fullFilename;
  return exec(cmd).then(toBlameInfo);
}
 
function blame(filename, lineNumber) {
  la(check.unemptyString(filename), 'missing filename');
 
  if (lineNumber) {
    return blameOneLine(filename, lineNumber);
  }
 
  var fullFilename = path.resolve(filename);
  la(fs.existsSync(filename), 'file', fullFilename, 'not found, based on', filename);
 
  console.log('who to blame for', fullFilename);
  // http://git-scm.com/docs/git-blame
  var cmd = 'git blame --porcelain --line-porcelain ' + fullFilename;
  return exec(cmd).then(toBlameInfoFile);
}
 
module.exports = blame;