git.fiddlerwoaroof.com
porcelain.lisp
991d0162
 (in-package :fwoar.cl-git)
 
e24e2318
 ;; TODO: Update the code so this uses an object instead of a path.
 (defvar *git-repository*)
 (setf (documentation '*git-repository* 'variable)
       "The git repository path for porcelain commands to operate on.")
 
991d0162
 (defvar *git-encoding* :utf-8
   "The encoding to use when parsing git objects")
 
bdf26afb
 (defun co.fwoar.git:in-repository (root)
e623be68
   (setf *git-repository*
216c17e7
         (ensure-repository
          (truename root))))
e623be68
 
bdf26afb
 (defun co.fwoar.git:repository ()
c672c979
   *git-repository*)
 
bdf26afb
 (defmacro co.fwoar.git:with-repository ((root) &body body)
216c17e7
   `(let ((*git-repository* (ensure-repository ,root)))
b7df27f1
      ,@body))
 
bdf26afb
 (defun co.fwoar.git:show-repository ()
ad9b8a82
   *git-repository*)
 
821ddf96
 (defun in-git-package (symbol)
   (intern (symbol-name symbol)
bdf26afb
           :co.fwoar.git))
821ddf96
 
71da880a
 (defun handle-list (_1)
   (case (in-git-package (car _1))
bdf26afb
     (co.fwoar.git::unwrap `(uiop:nest (car)
                                       (mapcar ,@(cdr _1))))
71da880a
     (t (cons (in-git-package (car _1))
              (cdr _1)))))
 
bdf26afb
 (defun co.fwoar.git::resolve-refish (it)
69664515
   (flet ((hash-p (it)
            (and (> (length it) 32)
                 (every (serapeum:op
                          (digit-char-p _1 16))
                        it))))
     (cond
a77dddb5
       ((block is-branch
          (mapc (fw.lu:destructuring-lambda ((name hash))
                  (when (equal it name)
                    (return-from is-branch
                      (ensure-ref hash))))
                (branches *git-repository*))
          nil))
69664515
       ((hash-p it) (ensure-ref it)))))
 
bdf26afb
 (defmacro co.fwoar.git:git (&rest commands)
0b3ed859
   `(uiop:nest ,@(reverse
69664515
                  (funcall (data-lens:<>1
                            (data-lens:over (serapeum:op
                                              (typecase _1
                                                (string `(identity ,_1))
                                                (list (handle-list _1)))))
                            (data-lens:transform-head (serapeum:op
                                                        (etypecase _1
bdf26afb
                                                          (string `(co.fwoar.git::resolve-refish ,_1))
69664515
                                                          (t _1)))))
                           commands))))
71da880a
 
bdf26afb
 (defun co.fwoar.git::ensure-ref (it)
b7df27f1
   (ensure-ref it))
 
bdf26afb
 (defun co.fwoar.git::decode (it)
69664515
   (babel:octets-to-string it :encoding *git-encoding*))
 
bdf26afb
 (defun co.fwoar.git::<<= (fun &rest args)
821ddf96
   (apply #'mapcan fun args))
 
bdf26afb
 (defmacro co.fwoar.git::map (fun list)
b7df27f1
   (alexandria:once-only (list)
     (alexandria:with-gensyms (it)
       `(mapcar ,(if (consp fun)
                     `(lambda (,it)
                        (,(in-git-package (car fun))
                         ,@(cdr fun)
                         ,it))
                     `',(in-git-package fun))
                ,list))))
 
bdf26afb
 (defmacro co.fwoar.git::juxt (&rest args)
b7df27f1
   (let ((funs (butlast args))
         (arg (car (last args))))
     (alexandria:once-only (arg)
       `(list ,@(mapcar (lambda (f)
                          `(,@(alexandria:ensure-list f) ,arg))
                        funs)))))
 
bdf26afb
 (defmacro co.fwoar.git::pipe (&rest funs)
71da880a
   (let ((funs (reverse (butlast funs)))
         (var (car (last funs))))
     `(uiop:nest ,@(mapcar (lambda (it)
                             (if (consp it)
                                 `(,(in-git-package (car it)) ,@(cdr it))
                                 `(,(in-git-package it))))
                           funs)
                 ,var)))
 
bdf26afb
 (defun co.fwoar.git::filter (fun &rest args)
b7df27f1
   (apply #'remove-if-not fun args))
 
bdf26afb
 (defun co.fwoar.git::object (thing)
b7df27f1
   (extract-object thing))
0c24c8f9
 
bdf26afb
 (defun co.fwoar.git:show (object)
69664515
   (extract-object object))
e623be68
 
bdf26afb
 (defun co.fwoar.git:contents (object)
   (co.fwoar.git:show object))
5cb25684
 
bdf26afb
 (defun co.fwoar.git:component (&rest args)
b7df27f1
   (let ((component-list (butlast args))
         (target (car (last args))))
     (fwoar.cl-git::component component-list target)))
 
bdf26afb
 (defun co.fwoar.git:tree (commit-object)
b7df27f1
   (component :tree
              commit-object))
5cb25684
 
bdf26afb
 (defun co.fwoar.git::filter-tree (name-pattern tree)
5cb25684
   #+lispworks
   (declare (notinline serapeum:string-prefix-p))
1869d7ea
   (let* ((tree-entries (component :entries tree))
          (scanner (cl-ppcre:create-scanner name-pattern)))
5cb25684
     (remove-if-not (serapeum:op
1869d7ea
                      (cl-ppcre:scan scanner _))
                    tree-entries
                    :key #'te-name)))
5cb25684
 
bdf26afb
 (defun co.fwoar.git:branch (&optional (branch :master))
0b3ed859
   #+lispworks
   (declare (notinline serapeum:assocadr))
216c17e7
   (let ((branches (branches *git-repository*)))
     (ref *git-repository*
b7df27f1
          (serapeum:assocadr (etypecase branch
                               (string branch)
                               (keyword (string-downcase branch)))
                             branches
                             :test 'equal))))
e623be68
 
bdf26afb
 (defun co.fwoar.git:branches ()
216c17e7
   (branches *git-repository*))
e623be68
 
bdf26afb
 (defun co.fwoar.git::parents (commit)
aa622e50
   (mapcar 'ensure-ref
           (component :parents commit)))
bdf26afb
 (defun co.fwoar.git:commit-parents (commit)
   (co.fwoar.git::parents commit))
961c04e3
 
bdf26afb
 (defun co.fwoar.git:rev-list (ref-id &optional (limit nil limit-p))
961c04e3
   "Return the commits reachable from the ref."
2d105ecd
   (when limit-p
     (rotatef ref-id limit))
c584afa5
   (let ((seen (make-hash-table)))
     (labels ((iterate (queue accum &optional (count 0))
                (if (or (when limit-p
                          (= limit count))
                        (null queue))
                    accum
                    (destructuring-bind (next . rest) queue
                      (let ((parents (co.fwoar.git::parents next)))
                        (iterate (append rest parents)
                          (if (gethash next seen)
                              accum
                              (progn
                                (setf (gethash next seen) t)
                                (cons next accum)))
                          (1+ count)))))))
       (iterate (list (ensure-ref ref-id))
         ()))))