Created
January 22, 2021 20:46
-
-
Save svetlyak40wt/6b987ff9a5fef966a8c180a331bcf352 to your computer and use it in GitHub Desktop.
A short script to calculate how many lines belong to each SVN commiter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defpackage #:blame-stats | |
(:use #:cl)) | |
(in-package blame-stats) | |
(ql:quickload '(alexandria cl-fad str lparallel) | |
:silent t) | |
(defun merge-hash-table-into (left right) | |
"Merges values from right hash map into the left" | |
(loop for key being the hash-key of right using (hash-value value) | |
do (incf (gethash key left 0) | |
value))) | |
(defun file-stats (filename) | |
(let* ((blame-results | |
(with-output-to-string (s) | |
(let ((error (with-output-to-string (err) | |
(uiop:run-program (list "svn" "blame" | |
(uiop:unix-namestring filename)) | |
:ignore-error-status t | |
:output s | |
:error-output err)))) | |
(unless (string= error "") | |
(warn "Unable to process ~A:~%~A~%" | |
filename | |
error) | |
(return-from file-stats | |
nil))))) | |
(lines (uiop:split-string blame-results | |
:separator '(#\Newline)))) | |
(loop with result = (make-hash-table :test 'equal) | |
for line in lines | |
for (revision login) = (str:split-omit-nulls #\Space line) | |
unless (string= line "") | |
do (incf (gethash login result 0)) | |
finally (return result)))) | |
(defun ignore-p (filename) | |
;; Don't dive into .qlot/ и .svn/ | |
(loop for part in (pathname-directory filename) | |
when (or (string= part ".svn") | |
(string= part ".qlot")) | |
do (return nil) | |
finally (return t))) | |
(defun print-results (hash) | |
(loop with sorted-results = (sort (alexandria:hash-table-alist hash) | |
#'> | |
:key #'cdr) | |
for (login . line-count) in sorted-results | |
do (format t "~30,,5<~A~;кто:~A~>~%" | |
line-count | |
login))) | |
(defun process-all-files (&optional (dir "./")) | |
(let ((lparallel:*kernel* (lparallel:make-kernel 10))) | |
(unwind-protect | |
(let ((results (make-hash-table :test 'equal)) | |
(channel (lparallel:make-channel))) | |
(cl-fad:walk-directory | |
dir | |
(lambda (filename) | |
(lparallel:submit-task channel | |
'file-stats filename)) | |
:test 'ignore-p) | |
(lparallel:submit-task channel | |
(constantly :end)) | |
(loop for stats = (lparallel:receive-result channel) | |
then (lparallel:receive-result channel) | |
until (eql stats :end) | |
when stats | |
do (merge-hash-table-into results stats)) | |
(print-results results) | |
(values results)) | |
(format *error-output* "Shutting down LParallel threads...") | |
(lparallel:end-kernel)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment