Files
git-cli/main.rkt
T
2026-08-12 22:28:27 +02:00

171 lines
5.7 KiB
Racket

#lang racket/base
(require "private/git-provider.rkt"
"private/git-commands.rkt"
"private/config.rkt"
"private/diff.rkt"
simple-log
racket/string
net/sendurl
)
(provide git
git-add
git-status
git-commit
git-pull
git-push
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Provided commands
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define git-commands (make-hash))
(define-syntax git
(syntax-rules ()
((_ command b1 ...)
((hash-ref git-commands command
(λ ()
(error "Not a supported or recognized git command: " command)))
(list b1 ...)))
)
)
(define (add-porcelain args info . f)
(let ((m (has-git-arg? args #px"^[-][-]porcelain([=](.*))?"))
(g (if (null? f) (λ (x) x) (car f)))
)
(if m
(g args)
(g (cons '--porcelain=v1 args)))))
(define-syntax def-cmd
(syntax-rules ()
((_ cmd cmd* cmd-sym)
(def-cmd cmd cmd* cmd-sym (λ (args info) args) std-process-git-result))
((_ cmd cmd* cmd-sym pre-code)
(def-cmd cmd cmd* cmd-sym pre-code std-process-git-result))
((_ cmd cmd* cmd-sym pre-code process-result)
(begin
(def-git-cmd-proxy cmd* cmd-sym
pre-code
process-result)
(define (cmd . args) (cmd* args))
(hash-set! git-commands cmd-sym cmd*)))
)
)
(def-cmd git-status cmd-git-status 'status
add-porcelain
(λ (cmd exit-code result output out info)
(dbg-git (format "~a" output))
(if (= exit-code 0)
(if result
(map (λ (line)
(let* ((state (string->symbol (string-trim (substring line 0 2))))
(file (string-trim (substring line 3))))
(cond
([eq? state '??] (list 'new file))
([eq? state 'M] (list 'modified file))
([eq? state 'A] (list 'added file))
([eq? state 'D] (list 'deleted file))
([eq? state 'AM] (list 'modified file))
([eq? state 'AD] (list 'deleted file))
([eq? state 'MM] (list 'modified file))
([eq? state 'MD] (list 'deleted file))
(else
(git-error 'status "Unexpected state" state))
)
))
out)
(git-error 'status "Error" output))
(git-error 'status "Exitcode <> 0" (cons (format "~a\n" exit-code) output))
))
)
(def-cmd git-add cmd-git-add 'add)
(def-cmd git-commit cmd-git-commit 'commit
(λ (args) (check-git-args 'commit args '((-m 1 "A commit message is mandatory"))))
(λ (cmd exit-code result output out)
(cond
((= exit-code 1) (git-displ out) #t)
(std-process-git-result cmd exit-code result output out)))
)
(def-cmd git-push cmd-git-push 'push add-porcelain)
(def-cmd git-pull cmd-git-pull 'pull)
(def-cmd git-branch cmd-git-branch 'branch)
(def-cmd git-clone cmd-git-clone 'clone)
(def-cmd git-log cmd-git-log 'log)
(def-cmd git-rev-list cmd-git-rev-list 'rev-list)
(def-cmd git-diff cmd-git-diff 'diff
(λ (args info) args)
(λ (cmd exit-code result output out info)
(if (and (zero? exit-code)
result)
(let ((diff (string-join
(filter (λ (line)
(not (string-prefix? (string-downcase line) "warning:")))
out)
"\n")))
(diff->html diff)
#t)
#f)))
(def-cmd git-grep cmd-git-grep 'grep
(λ (args info)
(let ((matches #f)
(line-nr #f)
)
(let ((nargs (map
(λ (e)
(let ((o (format "~a" e)))
(cond
((string=? o "-c") (set! matches #t))
((string=? o "-n") (set! line-nr #t))))
e)
(map (λ (x) (if (eq? x '-i) "-i" x)) args))))
(when (eq? line-nr #f)
(set! nargs (cons "-n" nargs))) ;; add line numbers / counts for pattern recognition
(hash-set! info 'matches matches)
(hash-set! info 'line-nr (if matches #f line-nr))
nargs)))
(λ (cmd exit-code result output out info)
(with-handlers ([exn:fail? (λ (e)
(err-git (string-join (map cadr output) "\n"))
(raise e))])
(if (and result
(or (= exit-code 0) (= exit-code 1)))
(let ((re #px"([^:]+)[:]([^:]+)([:](.*))?"))
(map (λ (line)
(let ((m (regexp-match re line)))
(let ((line-nr (if (hash-ref info 'line-nr #f)
(if (eq? m #f)
#f
(string->number (caddr m)))
#f))
(matches (if (hash-ref info 'matches #f)
(if (eq? m #f)
#f
(string->number (caddr m)))
#f))
)
(if m
(list (cadr m) line-nr matches (cadddr (cdr m)))
(list line #f #f #f)))))
out))
(begin
(err-git (string-join (map cadr output) "\n"))
#f))))
)
(def-cmd git-help cmd-git-help 'help)