add git utilities to display stats of a repo
This commit is contained in:
parent
595443e89b
commit
a9feda9383
1
Brewfile
1
Brewfile
@ -200,3 +200,4 @@ brew "git-delta"
|
||||
brew "sn0int"
|
||||
brew "prettier"
|
||||
brew "switchaudio-osx"
|
||||
brew "git-extras"
|
||||
|
166
dot_scripts/git-overview.py
Executable file
166
dot_scripts/git-overview.py
Executable file
@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Origin : https://github.com/qznc/dot/blob/master/bin/git-overview
|
||||
Gather a general overview from a git repository.
|
||||
It shall identify the major contributors within a project.
|
||||
It shall identify interesting parts in the code.
|
||||
"""
|
||||
|
||||
from subprocess import check_output
|
||||
from datetime import datetime
|
||||
from os.path import splitext
|
||||
|
||||
_CMD_AUTHOR_HISTORY = 'git log --date=iso --format="format:%an;%ad;%G?;%H"'
|
||||
_CMD_FILELIST = "git ls-files"
|
||||
_CMD_COMMIT_FILES_CHANGES = "git diff-tree --no-commit-id --name-only -r "
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
FileInfo = namedtuple("FileInfo", "path,authors,commits,firstc,lastc")
|
||||
|
||||
|
||||
def read_log_data():
|
||||
total_commits = 0
|
||||
authors = dict()
|
||||
files = dict()
|
||||
raw = check_output(_CMD_AUTHOR_HISTORY, shell=True, universal_newlines=True)
|
||||
for line in raw.split("\n"):
|
||||
total_commits += 1
|
||||
name, dt, signed, hash = line.split(";")
|
||||
if not name in authors:
|
||||
authors[name] = (datetime.now(), datetime(1900, 1, 1), 0)
|
||||
first, last, count = authors[name]
|
||||
when = datetime.strptime(dt[:-6], "%Y-%m-%d %H:%M:%S")
|
||||
if when < first:
|
||||
first = when
|
||||
if when > last:
|
||||
last = when
|
||||
authors[name] = (first, last, count + 1)
|
||||
craw = check_output(
|
||||
_CMD_COMMIT_FILES_CHANGES + hash, shell=True, universal_newlines=True
|
||||
)
|
||||
for file in craw.split("\n"):
|
||||
if file == "":
|
||||
continue
|
||||
if file in files:
|
||||
fi = files[file]
|
||||
else:
|
||||
files[file] = fi = dict(
|
||||
path=file,
|
||||
authors=set(),
|
||||
commits=0,
|
||||
firstc=datetime.now(),
|
||||
lastc=datetime(1900, 1, 1),
|
||||
)
|
||||
fi["authors"].add(name)
|
||||
fi["commits"] += 1
|
||||
if when < fi["firstc"]:
|
||||
fi["firstc"] = when
|
||||
if when > fi["lastc"]:
|
||||
fi["lastc"] = when
|
||||
return authors, files, total_commits
|
||||
|
||||
|
||||
def readable_duration(dur):
|
||||
"""Given a timedelta object, returns a human-readable string"""
|
||||
if dur.days > 0:
|
||||
if dur.days > 365 * 2:
|
||||
return "%d years" % (dur.days / 365)
|
||||
elif dur.days > 30 * 5:
|
||||
return "%d months" % (dur.days / 30)
|
||||
else:
|
||||
return "%d days" % (dur.days)
|
||||
else:
|
||||
if dur.seconds > 60 * 60 * 2:
|
||||
return "%d hours" % (dur.seconds / 60 / 60)
|
||||
elif dur.seconds > 60 * 5:
|
||||
return "%d minutes" % (dur.seconds / 60)
|
||||
else:
|
||||
return "%d seconds" % (dur.seconds)
|
||||
|
||||
|
||||
def partition_by_extension(fileinfo):
|
||||
pd = dict()
|
||||
meta = dict()
|
||||
total_filecount = 0
|
||||
total_extfilecount = 0
|
||||
for path, fi in fileinfo.items():
|
||||
total_filecount += 1
|
||||
root, ext = splitext(path)
|
||||
if not ext:
|
||||
continue
|
||||
total_extfilecount += 1
|
||||
if not ext in pd:
|
||||
pd[ext] = dict()
|
||||
meta[ext] = dict(filecount=0)
|
||||
pd[ext][path] = fi
|
||||
meta[ext]["filecount"] += 1
|
||||
return pd, meta, total_filecount, total_extfilecount
|
||||
|
||||
|
||||
def print_top_committers(authorinfo, total_commits):
|
||||
authors = sorted(authorinfo.items(), key=lambda x: x[1][2], reverse=True)
|
||||
total_authors = len(authors)
|
||||
committed = 0
|
||||
print("Top Committers (of %d authors):" % (total_authors))
|
||||
for author, info in authors:
|
||||
first, last, count = info
|
||||
readable_last = last.strftime("%Y-%m-%d")
|
||||
if count == 1:
|
||||
print("%-20s 1 commit on %s" % (author, readable_last))
|
||||
else:
|
||||
duration = readable_duration(last - first)
|
||||
print(
|
||||
"%-20s %4d commits during %s until %s"
|
||||
% (author, count, duration, readable_last)
|
||||
)
|
||||
committed += count
|
||||
if committed > total_commits * 0.8:
|
||||
break
|
||||
print(
|
||||
" together these authors have 80+%% of the commits (%d/%d)"
|
||||
% (committed, total_commits)
|
||||
)
|
||||
|
||||
|
||||
def print_important_files(fileinfo):
|
||||
files = list(fileinfo.values())
|
||||
files.sort(key=lambda x: x["commits"])
|
||||
print("Files with most commits:")
|
||||
for x in files[-5:]:
|
||||
readable_last = x["lastc"].strftime("%Y-%m-%d")
|
||||
duration = readable_duration(x["lastc"] - x["firstc"])
|
||||
print(
|
||||
"%4d commits: %-20s during %s until %s"
|
||||
% (x["commits"], x["path"], duration, readable_last)
|
||||
)
|
||||
print()
|
||||
files.sort(key=lambda x: len(x["authors"]))
|
||||
print("Files with most authors:")
|
||||
for x in files[-5:]:
|
||||
print("%2d authors: %-20s" % (len(x["authors"]), x["path"]))
|
||||
|
||||
|
||||
def print_extension_info(fileinfo):
|
||||
pfileinfo, extinfo, total_filecount, total_extfilecount = partition_by_extension(
|
||||
fileinfo
|
||||
)
|
||||
pf = [(e, i) for e, i in pfileinfo.items()]
|
||||
pf.sort(key=lambda x: extinfo[x[0]]["filecount"], reverse=True)
|
||||
print("By file extension:")
|
||||
fc = 0
|
||||
for ext, info in pf:
|
||||
c = extinfo[ext]["filecount"]
|
||||
print("%5s: %d files" % (ext, extinfo[ext]["filecount"]))
|
||||
fc += c
|
||||
if fc > total_filecount * 0.8:
|
||||
break
|
||||
print(" together these make up 80+%% of the files (%d/%d)" % (fc, total_filecount))
|
||||
|
||||
|
||||
authorinfo, fileinfo, total_commits = read_log_data()
|
||||
print_top_committers(authorinfo, total_commits)
|
||||
print()
|
||||
print_important_files(fileinfo)
|
||||
print()
|
||||
print_extension_info(fileinfo)
|
@ -12,6 +12,10 @@ Darwin)
|
||||
export PATH="$PATH:/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/bin/:/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/bin:/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/bin:/usr/local/opt/python/bin/"
|
||||
;;
|
||||
esac
|
||||
if ! test -f "/usr/bin/python2" || ! command -v python2 &>/dev/null; then
|
||||
sudo ln -snf /usr/bin/python /usr/bin/python2
|
||||
fi
|
||||
|
||||
echo "Upgrading pip"
|
||||
sudo pip install --upgrade pip
|
||||
echo "Pip installing stuff"
|
||||
|
@ -10,6 +10,7 @@ ln -snf "$HOME/Code/dotfiles/dot_gitconfig" "$HOME/.gitconfig"
|
||||
ln -snf "$HOME/Code/dotfiles/dot_macos" "$HOME/.macos"
|
||||
ln -snf "$HOME/Code/dotfiles/dot_scripts" "$HOME/.scripts"
|
||||
ln -snf "$HOME/Code/dotfiles/dot_scripts/checkci.sh" /usr/local/bin/checkci
|
||||
ln -snf "$HOME/Code/dotfiles/dot_scripts/git-overview.py" /usr/local/bin/git-overview # via https://github.com/qznc/dot/blob/master/bin/git-overview
|
||||
ln -snf "$HOME/Code/dotfiles/dot_scripts/pre-commit-verify-committer.sh" /usr/local/bin/pre-commit-verify-committer
|
||||
ln -snf "$HOME/Code/dotfiles/dot_vimrc" "$HOME/.vimrc"
|
||||
# custom linters
|
||||
|
Loading…
Reference in New Issue
Block a user