summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Isaac2022-06-23 19:07:46 +0530
committerArun Isaac2022-06-23 19:07:46 +0530
commit76d9a3a7c7b2d86060670b3a53210bad66dbc3c8 (patch)
tree6de76b9cee3a273d14ac1214f394e8f3f67868fc
parent49163adccd8ca1b60192dd54770e3525f1a2ad97 (diff)
downloadtissue-76d9a3a7c7b2d86060670b3a53210bad66dbc3c8.tar.gz
tissue-76d9a3a7c7b2d86060670b3a53210bad66dbc3c8.tar.lz
tissue-76d9a3a7c7b2d86060670b3a53210bad66dbc3c8.zip
issue: Lookup commits affecting file using guile-git.
* tissue/git.scm: Import (rnrs hashtables) and (ice-9 match). (commit-deltas): New function. (file-modification-table): New public function. * tissue/issue.scm: Import (git). (file-details): Do not lookup git history for file using `git log'. (issues): Use file-modification-table to lookup git history.
-rw-r--r--tissue/git.scm56
-rw-r--r--tissue/issue.scm90
2 files changed, 92 insertions, 54 deletions
diff --git a/tissue/git.scm b/tissue/git.scm
index 4b7f7c7..fcfc6c7 100644
--- a/tissue/git.scm
+++ b/tissue/git.scm
@@ -18,11 +18,13 @@
(define-module (tissue git)
#:use-module (rnrs arithmetic bitwise)
+ #:use-module (rnrs hashtables)
#:use-module (rnrs io ports)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-19)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-171)
+ #:use-module (ice-9 match)
#:use-module (git)
#:use-module (git types)
;; There are many name conflicts between (system foreign). So, we
@@ -42,7 +44,8 @@
#:export (git-top-level
current-git-repository
commit-date
- git-tracked-files))
+ git-tracked-files
+ file-modification-table))
;; We bind additional functions from libgit2 that are not already
;; bound in guile-git. TODO: Contribute them to guile-git.
@@ -197,3 +200,54 @@ filenames are relative to the top-level directory of REPOSITORY and do
not have a leading slash."
(map index-entry-path
(index-entries (repository-index repository))))
+
+(define (commit-deltas repository commit)
+ "Return the list of <diff-delta> objects created by COMMIT with
+respect to its first parent in REPOSITORY."
+ (match (commit-parents commit)
+ ((parent _ ...)
+ (let ((diff (diff-tree-to-tree repository
+ (commit-tree parent)
+ (commit-tree commit))))
+ (diff-find-similar! diff)
+ (diff-fold (lambda (delta progress result)
+ (cons delta result))
+ (lambda (delta binary result)
+ result)
+ (lambda (delta hunk result)
+ result)
+ (lambda (delta hunk line result)
+ result)
+ (list)
+ diff)))
+ (() (list))))
+
+(define (file-modification-table repository)
+ "Return a hashtable mapping files to the list of commits in REPOSITORY
+that modified them."
+ (let ((result (make-hashtable string-hash string=?))
+ (renames (make-hashtable string-hash string=?)))
+ (fold-commits
+ (lambda (commit _)
+ (map (lambda (delta)
+ ;; Map old filename to current filename if they are
+ ;; different. Note that this manner of following renames
+ ;; requires a linear git history and will not work with
+ ;; branch merges.
+ (unless (string=? (diff-file-path (diff-delta-old-file delta))
+ (diff-file-path (diff-delta-new-file delta)))
+ (hashtable-set! renames
+ (diff-file-path (diff-delta-old-file delta))
+ (diff-file-path (diff-delta-new-file delta))))
+ (hashtable-update! result
+ ;; If necessary, translate old
+ ;; filename to current filename.
+ (hashtable-ref renames
+ (diff-file-path (diff-delta-old-file delta))
+ (diff-file-path (diff-delta-old-file delta)))
+ (cut cons commit <>)
+ (list)))
+ (commit-deltas repository commit)))
+ #f
+ repository)
+ result))
diff --git a/tissue/issue.scm b/tissue/issue.scm
index f41576d..8f36d94 100644
--- a/tissue/issue.scm
+++ b/tissue/issue.scm
@@ -27,6 +27,7 @@
#:use-module (srfi srfi-171)
#:use-module (ice-9 match)
#:use-module (ice-9 regex)
+ #:use-module (git)
#:use-module (tissue git)
#:use-module (tissue utils)
#:export (%issue-files
@@ -225,32 +226,6 @@ in (tissue tissue). If no alias is found, NAME is returned as such."
(const #t)
get-line-dos-or-unix
port))))
- (call-with-input-pipe
- (lambda (port)
- (hashtable-set!
- result 'posts
- (reverse
- (port-transduce
- (compose (tenumerate)
- (tmap (match-lambda
- ((index . line)
- (let* ((alist (call-with-input-string line read))
- (author (resolve-alias (assq-ref alist 'author)
- (%aliases)))
- (date (assq-ref alist 'author-date)))
- (when (zero? index)
- (hashtable-set! result 'last-updater author)
- (hashtable-set! result 'last-updated-date (unix-time->date date)))
- (hashtable-set! result 'creator author)
- (hashtable-set! result 'created-date (unix-time->date date))
- (post author date))))))
- rcons get-line port))))
- "git" "log" "--follow"
- (string-append "--format=format:("
- "(author . \"%an\")"
- "(author-date . %at)"
- ")")
- "--" file)
result))
(define issues
@@ -260,30 +235,39 @@ in (tissue tissue). If no alias is found, NAME is returned as such."
;; Get all gemini files except README.gmi and hidden files. Text
;; editors tend to create hidden files while editing, and we want to
;; avoid them.
- (sort (filter-map (lambda (file)
- (let* ((file-details (file-details file))
- ;; Downcase keywords to make them
- ;; case-insensitive.
- (all-keywords (map string-downcase
- (hashtable-ref file-details 'keywords '()))))
- (issue file
- ;; Fallback to filename if title has no alphabetic
- ;; characters.
- (let ((title (hashtable-ref file-details 'title "")))
- (if (string-any char-set:letter title) title file))
- (hashtable-ref file-details 'creator #f)
- (hashtable-ref file-details 'created-date #f)
- (hashtable-ref file-details 'last-updater #f)
- (hashtable-ref file-details 'last-updated-date #f)
- (hashtable-ref file-details 'assigned '())
- ;; "closed" is a special keyword to indicate
- ;; the open/closed status of an issue.
- (delete "closed" all-keywords)
- (not (member "closed" all-keywords))
- (hashtable-ref file-details 'tasks 0)
- (hashtable-ref file-details 'completed-tasks 0)
- (hashtable-ref file-details 'posts #f))))
- (%issue-files))
- (lambda (issue1 issue2)
- (time<? (date->time-monotonic (issue-created-date issue1))
- (date->time-monotonic (issue-created-date issue2))))))))
+ (let ((file-modification-table (file-modification-table (current-git-repository))))
+ (sort (filter-map (lambda (file)
+ (let* ((file-details (file-details file))
+ ;; Downcase keywords to make them
+ ;; case-insensitive.
+ (all-keywords (map string-downcase
+ (hashtable-ref file-details 'keywords '())))
+ (commits (hashtable-ref file-modification-table file #f))
+ (commit-authors (map (lambda (commit)
+ (resolve-alias (signature-name (commit-author commit))
+ (%aliases)))
+ commits)))
+ (issue file
+ ;; Fallback to filename if title has no alphabetic
+ ;; characters.
+ (let ((title (hashtable-ref file-details 'title "")))
+ (if (string-any char-set:letter title) title file))
+ (first commit-authors)
+ (commit-date (first commits))
+ (last commit-authors)
+ (commit-date (last commits))
+ (hashtable-ref file-details 'assigned '())
+ ;; "closed" is a special keyword to indicate
+ ;; the open/closed status of an issue.
+ (delete "closed" all-keywords)
+ (not (member "closed" all-keywords))
+ (hashtable-ref file-details 'tasks 0)
+ (hashtable-ref file-details 'completed-tasks 0)
+ (map (lambda (commit author)
+ (post author (commit-date commit)))
+ commits
+ commit-authors))))
+ (%issue-files))
+ (lambda (issue1 issue2)
+ (time<? (date->time-monotonic (issue-created-date issue1))
+ (date->time-monotonic (issue-created-date issue2)))))))))