Skip to content

Instantly share code, notes, and snippets.

@zpao
Created May 21, 2018 23:52
Show Gist options
  • Save zpao/58a70ce6739a909d326d703361eba68c to your computer and use it in GitHub Desktop.
Save zpao/58a70ce6739a909d326d703361eba68c to your computer and use it in GitHub Desktop.
Script used to generate data for https://twitter.com/zpao/status/998702031278768129
// inspired by https://github.com/tj/git-extras/blob/master/bin/git-line-summary
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const execSync = require("child_process").execSync;
const DIR = "/Users/poshannessy/FB/code/react-clean";
const REF = "origin/master";
const AUTHOR = "Paul O’Shannessy";
const PERIOD_DAYS = 1;
async function e(cmd, cwd) {
const { stdout, stderr } = await exec(cmd, { cwd: cwd });
return stdout.trim();
}
function eSync(cmd, cwd) {
let stdout = execSync(cmd, { cwd: cwd, encoding: "utf8" });
return stdout.trim();
}
async function git(cmd) {
return await e(`git ${cmd}`, DIR);
}
function formatDate(d) {
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
}
function getStartNextMonth(d) {
let rv = new Date(d);
rv.setMonth(d.getMonth() + 1, 1);
rv.setHours(0, 0, 0, 0);
return rv;
}
function getEndMonth(d) {
let rv = new Date(d);
rv.setMonth(d.getMonth() + 1, 0);
rv.setHours(23, 59, 59, 999);
return rv;
}
async function main() {
await git(`checkout ${REF}`);
let firstCommit = await git("rev-list --max-parents=0 HEAD");
let firstCommitTimestamp = await git(`show -s --format=%cI ${firstCommit}`);
let firstCommitDate = new Date(firstCommitTimestamp);
// firstCommitDate.setDate(firstCommitDate.getDate() - 1);
let lastCommitTimestamp = await git(`show -s --format=%cI ${REF}`);
let lastCommitDate = new Date(lastCommitTimestamp);
lastCommitDate.setDate(lastCommitDate.getDate() + 1);
let workingDate = new Date(firstCommitDate);
workingDate.setHours(0, 0, 0, 0);
console.log(firstCommit, firstCommitDate);
console.log(REF, lastCommitDate);
let commitsByDate = new Map();
await git(`checkout ${REF}`);
while (workingDate < lastCommitDate) {
// let endDate = new Date(workingDate);
// endDate.setDate(endDate.getDate() + PERIOD_DAYS);
let endDate = getEndMonth(workingDate);
let dayCommitRaw = await git(
`log --no-merges --since='${workingDate.toISOString()}' --until='${endDate.toISOString()}' --format='%H %cI' --reverse | head -n1`
);
let [dayCommit, dayCommitTimestamp] = dayCommitRaw.split(" ");
console.log(`${workingDate.toISOString()} - ${endDate.toISOString()}`);
if (dayCommit !== "") {
commitsByDate.set(new Date(dayCommitTimestamp), dayCommit);
}
// workingDate = endDate;
workingDate = getStartNextMonth(workingDate);
}
for (let [commitDate, commitSHA] of commitsByDate) {
await git(`checkout ${commitSHA}`);
let totalCount = 0;
let blameCount = 0;
let files = await git("ls-files");
files = files
.split("\n")
.map(s => s.trim())
.filter(f => {
try {
let typeText = eSync(`file ${f} | grep text`, DIR);
return typeText !== "";
} catch (e) {
return false;
}
});
for (file of files) {
let lines = await e(`wc -l ${file}`, DIR);
lines = parseInt(lines);
totalCount += lines;
let blameLines = await e(
`git blame --line-porcelain ${file} | grep "^author\ " | LC_ALL=C sed -n 's/^author //p' | grep '${AUTHOR}' | wc -l`,
DIR
);
blameLines = parseInt(blameLines);
blameCount += blameLines;
}
console.log(`${formatDate(commitDate)}\t${totalCount}\t${blameCount}`);
}
}
try {
main();
} catch (e) {
console.error(e);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment