├── .gitignore ├── package.json ├── README.md └── bin └── git-time.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-time", 3 | "version": "1.0.7", 4 | "description": "Measure time spent on a git repository", 5 | "homepage": "https://github.com/vmf91/git-time#readme", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/vmf91/git-time" 9 | }, 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [ 15 | "git", 16 | "hours", 17 | "time", 18 | "spent" 19 | ], 20 | "bin": { 21 | "git-time": "./bin/git-time.js" 22 | }, 23 | "preferGlobal": true, 24 | "author": "Victor Miranda Fernandes", 25 | "license": "ISC", 26 | "dependencies": { 27 | "cli-progress": "^2.1.1", 28 | "minimist": "^1.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git-time 2 | Estimate time spent on a git repository. 3 | 4 | ## Install 5 | npm install -g git-time 6 | 7 | ## Usage 8 | git-time 9 | 10 | ## Help 11 | Usage: git-time 12 | 13 | Where is the path of your Git repository. 14 | 15 | Options: 16 | -h, --help output usage information 17 | -v, --version get the current version 18 | --max maximum time diff in minutes between two consecultive commits. Default: 90 19 | --min minimum time in minutes for the start commit. Default: 25 20 | --author filter out authors. Value(s) are passed to the git log command. 21 | 22 | 23 | ## Output 24 | 25 | Output is grouped by author 26 | ``` 27 | John Doe 28 | 102 commits found 29 | Total time spent: 103.84 hours 30 | 31 | Jane Doe 32 | 321 commits found 33 | Total time spent: 183.11 hours 34 | ``` 35 | -------------------------------------------------------------------------------- /bin/git-time.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var argv = require('minimist')(process.argv.slice(2)); 3 | var os = require('os'); 4 | 5 | var version = '1.0.7' 6 | 7 | // If help or bad usage 8 | if (typeof argv.help == 'boolean' || typeof argv.h == 'boolean') { 9 | console.log('\nUsage: git-time \n\nWhere is the path of your Git repository.\n') 10 | console.log('Options:\n') 11 | console.log(' -h, --help\toutput usage information') 12 | console.log(' -v, --version\tget the current version') 13 | console.log(' --max\t\tmaximum time in minutes between two consecultive commits. Default: 90') 14 | console.log(' --min\t\tminimum time in minutes for the start commit. Default: 25') 15 | console.log(' --author\tfilter out authors. Value(s) are passed to the git log command.') 16 | 17 | return; 18 | } 19 | 20 | // If version 21 | if (typeof argv.version == 'boolean' || typeof argv.v == 'boolean') { 22 | console.log(version) 23 | 24 | return; 25 | } 26 | 27 | const { exec } = require('child_process'); 28 | const _cliProgress = require('cli-progress'); 29 | 30 | var dir = argv._[0]; 31 | if (!dir || dir == '.') { 32 | dir = process.cwd() 33 | } 34 | 35 | var min = 25 36 | if (typeof argv.min === 'number') { 37 | min = argv.min 38 | } 39 | min *= 60 40 | 41 | var max = 90 42 | if (typeof argv.max === 'number') { 43 | max = argv.max 44 | } 45 | max *= 60 46 | 47 | var authors = []; 48 | if (argv.author) { 49 | if(typeof argv.author.map === 'function') { 50 | authors = argv.author; 51 | } else { 52 | authors = [argv.author]; 53 | } 54 | } 55 | 56 | // Wrap in quotes to escape spaces 57 | dir = '"' + dir + '"' 58 | 59 | var lsCommand = `ls ${dir}/.git`; 60 | 61 | if(os.platform() === 'win32'){ 62 | lsCommand = `dir ${dir}\\.git`; 63 | } 64 | 65 | exec(lsCommand, function (err, data) { 66 | if (err) { 67 | console.log(`${dir} is not a valid Git directory`) 68 | return 69 | } 70 | 71 | exec(`cd ${dir} && git log ${authors.map(author => `--author="${author}"`).join(" ")} --pretty="%an <%ae> %ct"`, function (err, data) { 72 | if (err) { 73 | console.log(err) 74 | return 75 | } 76 | 77 | var log = data.split('\n') 78 | log.sort(); 79 | var byAuthor = log.reduce((acc, val) => { 80 | var logParts = val.split(" "); 81 | var time = logParts.pop(); 82 | var author = logParts.join(" "); 83 | acc[author] = acc[author] || []; 84 | acc[author].push(time); 85 | return acc; 86 | }, {}); 87 | 88 | Object.keys(byAuthor).forEach(author => { 89 | var authorLogTimes = byAuthor[author]; 90 | console.log(`${author}`) 91 | console.log(`${authorLogTimes.length} commits found`) 92 | 93 | // create a new progress bar instance and use shades_classic theme 94 | const bar1 = new _cliProgress.Bar({}, _cliProgress.Presets.shades_classic); 95 | bar1.clearOnComplete = true 96 | 97 | // start the progress bar with a total value of 200 and start value of 0 98 | bar1.start(authorLogTimes.length, 0); 99 | 100 | // Initialize variables 101 | var barValue = 0; 102 | var lastCommit = 0; 103 | var total = 0; 104 | for(var i = 0; i < authorLogTimes.length; i++){ 105 | var c = authorLogTimes[i] 106 | 107 | if(lastCommit == 0){ 108 | total += min 109 | }else{ 110 | var diff = c - lastCommit; 111 | 112 | if(diff < max && diff > 0){ 113 | total += diff 114 | }else{ 115 | total += min 116 | } 117 | } 118 | 119 | lastCommit = c 120 | barValue++; 121 | bar1.update(barValue); 122 | } 123 | 124 | bar1.stop(); 125 | 126 | var totalHours = total/3600 127 | console.log(`Total time spent: ${totalHours.toFixed(2)} hours\n`) 128 | }) 129 | }) 130 | }) 131 | 132 | 133 | 134 | --------------------------------------------------------------------------------