Skip to content

Instantly share code, notes, and snippets.

@austil
Last active November 5, 2019 21:00
Show Gist options
  • Save austil/a69e006a37404a527c7b5cdf25d582a9 to your computer and use it in GitHub Desktop.
Save austil/a69e006a37404a527c7b5cdf25d582a9 to your computer and use it in GitHub Desktop.
Commit count by tag & author per week in CSV
year week feat fix docs style refactor test chore Dev 1 Dev 2 Dev 3 Dev 4 Dev 5 Dev 6 Dev 7 Dev 8 Dev 9 Dev 10 Dev 11 Dev 12
2016 43 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0
2016 44 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 4 0 0 0
2016 45 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 3 0 0 0
2016 51 2 0 0 2 1 0 0 0 0 0 0 0 0 0 0 5 0 0 0
2016 52 5 1 0 0 1 0 0 0 0 0 0 0 0 0 0 7 0 0 0
2016 53 0 1 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0
2017 1 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0
2017 2 1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 4 0 0 0
2017 3 1 0 0 1 1 0 1 0 0 0 0 0 0 0 0 4 0 0 0
2017 4 4 2 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0
2017 5 6 0 0 0 0 0 4 0 0 0 0 0 0 0 0 10 0 0 0
2017 6 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 2
2017 7 0 1 0 0 1 0 1 0 0 0 0 0 0 0 0 3 0 0 0
2017 8 1 2 0 0 1 1 2 0 0 0 0 0 0 0 0 7 0 0 0
2017 11 1 1 0 0 0 0 2 0 0 0 0 0 0 0 0 4 0 0 0
2017 15 4 1 0 0 0 0 2 0 0 0 0 0 0 0 0 7 0 0 0
2017 16 0 2 0 0 0 0 1 0 0 0 0 0 0 0 0 3 0 0 0
2017 18 3 2 0 0 0 0 1 0 0 0 0 0 0 0 0 6 0 0 0
2017 19 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0
2017 20 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0
2017 22 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0
2017 23 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0
2017 24 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
2017 25 2 1 0 0 0 0 1 0 0 0 0 0 0 0 0 4 0 0 0
2017 26 1 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 1 3 0
2017 27 0 3 0 0 0 0 1 0 0 0 0 0 0 0 0 3 1 0 0
2017 28 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 0
2017 29 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0
2017 30 0 0 0 0 1 3 1 0 0 0 0 0 0 0 0 5 0 0 0
2017 31 2 1 0 0 0 1 1 0 0 0 0 0 0 0 0 5 0 0 0
2017 36 0 1 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 0
2017 38 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
2017 40 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
2017 41 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
2017 43 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
2017 51 3 0 0 0 1 0 1 0 0 0 0 0 0 5 0 0 0 0 0
2017 52 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
2018 3 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
2018 4 0 1 0 0 0 0 2 0 0 0 0 3 0 0 0 0 0 0 0
2018 6 0 0 0 0 0 0 26 0 0 0 0 26 0 0 0 0 0 0 0
2018 7 0 0 0 0 0 0 5 0 0 0 0 5 0 0 0 0 0 0 0
2018 10 0 0 0 0 0 0 15 0 0 0 0 15 0 0 0 0 0 0 0
2018 11 1 0 0 0 0 0 15 0 0 0 0 15 1 0 0 0 0 0 0
2018 12 0 0 0 0 0 0 4 0 0 0 0 4 0 0 0 0 0 0 0
2018 14 0 0 0 0 0 0 4 0 0 0 0 4 0 0 0 0 0 0 0
2018 15 0 2 0 0 11 3 11 0 0 0 0 25 2 0 0 0 0 0 0
2018 16 0 0 0 0 7 0 0 0 0 0 0 7 0 0 0 0 0 0 0
2018 17 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 0
2018 23 0 0 0 0 9 0 0 0 0 0 0 9 0 0 0 0 0 0 0
2018 24 0 0 0 0 9 0 0 0 0 0 0 9 0 0 0 0 0 0 0
2018 26 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 0
2018 27 0 0 0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0
2018 28 0 4 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 0 0
2018 29 0 6 0 0 0 0 4 8 2 0 0 0 0 0 0 0 0 0 0
2018 31 0 5 0 0 0 0 2 3 0 1 3 0 0 0 0 0 0 0 0
2018 40 1 2 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0
2018 41 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
2018 49 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0
2018 51 0 3 0 0 0 0 1 3 1 0 0 0 0 0 0 0 0 0 0
2019 2 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
2019 6 0 10 0 0 0 0 1 11 0 0 0 0 0 0 0 0 0 0 0
2019 7 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
2019 9 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
year week feat fix docs style refactor test chore Dev 1 Dev 2 Dev 3
2017 47 13 1 0 0 3 0 0 0 17 0
2017 48 6 1 0 0 4 0 1 1 11 0
2017 49 2 0 3 1 4 0 0 4 6 0
2017 50 0 0 0 0 0 0 6 0 6 0
2017 51 3 2 0 0 2 0 0 1 6 0
2017 52 1 0 1 0 1 1 0 1 3 0
2018 1 2 0 0 1 4 0 4 6 5 0
2018 3 2 0 0 2 0 0 1 5 0 0
2018 4 2 1 0 0 0 0 2 1 0 4
2018 7 0 1 0 0 0 0 1 1 0 1
2018 8 7 7 0 1 1 0 12 8 16 4
2018 9 1 3 1 0 2 0 16 2 21 0
2018 10 0 1 0 0 1 1 0 0 3 0
2018 11 0 6 1 0 0 1 1 0 9 0
2018 12 0 1 0 0 1 0 1 0 3 0
2018 13 0 1 0 0 0 0 0 0 1 0
2018 16 3 0 0 0 1 0 1 4 0 1
2018 19 1 0 0 0 0 0 0 1 0 0
2018 20 1 0 0 0 0 0 0 1 0 0
2018 21 0 0 0 0 0 0 1 0 1 0
2018 23 1 1 1 0 0 0 0 0 2 1
2018 24 3 3 0 0 0 1 1 0 3 5
2018 25 0 1 0 0 0 0 0 0 0 1
2018 41 1 0 0 0 0 0 2 3 0 0
2018 42 1 0 0 0 0 0 0 0 0 1
2018 50 0 0 0 0 1 0 0 1 0 0
2018 51 1 0 0 0 0 0 0 1 0 0
2019 1 0 0 0 0 1 0 0 1 0 0
2019 2 0 0 0 0 0 0 3 3 0 0
2019 14 1 1 0 0 5 0 6 6 7 0
2019 18 0 0 0 1 0 0 3 4 0 0
/**
* Summarize a git repos logs as a CSV file.
* This script was made for a viz, you can find it at
* https://observablehq.com/@austil/project-summary-git-commit-by-type-per-week
*
* First : export your log in a file using `git log --pretty=format:"%h%x09%an%x09%ad%x09%s" > git-log.txt`
* Then process it with this script `node gitLogSummary.js git-log.txt`
* and save it in a csv : `node gitLogSummary.js git-log.txt --csvOnly > git-log.csv`
*/
// TWEAK THIS TO YOUR NEEDS
const COMMIT_TAGS = ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore'];
const COMMIT_TAG_REGEX = /^\w*(?=(:|\())/;
const RENAMED_AUTHORS = {
'Dev X': 'Others',
'Dev Y': 'Others',
'austil': 'alacour'
};
const IGNORED_AUTHORS = ['alacour'];
// Thx to https://stackoverflow.com/a/19910622
const getWeek = (date) => {
var onejan = new Date(date.getFullYear(),0,1);
var millisecsInDay = 86400000;
return Math.ceil((((date - onejan) /millisecsInDay) + onejan.getDay()+1)/7);
};
const flatten = (arr) => [].concat(...arr);
// Let's start
const fs = require('fs');
const [ , , logFile, flag] = process.argv;
const humanLog = msg => {
if(flag !== '--csvOnly') {
console.log(msg);
}
};
let gitLogs = fs.readFileSync(logFile, 'utf8')
.split('\n')
.map(l => {
const log = l.split('\t');
return {
'author': RENAMED_AUTHORS[log[1]] || log[1],
'date': new Date(log[2]),
'msg': log[3]
};
});
humanLog(`raw log : ${gitLogs.length} commits`);
gitLogs = gitLogs.filter(log => log.msg.match(/(^Merge|^Revert)/) === null);
humanLog(`after removing merge/revert : ${gitLogs.length} commits`);
gitLogs = gitLogs.filter(log => {
const tag = log.msg.match(COMMIT_TAG_REGEX);
return tag !== null && COMMIT_TAGS.includes(tag[0]);
});
humanLog(`after removing missing tags : ${gitLogs.length} commits`);
gitLogs = gitLogs.filter(log => !IGNORED_AUTHORS.includes(log.author));
humanLog(`after removing ignored authors : ${gitLogs.length} commits`);
const authors = Array.from(new Set(gitLogs.map(l => l.author)));
humanLog(`remaining : ${gitLogs.length} commits from ${authors.length} authors`);
const logsSummary = gitLogs
.map(log => ({
'week': getWeek(log.date),
'year': log.date.getFullYear(),
'type': log.msg.match(COMMIT_TAG_REGEX)[0],
'author': log.author
}))
.reverse() // Commit are from younger to older, reverse it
.reduce((nest, log) => {
// Count by week and year
if(!nest[log.year]) nest[log.year] = {};
if(!nest[log.year][log.week]) nest[log.year][log.week] = {};
// for each tag
if(!nest[log.year][log.week][log.type]) {
nest[log.year][log.week][log.type] = 1;
} else {
nest[log.year][log.week][log.type] += 1;
}
// for each author
if(!nest[log.year][log.week][log.author]) {
nest[log.year][log.week][log.author] = 1;
} else {
nest[log.year][log.week][log.author] += 1;
}
return nest;
}, {});
const csvLogs = flatten(Object.entries(logsSummary)
.map(([year, val]) => Object.entries(val)
.map(([week, counts]) => [
year,
week,
...COMMIT_TAGS.map(t => counts[t] || 0),
...authors.map(a => counts[a] || 0),
].join(','))
)
).join('\n');
const csvHeader = ['year', 'week', ...COMMIT_TAGS, ...authors].join(',');
humanLog('\n===== Commit count by tag & author per week =====\n');
console.log(csvHeader);
console.log(csvLogs);
@austil
Copy link
Author

austil commented Jul 3, 2019

ATM contributors spamming commits will appear as more active than the others.
Maybe parse the --shortstat output to compute a "change delta" to be used instead of the count of commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment