Browse code
Split files
Ed Langley authored on 03/05/2019 06:36:47
Showing 6 changed files
Showing 6 changed files
... | ... |
@@ -7,12 +7,16 @@ |
7 | 7 |
:license "MIT" |
8 | 8 |
:pathname #-fw.dev nil #+fw.dev #p"PROJECTS:cl-git;" |
9 | 9 |
:depends-on (:alexandria |
10 |
- :split-sequence |
|
11 |
- :cl-dot |
|
12 | 10 |
:chipz |
11 |
+ :cl-dot |
|
13 | 12 |
:data-lens |
14 | 13 |
:fwoar-lisputils |
15 | 14 |
:fwoar-lisputils/bin-parser |
15 |
+ :ironclad |
|
16 | 16 |
:serapeum |
17 |
+ :split-sequence |
|
17 | 18 |
:uiop) |
18 |
- :components ((:file "cl-git"))) |
|
19 |
+ :components ((:file "package") |
|
20 |
+ (:file "util" :depends-on ("package")) |
|
21 |
+ (:file "git" :depends-on ("package" "util")) |
|
22 |
+ (:file "porcelain" :depends-on ("package" "git")))) |
19 | 23 |
similarity index 67% |
20 | 24 |
rename from cl-git.lisp |
21 | 25 |
rename to git.lisp |
... | ... |
@@ -1,11 +1,21 @@ |
1 |
-(defpackage :fwoar.cl-git |
|
2 |
- (:use :cl ) |
|
3 |
- (:export )) |
|
4 | 1 |
(in-package :fwoar.cl-git) |
5 | 2 |
|
3 |
+(defparameter *object-data-lens* |
|
4 |
+ (data-lens.lenses:make-alist-lens :object-data)) |
|
5 |
+ |
|
6 |
+(defclass pack () |
|
7 |
+ ((%pack :initarg :pack :reader pack-file) |
|
8 |
+ (%index :initarg :index :reader index-file))) |
|
9 |
+ |
|
6 | 10 |
(defclass repository () |
7 | 11 |
((%root :initarg :root :reader root))) |
8 | 12 |
|
13 |
+(defclass git-object () |
|
14 |
+ ()) |
|
15 |
+ |
|
16 |
+(defclass commit (git-object) |
|
17 |
+ ()) |
|
18 |
+ |
|
9 | 19 |
(defun repository (root) |
10 | 20 |
(fw.lu:new 'repository root)) |
11 | 21 |
|
... | ... |
@@ -21,10 +31,6 @@ |
21 | 31 |
(let ((obj-path (fwoar.string-utils:insert-at 2 #\/ sha))) |
22 | 32 |
(merge-pathnames obj-path ".git/objects/"))) |
23 | 33 |
|
24 |
-(defclass pack () |
|
25 |
- ((%pack :initarg :pack :reader pack-file) |
|
26 |
- (%index :initarg :index :reader index-file))) |
|
27 |
- |
|
28 | 34 |
(defun pack (index pack) |
29 | 35 |
(fw.lu:new 'pack index pack)) |
30 | 36 |
|
... | ... |
@@ -47,9 +53,10 @@ |
47 | 53 |
(defun edges-in-fanout (toc s sha) |
48 | 54 |
(let* ((fanout-offset (getf toc :fanout))) |
49 | 55 |
(file-position s (+ fanout-offset (* 4 (1- (elt sha 0))))) |
50 |
- (destructuring-bind ((_ . cur) (__ . next)) (fwoar.bin-parser:extract '((cur 4 fwoar.bin-parser:be->int) |
|
51 |
- (next 4 fwoar.bin-parser:be->int)) |
|
52 |
- s) |
|
56 |
+ (destructuring-bind ((_ . cur) (__ . next)) |
|
57 |
+ (fwoar.bin-parser:extract '((cur 4 fwoar.bin-parser:be->int) |
|
58 |
+ (next 4 fwoar.bin-parser:be->int)) |
|
59 |
+ s) |
|
53 | 60 |
(declare (ignore _ __)) |
54 | 61 |
(values cur next)))) |
55 | 62 |
|
... | ... |
@@ -67,7 +74,8 @@ |
67 | 74 |
(t mid)))))) |
68 | 75 |
|
69 | 76 |
(defun find-pack-containing (pack-file id) |
70 |
- (with-open-file (s (index-file pack-file) :element-type '(unsigned-byte 8)) |
|
77 |
+ (with-open-file (s (index-file pack-file) |
|
78 |
+ :element-type '(unsigned-byte 8)) |
|
71 | 79 |
(let ((binary-sha (ironclad:hex-string-to-byte-array id)) |
72 | 80 |
(toc (idx-toc s))) |
73 | 81 |
(multiple-value-bind (_ end) (edges-in-fanout toc s binary-sha) |
... | ... |
@@ -87,6 +95,19 @@ |
87 | 95 |
(file-position p object-offset-in-pack) |
88 | 96 |
(read-object-from-pack p)))))) |
89 | 97 |
|
98 |
+(defun seek-to-object-in-pack (idx-stream pack-stream obj-number) |
|
99 |
+ (let* ((toc (idx-toc idx-stream)) |
|
100 |
+ (offset-offset (getf toc :4-byte-offsets))) |
|
101 |
+ (file-position idx-stream (+ offset-offset (* 4 obj-number))) |
|
102 |
+ (let ((object-offset-in-pack (read-bytes 4 'fwoar.bin-parser:be->int idx-stream))) |
|
103 |
+ (file-position pack-stream object-offset-in-pack)))) |
|
104 |
+ |
|
105 |
+(defun extract-object-metadata-from-pack (pack obj-number) |
|
106 |
+ (with-open-file (s (index-file pack) :element-type '(unsigned-byte 8)) |
|
107 |
+ (with-open-file (p (pack-file pack) :element-type '(unsigned-byte 8)) |
|
108 |
+ (seek-to-object-in-pack s p obj-number) |
|
109 |
+ (read-object-metadata-from-pack p)))) |
|
110 |
+ |
|
90 | 111 |
(defun extract-loose-object (repo id) |
91 | 112 |
(with-open-file (s (object repo id) |
92 | 113 |
:element-type '(unsigned-byte 8)) |
... | ... |
@@ -100,8 +121,6 @@ |
100 | 121 |
(multiple-value-call 'extract-object-from-pack |
101 | 122 |
(find-object-in-pack-files (root repo) id))))) |
102 | 123 |
|
103 |
-(defparameter *object-data-lens* |
|
104 |
- (data-lens.lenses:make-alist-lens :object-data)) |
|
105 | 124 |
|
106 | 125 |
(defun turn-read-object-to-string (object) |
107 | 126 |
(data-lens.lenses:over *object-data-lens* 'babel:octets-to-string object)) |
... | ... |
@@ -133,13 +152,6 @@ |
133 | 152 |
'fanout-table) |
134 | 153 |
'vector)) |
135 | 154 |
|
136 |
-(defun batch-4 (bytes) |
|
137 |
- (mapcar 'fwoar.bin-parser:be->int |
|
138 |
- (serapeum:batches bytes 4))) |
|
139 |
- |
|
140 |
-(defun batch-20 (bytes) |
|
141 |
- (serapeum:batches bytes 20)) |
|
142 |
- |
|
143 | 155 |
(defun get-object-size (bytes) |
144 | 156 |
(let ((first (elt bytes 0)) |
145 | 157 |
(rest (subseq bytes 1))) |
... | ... |
@@ -151,10 +163,6 @@ |
151 | 163 |
(ldb (byte 3 4) |
152 | 164 |
first))) |
153 | 165 |
|
154 |
-(serapeum:defalias ->sha-string |
|
155 |
- (<>1 (data-lens:over 'fwoar.bin-parser:byte-array-to-hex-string) |
|
156 |
- batch-20)) |
|
157 |
- |
|
158 | 166 |
(defun get-shas-before (fanout-table first-sha-byte s) |
159 | 167 |
(let ((num-before (elt fanout-table first-sha-byte)) |
160 | 168 |
(num-total (alexandria:last-elt fanout-table))) |
... | ... |
@@ -175,11 +183,6 @@ |
175 | 183 |
(fwoar.bin-parser:extract '((offset 4 fwoar.bin-parser:be->int)) |
176 | 184 |
s)) |
177 | 185 |
|
178 |
-(defmacro sym->plist (&rest syms) |
|
179 |
- `(list ,@(loop for sym in syms |
|
180 |
- append (list (alexandria:make-keyword sym) |
|
181 |
- sym)))) |
|
182 |
- |
|
183 | 186 |
(defun idx-toc (idx-stream) |
184 | 187 |
(let* ((object-count (progn (file-position idx-stream 1028) |
185 | 188 |
(let ((buf (make-array 4))) |
... | ... |
@@ -224,6 +227,7 @@ |
224 | 227 |
(crc-idx (getf idx-toc :packed-crcs)) |
225 | 228 |
(4-byte-offsets-idx (getf idx-toc :4-byte-offsets)) |
226 | 229 |
(8-byte-offsets-idx (getf idx-toc :8-byte-offsets))) |
230 |
+ (declare (ignore 8-byte-offsets-idx)) |
|
227 | 231 |
(values num |
228 | 232 |
(progn |
229 | 233 |
(file-position s (+ sha-idx (* num 20))) |
... | ... |
@@ -275,38 +279,6 @@ |
275 | 279 |
result)) |
276 | 280 |
))) |
277 | 281 |
|
278 |
-(defmacro inspect- (s form) |
|
279 |
- `(let ((result ,form)) |
|
280 |
- (format ,s "~&~s (~{~s~^ ~})~%~4t~s~%" |
|
281 |
- ',form |
|
282 |
- ,(typecase form |
|
283 |
- (list `(list ',(car form) ,@(cdr form))) |
|
284 |
- (t `(list ,form))) |
|
285 |
- result) |
|
286 |
- result)) |
|
287 |
- |
|
288 |
-(defun inspect-* (fn) |
|
289 |
- (lambda (&rest args) |
|
290 |
- (declare (dynamic-extent args)) |
|
291 |
- (inspect- *trace-output* |
|
292 |
- (apply fn args)))) |
|
293 |
- |
|
294 |
-(defun partition (char string &key from-end) |
|
295 |
- (let ((pos (position char string :from-end from-end))) |
|
296 |
- (if pos |
|
297 |
- (list (subseq string 0 pos) |
|
298 |
- (subseq string (1+ pos))) |
|
299 |
- (list string |
|
300 |
- nil)))) |
|
301 |
- |
|
302 |
-(defun partition-subseq (subseq string &key from-end) |
|
303 |
- (let ((pos (search subseq string :from-end from-end))) |
|
304 |
- (if pos |
|
305 |
- (list (subseq string 0 pos) |
|
306 |
- (subseq string (+ (length subseq) pos))) |
|
307 |
- (list string |
|
308 |
- nil)))) |
|
309 |
- |
|
310 | 282 |
(defun split-object (object-data) |
311 | 283 |
(destructuring-bind (head tail) |
312 | 284 |
(partition 0 |
... | ... |
@@ -318,10 +290,6 @@ |
318 | 290 |
(list type |
319 | 291 |
(parse-integer length)))))) |
320 | 292 |
|
321 |
-(defclass git-object () |
|
322 |
- ()) |
|
323 |
-(defclass commit (git-object) |
|
324 |
- ()) |
|
325 | 293 |
|
326 | 294 |
(defun parse-commit (commit) |
327 | 295 |
(destructuring-bind (metadata message) |
... | ... |
@@ -329,65 +297,3 @@ |
329 | 297 |
commit #+(or)(babel:octets-to-string commit :encoding :latin1)) |
330 | 298 |
(values message |
331 | 299 |
(fwoar.string-utils:split #\newline metadata)))) |
332 |
- |
|
333 |
-(defclass git-graph () |
|
334 |
- ((%repo :initarg :repo :reader repo) |
|
335 |
- (%depth :initarg :depth :reader depth) |
|
336 |
- (%branches :reader branches) |
|
337 |
- (%node-cache :reader node-cache :initform (make-hash-table :test 'equal)) |
|
338 |
- (%edge-cache :reader edge-cache :initform (make-hash-table :test 'equal)))) |
|
339 |
- |
|
340 |
-(defmethod initialize-instance :after ((object git-graph) &key) |
|
341 |
- (setf (slot-value object '%branches) |
|
342 |
- (fw.lu:alist-string-hash-table |
|
343 |
- (funcall (data-lens:over |
|
344 |
- (<>1 (data-lens:applying #'cons) |
|
345 |
- (data-lens:transform-head |
|
346 |
- (serapeum:op (subseq _1 0 (min (length _1) 7)))) |
|
347 |
- #'reverse)) |
|
348 |
- (branches (repo object)))))) |
|
349 |
- |
|
350 |
-(defun git-graph (repo) |
|
351 |
- (fw.lu:new 'git-graph repo)) |
|
352 |
- |
|
353 |
-(defun get-commit-parents (repository commit) |
|
354 |
- (map 'list |
|
355 |
- (serapeum:op (second (partition #\space _))) |
|
356 |
- (remove-if-not (lambda (it) |
|
357 |
- (serapeum:string-prefix-p "parent" it)) |
|
358 |
- (nth-value 1 (parse-commit |
|
359 |
- (split-object |
|
360 |
- (chipz:decompress nil (chipz:make-dstate 'chipz:zlib) |
|
361 |
- (object repository |
|
362 |
- commit)))))))) |
|
363 |
- |
|
364 |
-(defmethod cl-dot:graph-object-node ((graph git-graph) (commit string)) |
|
365 |
- (alexandria:ensure-gethash commit |
|
366 |
- (node-cache graph) |
|
367 |
- (make-instance 'cl-dot:node |
|
368 |
- :attributes `(:label ,(gethash #1=(subseq commit 0 7) |
|
369 |
- (branches graph) |
|
370 |
- #1#))))) |
|
371 |
- |
|
372 |
-(defmethod cl-dot:graph-object-points-to ((graph git-graph) (commit string)) |
|
373 |
- (mapcar (lambda (c) |
|
374 |
- (setf (gethash (list commit c) |
|
375 |
- (edge-cache graph)) |
|
376 |
- t) |
|
377 |
- c) |
|
378 |
- (remove-if (lambda (it) |
|
379 |
- (gethash (list commit it) |
|
380 |
- (edge-cache graph))) |
|
381 |
- (mapcar (serapeum:op (subseq _ 0 7)) |
|
382 |
- (get-commit-parents (repo graph) commit) |
|
383 |
- #+nil |
|
384 |
- (loop |
|
385 |
- for cur = (list commit) then parents |
|
386 |
- for parents = (let ((f (get-commit-parents (repo graph) (car cur)))) |
|
387 |
- f) |
|
388 |
- until (or (not parents) |
|
389 |
- (cdr parents)) |
|
390 |
- finally (return (or parents |
|
391 |
- (when (not (equal commit (car cur))) |
|
392 |
- cur)))))))) |
|
393 |
- |
394 | 300 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,62 @@ |
1 |
+(in-package :fwoar.cl-git) |
|
2 |
+ |
|
3 |
+(defclass git-graph () |
|
4 |
+ ((%repo :initarg :repo :reader repo) |
|
5 |
+ (%depth :initarg :depth :reader depth) |
|
6 |
+ (%branches :reader branches) |
|
7 |
+ (%node-cache :reader node-cache :initform (make-hash-table :test 'equal)) |
|
8 |
+ (%edge-cache :reader edge-cache :initform (make-hash-table :test 'equal)))) |
|
9 |
+ |
|
10 |
+(defmethod initialize-instance :after ((object git-graph) &key) |
|
11 |
+ (setf (slot-value object '%branches) |
|
12 |
+ (fw.lu:alist-string-hash-table |
|
13 |
+ (funcall (data-lens:over |
|
14 |
+ (<>1 (data-lens:applying #'cons) |
|
15 |
+ (data-lens:transform-head |
|
16 |
+ (serapeum:op (subseq _1 0 (min (length _1) 7)))) |
|
17 |
+ #'reverse)) |
|
18 |
+ (branches (repo object)))))) |
|
19 |
+ |
|
20 |
+(defun git-graph (repo) |
|
21 |
+ (fw.lu:new 'git-graph repo)) |
|
22 |
+ |
|
23 |
+(defun get-commit-parents (repository commit) |
|
24 |
+ (map 'list |
|
25 |
+ (serapeum:op (second (partition #\space _))) |
|
26 |
+ (remove-if-not (lambda (it) |
|
27 |
+ (serapeum:string-prefix-p "parent" it)) |
|
28 |
+ (nth-value 1 (parse-commit |
|
29 |
+ (split-object |
|
30 |
+ (chipz:decompress nil (chipz:make-dstate 'chipz:zlib) |
|
31 |
+ (object repository |
|
32 |
+ commit)))))))) |
|
33 |
+ |
|
34 |
+(defmethod cl-dot:graph-object-node ((graph git-graph) (commit string)) |
|
35 |
+ (alexandria:ensure-gethash commit |
|
36 |
+ (node-cache graph) |
|
37 |
+ (make-instance 'cl-dot:node |
|
38 |
+ :attributes `(:label ,(gethash #1=(subseq commit 0 7) |
|
39 |
+ (branches graph) |
|
40 |
+ #1#))))) |
|
41 |
+ |
|
42 |
+(defmethod cl-dot:graph-object-points-to ((graph git-graph) (commit string)) |
|
43 |
+ (mapcar (lambda (c) |
|
44 |
+ (setf (gethash (list commit c) |
|
45 |
+ (edge-cache graph)) |
|
46 |
+ t) |
|
47 |
+ c) |
|
48 |
+ (remove-if (lambda (it) |
|
49 |
+ (gethash (list commit it) |
|
50 |
+ (edge-cache graph))) |
|
51 |
+ (mapcar (serapeum:op (subseq _ 0 7)) |
|
52 |
+ (get-commit-parents (repo graph) commit) |
|
53 |
+ #+nil |
|
54 |
+ (loop |
|
55 |
+ for cur = (list commit) then parents |
|
56 |
+ for parents = (let ((f (get-commit-parents (repo graph) (car cur)))) |
|
57 |
+ f) |
|
58 |
+ until (or (not parents) |
|
59 |
+ (cdr parents)) |
|
60 |
+ finally (return (or parents |
|
61 |
+ (when (not (equal commit (car cur))) |
|
62 |
+ cur)))))))) |
0 | 9 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,11 @@ |
1 |
+(in-package :fwoar.cl-git) |
|
2 |
+ |
|
3 |
+(defvar *git-repository* nil |
|
4 |
+ "The git repository path for porcelain commands to operate on.") |
|
5 |
+(defvar *git-encoding* :utf-8 |
|
6 |
+ "The encoding to use when parsing git objects") |
|
7 |
+ |
|
8 |
+(defun git-show (object) |
|
9 |
+ (babel:octets-to-string (extract-object (repository *git-repository*) |
|
10 |
+ object) |
|
11 |
+ :encoding *git-encoding*)) |
0 | 12 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,49 @@ |
1 |
+(in-package :fwoar.cl-git) |
|
2 |
+ |
|
3 |
+(fw.lu:defun-ct batch-4 (bytes) |
|
4 |
+ (mapcar 'fwoar.bin-parser:be->int |
|
5 |
+ (serapeum:batches bytes 4))) |
|
6 |
+ |
|
7 |
+(fw.lu:defun-ct batch-20 (bytes) |
|
8 |
+ (serapeum:batches bytes 20)) |
|
9 |
+ |
|
10 |
+(defmacro sym->plist (&rest syms) |
|
11 |
+ `(list ,@(loop for sym in syms |
|
12 |
+ append (list (alexandria:make-keyword sym) |
|
13 |
+ sym)))) |
|
14 |
+ |
|
15 |
+(defmacro inspect- (s form) |
|
16 |
+ `(let ((result ,form)) |
|
17 |
+ (format ,s "~&~s (~{~s~^ ~})~%~4t~s~%" |
|
18 |
+ ',form |
|
19 |
+ ,(typecase form |
|
20 |
+ (list `(list ',(car form) ,@(cdr form))) |
|
21 |
+ (t `(list ,form))) |
|
22 |
+ result) |
|
23 |
+ result)) |
|
24 |
+ |
|
25 |
+(defun inspect-* (fn) |
|
26 |
+ (lambda (&rest args) |
|
27 |
+ (declare (dynamic-extent args)) |
|
28 |
+ (inspect- *trace-output* |
|
29 |
+ (apply fn args)))) |
|
30 |
+ |
|
31 |
+(defun partition (char string &key from-end) |
|
32 |
+ (let ((pos (position char string :from-end from-end))) |
|
33 |
+ (if pos |
|
34 |
+ (list (subseq string 0 pos) |
|
35 |
+ (subseq string (1+ pos))) |
|
36 |
+ (list string |
|
37 |
+ nil)))) |
|
38 |
+ |
|
39 |
+(defun partition-subseq (subseq string &key from-end) |
|
40 |
+ (let ((pos (search subseq string :from-end from-end))) |
|
41 |
+ (if pos |
|
42 |
+ (list (subseq string 0 pos) |
|
43 |
+ (subseq string (+ (length subseq) pos))) |
|
44 |
+ (list string |
|
45 |
+ nil)))) |
|
46 |
+ |
|
47 |
+(serapeum:defalias ->sha-string |
|
48 |
+ (data-lens:<>1 (data-lens:over 'fwoar.bin-parser:byte-array-to-hex-string) |
|
49 |
+ 'batch-20)) |