mirror of
https://github.com/IonicaBizau/git-stats.git
synced 2024-12-22 13:22:11 +01:00
fix: Display top contributors correctly when larger than chart
This commit is contained in:
parent
b5ebdb2be7
commit
fe198b1018
2 changed files with 616 additions and 5 deletions
618
lib/index.js
618
lib/index.js
|
@ -560,15 +560,16 @@ class GitStats {
|
||||||
;
|
;
|
||||||
|
|
||||||
self.authors(options, function (err, authors) {
|
self.authors(options, function (err, authors) {
|
||||||
|
const maxAuthors = 2 * options.radius;
|
||||||
if (err) { return callback(err); }
|
if (err) { return callback(err); }
|
||||||
if (authors.length > 50) {
|
if (authors.length > maxAuthors) {
|
||||||
let others = {
|
var others = {
|
||||||
value: authors.slice(50).reduce(function (a, b) {
|
value: authors.slice(maxAuthors).reduce(function (a, b) {
|
||||||
return a + b.value;
|
return a + b.value;
|
||||||
}, 0)
|
}, 0)
|
||||||
, label: "Others"
|
, label: "Others"
|
||||||
};
|
};
|
||||||
authors = authors.slice(0, 50);
|
authors = authors.slice(0, maxAuthors);
|
||||||
authors.push(others);
|
authors.push(others);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,4 +677,613 @@ GitStats.DEFAULT_CONFIG = {
|
||||||
, global_activity: false
|
, global_activity: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getConfig
|
||||||
|
* Fetches the configuration object from file (`~/.git-stats-config.js`).
|
||||||
|
*
|
||||||
|
* @name getConfig
|
||||||
|
* @function
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {Object|Undefined} If no callback is provided, the configuration object will be returned.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.getConfig = function (callback) {
|
||||||
|
var data = {}
|
||||||
|
, err = null
|
||||||
|
;
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = require(CONFIG_PATH);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === "MODULE_NOT_FOUND") {
|
||||||
|
err = null;
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
return callback(err, data);
|
||||||
|
} else {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initConfig
|
||||||
|
* Inits the configuration field (`this.config`).
|
||||||
|
*
|
||||||
|
* @name initConfig
|
||||||
|
* @function
|
||||||
|
* @param {Object|String} input The path to a custom git-stats configuration file or the configuration object.
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.initConfig = function (input, callback) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (Typpy(input, Function)) {
|
||||||
|
callback = input;
|
||||||
|
input = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
input = input || CONFIG_PATH;
|
||||||
|
|
||||||
|
// Handle object input
|
||||||
|
if (Typpy(input, Object)) {
|
||||||
|
this.config = Ul.deepMerge(input, GitStats.DEFAULT_CONFIG);
|
||||||
|
callback && callback(null, this.config);
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
this.getConfig(function (err, data) {
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
self.initConfig(data, callback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.initConfig(this.getConfig());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* record
|
||||||
|
* Records a new commit.
|
||||||
|
*
|
||||||
|
* @name record
|
||||||
|
* @function
|
||||||
|
* @param {Object} data The commit data containing:
|
||||||
|
*
|
||||||
|
* - `date` (String|Date): The date object or a string in a format that can be parsed.
|
||||||
|
* - `url` (String): The repository remote url.
|
||||||
|
* - `hash` (String): The commit hash.
|
||||||
|
* - `_data` (Object): If this field is provided, it should be the content of the git-stats data file as object. It will be modified in-memory and then returned.
|
||||||
|
* - `save` (Boolean): If `false`, the result will *not* be saved in the file.
|
||||||
|
*
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.record = function (data, callback) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Validate data
|
||||||
|
callback = callback || function (err) { if (err) throw err; };
|
||||||
|
data = Object(data);
|
||||||
|
|
||||||
|
if (typeof data.date === "string") {
|
||||||
|
data.date = new Moment(new Date(data.date));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^moment|date$/.test(Typpy(data.date))) {
|
||||||
|
callback(new Error("The date field should be a string or a date object."));
|
||||||
|
return GitStats;
|
||||||
|
} else if (Typpy(data.date, Date)) {
|
||||||
|
data.date = Moment(data.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.hash !== "string" || !data.hash) {
|
||||||
|
callback(new Error("Invalid hash."));
|
||||||
|
return GitStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not used, but remains here just in case we need
|
||||||
|
// it in the future
|
||||||
|
if (typeof data.url !== "string" || !data.url) {
|
||||||
|
delete data.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function modify (err, stats) {
|
||||||
|
|
||||||
|
var commits = stats.commits
|
||||||
|
, day = data.date.format(DATE_FORMAT)
|
||||||
|
, today = commits[day] = Object(commits[day])
|
||||||
|
;
|
||||||
|
|
||||||
|
today[data.hash] = 1;
|
||||||
|
|
||||||
|
if (data.save === false) {
|
||||||
|
callback(null, stats);
|
||||||
|
} else {
|
||||||
|
self.save(stats, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have input data
|
||||||
|
if (data._data) {
|
||||||
|
return modify(null, data._data);
|
||||||
|
} else {
|
||||||
|
// Get stats
|
||||||
|
self.get(modify);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removeCommit
|
||||||
|
* Deletes a specifc commit from the history.
|
||||||
|
*
|
||||||
|
* @name record
|
||||||
|
* @function
|
||||||
|
* @param {Object} data The commit data containing:
|
||||||
|
*
|
||||||
|
* - `date` (String|Date): The date object or a string in a format that can be parsed. If not provided, the hash object will be searched in all dates.
|
||||||
|
* - `hash` (String): The commit hash.
|
||||||
|
* - `_data` (Object): If this field is provided, it should be the content of the git-stats data file as object. It will be modified in-memory and then returned.
|
||||||
|
* - `save` (Boolean): If `false`, the result will *not* be saved in the file.
|
||||||
|
*
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.removeCommit = function (data, callback) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Validate data
|
||||||
|
callback = callback || function (err) { if (err) throw err; };
|
||||||
|
data = Object(data);
|
||||||
|
|
||||||
|
if (typeof data.date === "string") {
|
||||||
|
data.date = new Moment(new Date(data.date));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^moment|date$/.test(Typpy(data.date))) {
|
||||||
|
data.date = null;
|
||||||
|
} else if (Typpy(data.date, Date)) {
|
||||||
|
data.date = Moment(data.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.hash !== "string" || !data.hash) {
|
||||||
|
callback(new Error("Invalid hash."));
|
||||||
|
return GitStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
function modify (err, stats) {
|
||||||
|
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
if (!data.date) {
|
||||||
|
IterateObject(stats.commits, function (todayObj) {
|
||||||
|
delete todayObj[data.hash];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var commits = stats.commits
|
||||||
|
, day = data.date.format(DATE_FORMAT)
|
||||||
|
, today = commits[day] = Object(commits[day])
|
||||||
|
;
|
||||||
|
|
||||||
|
delete today[data.hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.save === false) {
|
||||||
|
callback(null, stats);
|
||||||
|
} else {
|
||||||
|
self.save(stats, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have input data
|
||||||
|
if (data._data) {
|
||||||
|
return modify(null, data._data);
|
||||||
|
} else {
|
||||||
|
// Get stats
|
||||||
|
self.get(modify);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get
|
||||||
|
* Gets the git stats.
|
||||||
|
*
|
||||||
|
* @name get
|
||||||
|
* @function
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.get = function (callback) {
|
||||||
|
var self = this;
|
||||||
|
ReadJson(self.path, function (err, data) {
|
||||||
|
|
||||||
|
if (err && err.code === "ENOENT") {
|
||||||
|
return self.save(DEFAULT_DATA, function (err) {
|
||||||
|
callback(err, DEFAULT_DATA);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
callback(null, data);
|
||||||
|
});
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save
|
||||||
|
* Saves the provided stats.
|
||||||
|
*
|
||||||
|
* @name save
|
||||||
|
* @function
|
||||||
|
* @param {Object} stats The stats to be saved.
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.save = function (stats, callback) {
|
||||||
|
WriteJson(this.path, stats, callback);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterateDays
|
||||||
|
* Iterate through the days, calling the callback function on each day.
|
||||||
|
*
|
||||||
|
* @name iterateDays
|
||||||
|
* @function
|
||||||
|
* @param {Object} data An object containing the following fields:
|
||||||
|
*
|
||||||
|
* - `start` (Moment): A `Moment` date object representing the start date (default: *an year ago*).
|
||||||
|
* - `end` (Moment): A `Moment` date object representing the end date (default: *now*).
|
||||||
|
* - `format` (String): The format of the date (default: `"MMM D, YYYY"`).
|
||||||
|
*
|
||||||
|
* @param {Function} callback The callback function called with the current day formatted (type: string) and the `Moment` date object.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.iterateDays = function (data, callback) {
|
||||||
|
|
||||||
|
if (typeof data === "function") {
|
||||||
|
callback = data;
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the defaults
|
||||||
|
data.end = data.end || Moment();
|
||||||
|
data.start = data.start || Moment().subtract(1, "years");
|
||||||
|
data.format = data.format || DATE_FORMAT;
|
||||||
|
|
||||||
|
var start = new Moment(data.start.format(DATE_FORMAT), DATE_FORMAT)
|
||||||
|
, end = new Moment(data.end.format(DATE_FORMAT), DATE_FORMAT)
|
||||||
|
, tomrrow = Moment(end.format(DATE_FORMAT), DATE_FORMAT).add(1, "days")
|
||||||
|
, endStr = tomrrow.format(DATE_FORMAT)
|
||||||
|
, cDay = null
|
||||||
|
;
|
||||||
|
|
||||||
|
while (start.format(DATE_FORMAT) !== endStr) {
|
||||||
|
cDay = start.format(data.format);
|
||||||
|
callback(cDay, start);
|
||||||
|
start.add(1, "days");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* graph
|
||||||
|
* Creates an object with the stats on the provided period (default: *last year*).
|
||||||
|
*
|
||||||
|
* @name graph
|
||||||
|
* @function
|
||||||
|
* @param {Object} data The object passed to the `iterateDays` method.
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.graph = function (data, callback) {
|
||||||
|
|
||||||
|
if (typeof data === "function") {
|
||||||
|
callback = data;
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Get commits
|
||||||
|
self.get(function (err, stats) {
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
|
||||||
|
var cDayObj = null
|
||||||
|
, year = {}
|
||||||
|
;
|
||||||
|
|
||||||
|
// Iterate days
|
||||||
|
self.iterateDays(data, function (cDay) {
|
||||||
|
cDayObj = Object(stats.commits[cDay]);
|
||||||
|
cDayObj = year[cDay] = {
|
||||||
|
_: cDayObj
|
||||||
|
, c: Object.keys(cDayObj).length
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(null, year);
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calendar
|
||||||
|
* Creates the calendar data for the provided period (default: *last year*).
|
||||||
|
*
|
||||||
|
* @name calendar
|
||||||
|
* @function
|
||||||
|
* @param {Object} data The object passed to the `graph` method.
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.calendar = function (data, callback) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.graph(data, function (err, graph) {
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
|
||||||
|
var cal = { total: 0, days: {}, cStreak: 0, lStreak: 0, max: 0 }
|
||||||
|
, cDay = null
|
||||||
|
, days = Object.keys(graph)
|
||||||
|
, levels = null
|
||||||
|
, cLevel = 0
|
||||||
|
;
|
||||||
|
|
||||||
|
days.forEach(function (c) {
|
||||||
|
cDay = graph[c];
|
||||||
|
cal.total += cDay.c;
|
||||||
|
if (cDay.c > cal.max) {
|
||||||
|
cal.max = cDay.c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cDay.c > 0) {
|
||||||
|
if (++cal.cStreak > cal.lStreak) {
|
||||||
|
cal.lStreak = cal.cStreak;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cal.cStreak = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
levels = cal.max / (LEVELS.length * 2);
|
||||||
|
days.forEach(function (c) {
|
||||||
|
cDay = graph[c];
|
||||||
|
cal.days[c] = {
|
||||||
|
c: cDay.c
|
||||||
|
, level: !levels
|
||||||
|
? 0 : (cLevel = Math.round(cDay.c / levels)) >= 4
|
||||||
|
? 4 : !cLevel && cDay.c > 0 ? 1 : cLevel
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(null, cal);
|
||||||
|
});
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ansiCalendar
|
||||||
|
* Creates the ANSI contributions calendar.
|
||||||
|
*
|
||||||
|
* @name ansiCalendar
|
||||||
|
* @function
|
||||||
|
* @param {Object} options The object passed to the `calendar` method.
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.ansiCalendar = function (options, callback) {
|
||||||
|
|
||||||
|
if (typeof options === "function") {
|
||||||
|
callback = options;
|
||||||
|
options = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.graph(options, function (err, graph) {
|
||||||
|
var cal = []
|
||||||
|
, data = {
|
||||||
|
theme: options.theme
|
||||||
|
, start: options.start
|
||||||
|
, end: options.end
|
||||||
|
, firstDay: options.firstDay
|
||||||
|
, cal: cal
|
||||||
|
, raw: options.raw
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
self.iterateDays(options, function (cDay) {
|
||||||
|
var cDayObj = graph[cDay];
|
||||||
|
if (!cDayObj) { return; }
|
||||||
|
cal.push([cDay, cDayObj.c]);
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(null, CliGhCal(cal, data));
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* authors
|
||||||
|
* Creates an array with the authors of a git repository.
|
||||||
|
*
|
||||||
|
* @name authors
|
||||||
|
* @function
|
||||||
|
* @param {String|Object} options The repo path or an object containing the following fields:
|
||||||
|
*
|
||||||
|
* - `repo` (String): The repository path.
|
||||||
|
* - `start` (String): The start date.
|
||||||
|
* - `end` (String): The end date.
|
||||||
|
*
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.authors = function (options, callback) {
|
||||||
|
var repo = new Gry(options.repo);
|
||||||
|
repo.exec(['shortlog', '-s', '-n', '--all', '--since', options.start.toString(), '--until', options.end.toString()], function (err, stdout) {
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
var lines = stdout.split("\n");
|
||||||
|
var pieData = stdout.split("\n").map(function (c) {
|
||||||
|
var splits = c.split("\t").map(function (cc) {
|
||||||
|
return cc.trim();
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
value: parseInt(splits[0])
|
||||||
|
, label: splits[1]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
callback(null, pieData);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* authorsPie
|
||||||
|
* Creates the authors pie.
|
||||||
|
*
|
||||||
|
* @name authorsPie
|
||||||
|
* @function
|
||||||
|
* @param {String|Object} options The repo path or an object containing the following fields:
|
||||||
|
*
|
||||||
|
* - `repo` (String): The repository path.
|
||||||
|
* - `radius` (Number): The pie radius.
|
||||||
|
* - `no_ansi` (Boolean): If `true`, the pie will not contain ansi characters.
|
||||||
|
* - `raw` (Boolean): If `true`, the raw JSON will be displayed.
|
||||||
|
*
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.authorsPie = function (options, callback) {
|
||||||
|
|
||||||
|
const radius = Math.round(process.stdout.rows / 2) || 20;
|
||||||
|
|
||||||
|
if (typeof options === "string") {
|
||||||
|
options = {
|
||||||
|
repo: options
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
options = Ul.merge(options, { radius });
|
||||||
|
|
||||||
|
if (!IsThere(options.repo)) {
|
||||||
|
return callback(new Error("The repository folder doesn't exist."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this
|
||||||
|
, repo = new Gry(options.repo)
|
||||||
|
, pie = null
|
||||||
|
, pieData = []
|
||||||
|
;
|
||||||
|
|
||||||
|
self.authors(options, function (err, authors) {
|
||||||
|
if (err) { return callback(err); }
|
||||||
|
if (authors.length > radius) {
|
||||||
|
var others = {
|
||||||
|
value: authors.slice(radius).reduce(function (a, b) {
|
||||||
|
return a + b.value;
|
||||||
|
}, 0)
|
||||||
|
, label: "Others"
|
||||||
|
};
|
||||||
|
authors = authors.slice(0, radius);
|
||||||
|
authors.push(others);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
legend: true
|
||||||
|
, flat: true
|
||||||
|
, no_ansi: options.no_ansi
|
||||||
|
, authors: authors
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(null, options.raw ? data : new CliPie(options.radius, authors, data).toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* globalActivity
|
||||||
|
* Creates the global contributions calendar (all commits made by all committers).
|
||||||
|
*
|
||||||
|
* @name globalActivity
|
||||||
|
* @function
|
||||||
|
* @param {String|Object} options The repo path or an object containing the following fields:
|
||||||
|
*
|
||||||
|
* - `repo` (String): The repository path.
|
||||||
|
* - `start` (String): The start date.
|
||||||
|
* - `end` (String): The end date.
|
||||||
|
* - `theme` (String|Object): The calendar theme.
|
||||||
|
* - `raw` (Boolean): If `true`, the raw JSON will be displayed.
|
||||||
|
*
|
||||||
|
* @param {Function} callback The callback function.
|
||||||
|
* @return {GitStats} The `GitStats` instance.
|
||||||
|
*/
|
||||||
|
GitStats.prototype.globalActivity = function (options, callback) {
|
||||||
|
|
||||||
|
if (typeof options === "string") {
|
||||||
|
options = {
|
||||||
|
repo: options
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.repo = Abs(options.repo);
|
||||||
|
|
||||||
|
if (!IsThere(options.repo)) {
|
||||||
|
return callback(new Error("The repository folder doesn't exist."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var commits = {}
|
||||||
|
, today = null
|
||||||
|
, cal = []
|
||||||
|
;
|
||||||
|
|
||||||
|
var logArgs = ["log","--since", options.start.format(DATE_FORMAT), "--until", options.end.format(DATE_FORMAT)]
|
||||||
|
if(options.author){
|
||||||
|
logArgs = logArgs.concat(["--author", options.author])
|
||||||
|
}
|
||||||
|
|
||||||
|
GitLogParser(Spawn("git",logArgs , { cwd: options.repo }).stdout).on("commit", function(commit) {
|
||||||
|
if (!commit) { return; }
|
||||||
|
today = Moment(commit.date).format(DATE_FORMAT);
|
||||||
|
commits[today] = commits[today] || 0;
|
||||||
|
++commits[today];
|
||||||
|
}).on("error", function (err) {
|
||||||
|
callback(err);
|
||||||
|
}).on("finish", function () {
|
||||||
|
Object.keys(commits).forEach(function (c) {
|
||||||
|
cal.push([c, commits[c]])
|
||||||
|
});
|
||||||
|
var data = {
|
||||||
|
theme: options.theme
|
||||||
|
, start: options.start
|
||||||
|
, end: options.end
|
||||||
|
, cal: cal
|
||||||
|
, raw: options.raw
|
||||||
|
};
|
||||||
|
callback(null, CliGhCal(cal, data));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = GitStats;
|
module.exports = GitStats;
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Gnab <as0n@gnab.fr>",
|
"Gnab <as0n@gnab.fr>",
|
||||||
"William Boman <william@redwill.se>",
|
"William Boman <william@redwill.se>",
|
||||||
"Fabian Furger <mystyfly@gmail.com>"
|
"Fabian Furger <mystyfly@gmail.com>",
|
||||||
|
"Jonah Lawrence <jonah@freshidea.com>"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
Loading…
Reference in a new issue