Skip to content

Instantly share code, notes, and snippets.

@svetlyak40wt
Created January 22, 2021 20:46
Show Gist options
  • Save svetlyak40wt/6b987ff9a5fef966a8c180a331bcf352 to your computer and use it in GitHub Desktop.
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
(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