perf(scanner): single-pass group-hunks-by-file

ober

441fd88df01c69e9d98df068afc57a40623142cb

diff --git a/gitsafe/scanner.ss b/gitsafe/scanner.ss
index 1ef2d29..4e2a3df 100644
--- a/gitsafe/scanner.ss
+++ b/gitsafe/scanner.ss
@@ -752,12 +752,20 @@
              [else #f]))))
 
   (def (group-hunks-by-file hunks)
-    (map (lambda (file)
-           (list file
-                 (filter (lambda (h)
-                           (string=? (diff-hunk-file h) file))
-                         hunks)))
-         (unique (map diff-hunk-file hunks))))
+    (let loop ([hs hunks] [table (make-hash-table)] [order '()])
+      (if (null? hs)
+        (map (lambda (file)
+               (list file (reverse (hash-ref table file '()))))
+             (reverse order))
+        (let* ([h    (car hs)]
+               [file (diff-hunk-file h)])
+          (if (hash-key? table file)
+            (begin
+              (hash-put! table file (cons h (hash-ref table file '())))
+              (loop (cdr hs) table order))
+            (begin
+              (hash-put! table file (list h))
+              (loop (cdr hs) table (cons file order))))))))
 
   ;; --- Scan diff hunks (added lines only) ---
   ;; Callers (scan-staged, scan-push-range) only ever pass hunks belonging