git.fiddlerwoaroof.com
model.lisp
a7c6e622
 (in-package :fwoar.cl-git)
 
 (defparameter *object-data-lens*
   (data-lens.lenses:make-alist-lens :object-data))
 
 (defclass pack ()
   ((%pack :initarg :pack :reader pack-file)
0b3ed859
    (%index :initarg :index :reader index-file)
    (%repository :initarg :repository :reader repository)))
a7c6e622
 
 (defclass repository ()
   ((%root :initarg :root :reader root)))
ea4edd3f
 (defclass git-repository (repository)
   ())
a7c6e622
 
 (defclass git-object ()
489818ad
   ((%hash :initarg :hash :accessor hash)))
a7c6e622
 
077088c8
 (defgeneric object-type->sym (object-type)
cea280bb
   (:documentation "Canonicalizes different representations of an
   object type to their symbol representation."))
 
 (defmethod object-type->sym ((o-t symbol))
     o-t)
077088c8
 
 (defmethod object-type->sym ((object-type number))
a7c6e622
   (ecase object-type
     (1 :commit)
     (2 :tree)
     (3 :blob)
     (4 :tag)
     (6 :ofs-delta)
     (7 :ref-delta)))
4cc1ee49
 
077088c8
 (defmethod object-type->sym ((object-type string))
   (string-case:string-case ((string-downcase object-type))
     ("commit" :commit)
     ("tree" :tree)
     ("blob" :blob)
     ("tag" :tag)
     ("ofs-delta" :ofs-delta)
     ("ref-delta" :ref-delta)))
a7c6e622
 
ea4edd3f
 (define-condition alts-fallthrough (error)
   ((%fallthrough-message :initarg :fallthrough-message :reader fallthrough-message)
    (%args :initarg :args :reader args))
   (:report (lambda (c s)
              (format s "~a ~s"
                      (fallthrough-message c)
                      (args c)))))
 
 ;; TODO: figure out how to handle ambiguity? restarts?
 (define-method-combination alts (&key fallthrough-message) ((methods *))
   (:arguments arg)
   (progn
     (mapc (serapeum:op
             (let ((qualifiers (method-qualifiers _1)))
               (unless (and (eql 'alts (car qualifiers))
                            (if (null (cdr qualifiers))
                                t
                                (and (symbolp (cadr qualifiers))
                                     (null (cddr qualifiers)))))
                 (invalid-method-error _1 "invalid qualifiers: ~s" qualifiers))))
           methods)
     `(or ,@(mapcar (serapeum:op `(call-method ,_1))
                    methods)
          (error 'alts-fallthrough
                 :fallthrough-message ,fallthrough-message
                 :args ,arg))))
 
 (defgeneric resolve-repository (object)
   (:documentation "resolve an OBJECT to a repository implementation")
   (:method-combination alts :fallthrough-message "failed to resolve repository"))
 
 (defmethod resolve-repository alts :git ((root pathname))
   (alexandria:when-let ((root (probe-file root)))
     (let* ((git-dir (merge-pathnames (make-pathname :directory '(:relative ".git"))
                                      root)))
       (when (probe-file git-dir)
         (fw.lu:new 'git-repository root)))))
 
 (defgeneric repository (object)
   (:documentation "get the repository for an object")
f5f88835
   (:method ((root repository))
     root)
ea4edd3f
   (:method ((root pathname))
     (resolve-repository root))
0b3ed859
   (:method ((root string))
216c17e7
     (let ((root (parse-namestring root)))
ea4edd3f
       (repository root))))
a7c6e622
 
 (defun get-local-branches (root)
   (append (get-local-unpacked-branches root)
           (get-local-packed-branches root)))
 
 (defun loose-object-path (sha)
   (let ((obj-path (fwoar.string-utils:insert-at 2 #\/ sha)))
     (merge-pathnames obj-path ".git/objects/")))
 
0b3ed859
 (defun pack (index pack repository)
   (fw.lu:new 'pack index pack repository))
a7c6e622
 
6df34ee9
 (defmacro with-pack-streams ((idx-sym pack-sym) pack &body body)
   (alexandria:once-only (pack)
     `(with-open-file (,idx-sym (index-file ,pack) :element-type 'fwoar.cl-git.types:octet)
        (with-open-file (,pack-sym (pack-file ,pack) :element-type 'fwoar.cl-git.types:octet)
          ,@body))))
 
ee4281cb
 (defgeneric pack-files (repo)
   (:method ((repo git-repository))
     (mapcar (serapeum:op
               (pack _1
                     (merge-pathnames
                      (make-pathname :type "pack") _1)
                     repo))
             (uiop:directory*
              (merge-pathnames ".git/objects/pack/*.idx"
                               (root-of repo))))))
846489f7
 
 (defgeneric loose-object (repository id)
   (:method ((repository string) id)
     (when (probe-file (merge-pathnames ".git" repository))
bb861f9e
       (loose-object (repository repository)
                     id)))
846489f7
   (:method ((repository pathname) id)
     (when (probe-file (merge-pathnames ".git" repository))
bb861f9e
       (loose-object (repository repository)
                     id)))
846489f7
   (:method ((repository repository) id)
     (car
      (uiop:directory*
       (merge-pathnames (loose-object-path (serapeum:concat id "*"))
                        (root repository))))))
 
 (defun loose-object-p (repository id)
   "Is ID an ID of a loose object?"
   (loose-object repository id))
 
4cc1ee49
 (defclass git-ref ()
   ((%repo :initarg :repo :reader ref-repo)
    (%hash :initarg :hash :reader ref-hash)))
 (defclass loose-ref (git-ref)
   ((%file :initarg :file :reader loose-ref-file)))
 (defclass packed-ref (git-ref)
   ((%pack :initarg :pack :reader packed-ref-pack)
    (%offset :initarg :offset :reader packed-ref-offset)))
 
 (defmethod print-object ((obj git-ref) s)
bb861f9e
   (print-unreadable-object (obj s :type t :identity t)
846489f7
     (format s "~a of ~a"
bb861f9e
             (subseq (ref-hash obj) 0 6)
ee4281cb
             (ref-repo obj)
             #+(or)
846489f7
             (serapeum:string-replace (namestring (user-homedir-pathname))
4cc1ee49
                                      (root-of (ref-repo obj))
846489f7
                                      "~/"))))
489818ad
 
 (defmethod component ((component (eql :hash)) (object git-object))
   (hash object))
69fc740c
 (defmethod component ((component (eql :hash)) (object git-ref))
   (ref-hash object))