Skip to content

Commit 8090023

Browse files
committed
Enables gitlogg to be distributed via npm
`npm install -g gitlogg` will put it on your path Script is now agnostic to the pwd in which it's invoked `.js` is pre-transpiled so babel isn't required at runtime - set environment variable GITLOGG_DEV to enable runtime transpilation and avoid the build step Instead of a hardcoded `yourpath` variable, each positional argument to `gitlogg` is a repository path. - You can easily `gitlogg ./myrepos/*/` to process all repositories in a directory `.js` reads and writes from file descriptors 3 and 4; these are managed by bash script
1 parent b3e356b commit 8090023

File tree

5 files changed

+77
-79
lines changed

5 files changed

+77
-79
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ node_modules/
55
assets/
66
repos/
77
gitlogg.json
8-
gitlogg.tmp
8+
gitlogg.tmp.*
9+
*.compiled.js
10+
*.compiled.js.map

gitlogg/gitlogg-generate-log.sh

+46-66
Original file line numberDiff line numberDiff line change
@@ -13,77 +13,57 @@ Pur='\033[0;35m'; BPur='\033[1;35m'; UPur='\033[4;35m'; IPur='\033[0;9
1313
Cya='\033[0;36m'; BCya='\033[1;36m'; UCya='\033[4;36m'; ICya='\033[0;96m'; BICya='\033[1;96m'; On_Cya='\033[46m'; On_ICya='\033[0;106m';
1414
Whi='\033[0;37m'; BWhi='\033[1;37m'; UWhi='\033[4;37m'; IWhi='\033[0;97m'; BIWhi='\033[1;97m'; On_Whi='\033[47m'; On_IWhi='\033[0;107m';
1515

16-
# define the absolute path to the directory that contains all your repositories.
17-
yourpath=../repos/
16+
# Each positional argument is a path to a git repository
17+
repositories=("$@")
1818

19-
# ensure there's always a '/' at the end of the 'yourpath' variable, since its value can be changed by user.
20-
case "$yourpath" in
21-
*/)
22-
yourpathSanitized="${yourpath}" # no changes if there's already a slash at the end - syntax sugar
23-
;;
24-
*)
25-
yourpathSanitized="${yourpath}/" # add a slash at the end if there isn't already one
26-
;;
27-
esac
28-
29-
# 'thepath' sets the path to each repository under 'yourpath' (the trailing asterix [*/] represents all the repository folders).
30-
thepath="${yourpathSanitized}*/"
31-
32-
# function to trim whitespace
33-
trim() {
34-
local var="$*"
35-
var="${var#'${var%%[![:space:]]*}'}" # remove leading whitespace characters
36-
var="${var%'${var##*[![:space:]]}'}" # remove trailing whitespace characters
37-
echo -n "$var"
38-
}
39-
40-
# number of directories (repos) under 'thepath'
41-
DIRCOUNT="$(find $thepath -maxdepth 0 -type d | wc -l)"
42-
43-
# trim whitespace from DIRCOUNT
44-
DIRNR="$(trim $DIRCOUNT)"
19+
# number of directories (repos)
20+
DIRCOUNT="${#repositories[@]}"
4521

4622
# determine if we're dealing with a singular repo or multiple
47-
if [ "${DIRNR}" -gt "1" ]; then
48-
reporef="all ${Red}${DIRNR}${Yel} repositories"
49-
elif [ "${DIRNR}" -eq "1" ]; then
50-
reporef="the one repository"
23+
if [ "${DIRCOUNT}" -gt "1" ]; then
24+
reporef="${Red}${DIRCOUNT}${Yel} repositories"
25+
elif [ "${DIRCOUNT}" -eq "1" ]; then
26+
reporef="${Red}${DIRCOUNT}${Yel} repository"
27+
elif [ "${DIRCOUNT}" -eq "0" ]; then
28+
echo -e "${Whi}[ERROR 003]: ${Yel}No repositories specified" 1>&2
29+
exit 1
5130
fi
5231

5332
# start counting seconds elapsed
5433
SECONDS=0
5534

56-
# if the path exists and is not empty
57-
if [ -d "${yourpathSanitized}" ] && [ "$(ls $yourpathSanitized)" ]; then
58-
echo -e "${Yel}Generating ${Pur}git log ${Yel}for ${reporef} located at ${Red}'${thepath}'${Yel}. ${Blu}This might take a while!${RCol}"
59-
for dir in $thepath
60-
do
61-
(cd $dir &&
62-
echo -e "${Whi}Outputting ${Pur}${PWD##*/}${RCol}" >&2 &&
63-
git log --all --no-merges --shortstat --reverse --pretty=format:'commits\trepository\t'"${PWD##*/}"'\tcommit_hash\t%H\tcommit_hash_abbreviated\t%h\ttree_hash\t%T\ttree_hash_abbreviated\t%t\tparent_hashes\t%P\tparent_hashes_abbreviated\t%p\tauthor_name\t%an\tauthor_name_mailmap\t%aN\tauthor_email\t%ae\tauthor_email_mailmap\t%aE\tauthor_date\t%ad\tauthor_date_RFC2822\t%aD\tauthor_date_relative\t%ar\tauthor_date_unix_timestamp\t%at\tauthor_date_iso_8601\t%ai\tauthor_date_iso_8601_strict\t%aI\tcommitter_name\t%cn\tcommitter_name_mailmap\t%cN\tcommitter_email\t%ce\tcommitter_email_mailmap\t%cE\tcommitter_date\t%cd\tcommitter_date_RFC2822\t%cD\tcommitter_date_relative\t%cr\tcommitter_date_unix_timestamp\t%ct\tcommitter_date_iso_8601\t%ci\tcommitter_date_iso_8601_strict\t%cI\tref_names\t%d\tref_names_no_wrapping\t%D\tencoding\t%e\tsubject\t%s\tsubject_sanitized\t%f\tcommit_notes\t%N\tstats\t' |
64-
sed '/^[ \t]*$/d' | # remove all newlines/line-breaks, including those with empty spaces
65-
tr '\n' 'ò' | # convert newlines/line-breaks to a character, so we can manipulate it without much trouble
66-
tr '\r' ' ' | # replace carriage returns with a space, so we avoid new lines popping from placeholders that allow user input
67-
sed 's/tòcommits/tòòcommits/g' | # because some commits have no stats, we have to create an extra line-break to make `paste -d ' ' - -` consistent
68-
tr 'ò' '\n' | # bring back all line-breaks
69-
sed '{
70-
N
71-
s/[)]\n\ncommits/)\
72-
commits/g
73-
}' | # some rogue mystical line-breaks need to go down to their knees and beg for mercy, which they're not getting
74-
paste -d ' ' - - | # collapse lines so that the `shortstat` is merged with the rest of the commit data, on a single line
75-
awk '{print NR"\\t",$0}' | # print line number in front of each line, along with the `\t` delimiter
76-
sed 's/\\t\ commits\\trepo/\\t\commits\\trepo/g' # get rid of the one space that shouldn't be there
77-
)
78-
done > gitlogg.tmp
79-
echo -e "${Gre}The file ${Blu}./gitlogg.tmp ${Gre}generated in${RCol}: ${SECONDS}s" &&
80-
babel gitlogg-parse-json.js | node # only parse JSON if we have a source to parse it from
81-
# if the path exists but is empty
82-
elif [ -d "${yourpathSanitized}" ] && [ ! "$(ls $yourpathSanitized)" ]; then
83-
echo -e "${Whi}[ERROR 002]: ${Yel}The path to the local repositories ${Red}'${yourpath}'${Yel}, which is set on the file ${Blu}'gitlogg-generate-log.sh' ${UYel}exists, but is empty!${RCol}"
84-
echo -e "${Yel}Please move the repos to ${Red}'${yourpath}'${Yel} or update the variable ${Pur}'yourpath'${Yel} to reflect the absolute path to the directory where the repos are located.${RCol}"
85-
# if the path does not exists
86-
elif [ ! -d "${yourpathSanitized}" ]; then
87-
echo -e "${Whi}[ERROR 001]: ${Yel}The path to the local repositories ${Red}'${yourpath}'${Yel}, which is set on the file ${Blu}'gitlogg-generate-log.sh' ${UYel}does not exist!${RCol}"
88-
echo -e "${Yel}Please create ${Red}'${yourpath}'${Yel} and move the repos under it, or update the variable ${Pur}'yourpath'${Yel} to reflect the absolute path to the directory where the repos are located.${RCol}"
89-
fi
35+
echo -e "${Yel}Generating ${Pur}git log ${Yel}for ${reporef}${Yel}. ${Blu}This might take a while!${RCol}"
36+
GITLOGGTMP="$( TMPDIR="$( pwd )" mktemp -t gitlogg.tmp )"
37+
for dir in "${repositories[@]}"
38+
do
39+
(cd "$dir" &&
40+
echo -e "${Whi}Outputting ${Pur}${PWD##*/}${RCol}" >&2 &&
41+
git log --all --no-merges --shortstat --reverse --pretty=format:'commits\trepository\t'"${PWD##*/}"'\tcommit_hash\t%H\tcommit_hash_abbreviated\t%h\ttree_hash\t%T\ttree_hash_abbreviated\t%t\tparent_hashes\t%P\tparent_hashes_abbreviated\t%p\tauthor_name\t%an\tauthor_name_mailmap\t%aN\tauthor_email\t%ae\tauthor_email_mailmap\t%aE\tauthor_date\t%ad\tauthor_date_RFC2822\t%aD\tauthor_date_relative\t%ar\tauthor_date_unix_timestamp\t%at\tauthor_date_iso_8601\t%ai\tauthor_date_iso_8601_strict\t%aI\tcommitter_name\t%cn\tcommitter_name_mailmap\t%cN\tcommitter_email\t%ce\tcommitter_email_mailmap\t%cE\tcommitter_date\t%cd\tcommitter_date_RFC2822\t%cD\tcommitter_date_relative\t%cr\tcommitter_date_unix_timestamp\t%ct\tcommitter_date_iso_8601\t%ci\tcommitter_date_iso_8601_strict\t%cI\tref_names\t%d\tref_names_no_wrapping\t%D\tencoding\t%e\tsubject\t%s\tsubject_sanitized\t%f\tcommit_notes\t%N\tstats\t' |
42+
sed '/^[ \t]*$/d' | # remove all newlines/line-breaks, including those with empty spaces
43+
tr '\n' 'ò' | # convert newlines/line-breaks to a character, so we can manipulate it without much trouble
44+
tr '\r' ' ' | # replace carriage returns with a space, so we avoid new lines popping from placeholders that allow user input
45+
sed 's/tòcommits/tòòcommits/g' | # because some commits have no stats, we have to create an extra line-break to make `paste -d ' ' - -` consistent
46+
tr 'ò' '\n' | # bring back all line-breaks
47+
sed '{
48+
N
49+
s/[)]\n\ncommits/)\
50+
commits/g
51+
}' | # some rogue mystical line-breaks need to go down to their knees and beg for mercy, which they're not getting
52+
paste -d ' ' - - | # collapse lines so that the `shortstat` is merged with the rest of the commit data, on a single line
53+
awk '{print NR"\\t",$0}' | # print line number in front of each line, along with the `\t` delimiter
54+
sed 's/\\t\ commits\\trepo/\\t\commits\\trepo/g' # get rid of the one space that shouldn't be there
55+
)
56+
done > "$GITLOGGTMP"
57+
echo -e "${Gre}The file ${Blu}$GITLOGGTMP ${Gre}generated in${RCol}: ${SECONDS}s" &&
58+
OUTPUT_FILE="gitlogg.json"
59+
(
60+
if [ "x$GITLOGG_DEV" = "x" ]; then
61+
# Run precompiled script
62+
node "$__dirname/gitlogg-parse-json.compiled.js"
63+
else
64+
# Development: transpile at runtime
65+
babel "$__dirname/gitlogg-parse-json.js" | node
66+
fi
67+
) 3<"$GITLOGGTMP" 4>"$OUTPUT_FILE" # only parse JSON if we have a source to parse it from
68+
echo -e "${Gre}The file ${Blu}$OUTPUT_FILE${Gre} was saved!${RCol}"
69+
rm "$GITLOGGTMP"

gitlogg/gitlogg-parse-json.js

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
var fs = require('fs'),
22
path = require('path'),
33
chalk = require('chalk'),
4-
output_file = './gitlogg.json';
4+
// Read and write from file descriptors. Bash script will hook these up.
5+
input_file = 3,
6+
output_file = 4;
7+
8+
try {
9+
require('source-map-support').install();
10+
} catch(e) {}
511

612
console.log(chalk.yellow('Generating JSON output...'));
713

@@ -16,7 +22,7 @@ var changes = function(data, index) {
1622

1723
console.time(chalk.green('JSON output generated in'));
1824

19-
var output = fs.readFileSync('gitlogg.tmp', 'utf8')
25+
var output = fs.readFileSync(input_file, 'utf8')
2026
.trim()
2127
.split('\n')
2228
.map(line => line.split('\\t'))
@@ -136,11 +142,4 @@ console.timeEnd(chalk.green('JSON output generated in'));
136142

137143
console.log(chalk.yellow('Writing output to file...'));
138144

139-
// console.log('output', JSON.stringify(output, null, 2)) // comment this out to see the output on the terminal as well
140-
141-
fs.writeFile(output_file, JSON.stringify(output, null, 2), function(err) {
142-
if(err) {
143-
return console.log(chalk.red(err, 'Something went wrong, the file could not be written / saved'));
144-
}
145-
console.log(chalk.green('The file ' + chalk.blue(output_file) + ' was saved! '));
146-
});
145+
fs.writeFileSync(output_file, JSON.stringify(output, null, 2));

gitlogg/gitlogg.sh

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
#!/usr/bin/env bash
22

3-
bash gitlogg-generate-log.sh
3+
# Magical incantation mimics node's __dirname
4+
__dirname="$(cd "$(
5+
dirname "$(
6+
node -e "var out=process.argv[1];try {out=require('path').resolve(out, '..', require('fs').readlinkSync(out))} catch(e) {} finally {console.log(out)}" "${BASH_SOURCE[0]}"
7+
)"
8+
)" && pwd)"
9+
10+
. "$__dirname/gitlogg-generate-log.sh"

package.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,19 @@
2222
},
2323
"copyright": "Copyright (c) Wallace Sidhrée - All rights reserved.",
2424
"license": "MIT",
25+
"bin": {
26+
"gitlogg": "gitlogg/gitlogg.sh"
27+
},
28+
"scripts": {
29+
"build": "babel --source-maps --out-file gitlogg/gitlogg-parse-json.compiled.js gitlogg/gitlogg-parse-json.js",
30+
"test": "GITLOGG_DEV=1 ./gitlogg/gitlogg.sh ./",
31+
"prepublish": "npm run build"
32+
},
2533
"devDependencies": {
34+
"babel-cli": "^6.18.0",
2635
"babel-preset-es2015": "^6.9.0",
27-
"chalk": "^1.1.3"
36+
"chalk": "^1.1.3",
37+
"source-map-support": "^0.4.6"
2838
},
2939
"engines": {
3040
"node": ">=4.0.3"

0 commit comments

Comments
 (0)