summaryrefslogtreecommitdiffstatshomepage
path: root/tekuti/git.scm
diff options
context:
space:
mode:
Diffstat (limited to 'tekuti/git.scm')
-rw-r--r--tekuti/git.scm170
1 files changed, 102 insertions, 68 deletions
diff --git a/tekuti/git.scm b/tekuti/git.scm
index 723882a..d154f7c 100644
--- a/tekuti/git.scm
+++ b/tekuti/git.scm
@@ -20,7 +20,7 @@
;;; Commentary:
;;
-;; This is the main script that will launch tekuti.
+;; Using git's object database as a persistent store.
;;
;;; Code:
@@ -31,20 +31,25 @@
#:use-module (tekuti config)
#:use-module (scheme kwargs)
#:use-module (match-bind)
+ #:use-module ((srfi srfi-1) #:select (filter-map partition))
+ #:use-module (srfi srfi-11) ; let-values
#:use-module (srfi srfi-34)
#:use-module (srfi srfi-35)
#:export (&git-condition git-condition? git-condition-argv
git-condition-output git-condition-status false-if-git-error
git git* ensure-git-repo git-ls-tree git-ls-subdirs
- parse-metadata parse-commit commit-utc-timestamp
- commit-parents git-mktree git-rev-parse
- git-hash-object git-update-ref
+ git-mktree git-rev-parse git-hash-object git-update-ref
git-commit-tree
- with-output-to-blob with-input-from-blob
+ munge-tree parse-commit commit-utc-timestamp
+
+ with-output-to-blob with-input-from-blob))
- write-indices read-indices))
+
+;;;
+;;; git conditions
+;;;
(define-condition-type &git-condition &condition git-condition?
(argv git-condition-argv)
@@ -55,6 +60,10 @@
`(,guard (c ((,git-condition? c) #f))
,@body))
+;;;
+;;; running git
+;;;
+
(define *debug* #f)
(define (trc . args)
(if *debug*
@@ -86,17 +95,7 @@
(output output)
(status ret))))))))
-(define (call-with-temp-file contents proc)
- (let* ((template (string-copy "/tmp/tekutiXXXXXX"))
- (tmp (mkstemp! template)))
- (display contents tmp)
- (close tmp)
- (unwind-protect
- (proc template)
- (delete-file template))))
-
(define/kwargs (git* args (input #f) (env '()))
- ;; foolishness regarding env
(if input
(call-with-temp-file
input
@@ -108,6 +107,10 @@
(define (git . args)
(git* args))
+;;;
+;;; git commands
+;;;
+
(define (is-dir? path)
(catch 'system-error
(lambda () (eq? (stat:type (stat path)) 'directory))
@@ -137,45 +140,6 @@
(cons name object)))
'()))
-(define (parse-metadata treeish specs)
- (filter
- identity
- (match-lines (git "show" treeish)
- "^([^: ]+): +(.*)$" (_ k v)
- (let* ((k (string->symbol k))
- (parse (assq-ref specs k)))
- (if parse
- (catch 'parse-error
- (lambda ()
- (cons k (parse v)))
- (lambda args #f))
- (cons k v))))))
-
-(define (parse-commit commit)
- (let ((text (git "cat-file" "commit" commit)))
- (match-bind
- "\n\n(.*)$" text (_ message)
- (acons
- 'message message
- (match-lines (substring text 0 (- (string-length text) (string-length _)))
- "^([^ ]+) (.*)$" (_ k v)
- (cons (string->symbol k) v))))))
-
-(define (commit-utc-timestamp commit)
- (match-bind
- "^(.*) ([0-9]+) ([+-][0-9]+)" (assq-ref (parse-commit commit) 'committer)
- (_ who ts tz)
- (let ((ts (string->number ts)) (tz (string->number tz)))
- (- ts (* (+ (* (quotient tz 100) 60) (remainder tz 100)) 60)))))
-
-;; fixme: do to commits what i already did to posts
-
-(define (commit-parents commit)
- (map cdr
- (filter
- (lambda (x) (eq? (car x) 'parent))
- (parse-commit commit))))
-
(define (git-mktree alist)
(string-trim-both
(git* '("mktree")
@@ -197,18 +161,6 @@
(string-trim-both
(git* '("hash-object" "-w" "--stdin") #:input contents)))
-(define (with-output-to-blob* thunk)
- (git-hash-object (with-output-to-string thunk)))
-
-(define-macro (with-output-to-blob . forms)
- `(,with-output-to-blob* (lambda () ,@forms)))
-
-(define (with-input-from-blob* sha1 thunk)
- (with-input-from-string (git "show" sha1) thunk))
-
-(define-macro (with-input-from-blob sha1 . forms)
- `(,with-input-from-blob* ,sha1 (lambda () ,@forms)))
-
(define (git-update-ref refname proc count)
(let* ((ref (git-rev-parse refname))
(commit (proc ref)))
@@ -233,5 +185,87 @@
(format #f "GIT_AUTHOR_DATE=~a +0100" timestamp))
(list "GIT_COMMMITTER=tekuti")))))
-;; fixme: map-pairs
+;;;
+;;; utilities
+;;;
+
+(define (munge-tree treeish add remove change)
+ (define (local? x) (null? (car x)))
+ (define (assert-added-files-not-present names dents)
+ (for-each
+ (lambda (dent)
+ (if (member (car dent) names)
+ (error "file already added" dent)))
+ dents))
+ (define (assert-referenced-files-present names dents)
+ (for-each
+ (lambda (name)
+ (if (not (assoc name dent-names))
+ (error "file already removed" name)))
+ names))
+ (let-values (((dents) (if treeish (git-ls-tree treeish #f) '()))
+ ((ladd dadd) (partition local? add))
+ ((lremove dremove) (partition local? remove))
+ ((lchange dchange) (partition local? change)))
+ (assert-added-files-not-present (map cadr ladd) dents)
+ (assert-referenced-files-present
+ (append (map cdr lremove) (map caar lchange)) dents)
+ ; (trc 'munge-tree treeish add remove change)
+ (git-mktree
+ (append
+ (map cdr ladd)
+ (filter-map
+ (lambda (dent)
+ (cond
+ ((member (car dent) (map cdr lremove))
+ #f)
+ ((member (car dent) (map cadr lchange))
+ (cdr lchange))
+ ((and (eq? (caddr dent) 'tree)
+ (member (car dent)
+ (map caar (append dadd dremove dchange))))
+ (let ((level-down (lambda (x)
+ (if (equal? (caar x) (car dent))
+ (cons (cdar x) (cdr x))
+ #f))))
+ (list (car dent)
+ (munge-tree (cadr dent)
+ (filter-map level-down dadd)
+ (filter-map level-down dremove)
+ (filter-map level-down dchange))
+ 'tree)))
+ (else dent)))
+ (append (filter-map (lambda (x)
+ (and (not (assoc (caar x) dents))
+ (list (caar x) #f 'tree)))
+ dadd)
+ dents))))))
+
+(define (parse-commit commit)
+ (let ((text (git "cat-file" "commit" commit)))
+ (match-bind
+ "\n\n(.*)$" text (_ message)
+ (acons
+ 'message message
+ (match-lines (substring text 0 (- (string-length text) (string-length _)))
+ "^([^ ]+) (.*)$" (_ k v)
+ (cons (string->symbol k) v))))))
+(define (commit-utc-timestamp commit)
+ (match-bind
+ "^(.*) ([0-9]+) ([+-][0-9]+)" (assq-ref (parse-commit commit) 'committer)
+ (_ who ts tz)
+ (let ((ts (string->number ts)) (tz (string->number tz)))
+ (- ts (* (+ (* (quotient tz 100) 60) (remainder tz 100)) 60)))))
+
+(define (with-output-to-blob* thunk)
+ (git-hash-object (with-output-to-string thunk)))
+
+(define-macro (with-output-to-blob . forms)
+ `(,with-output-to-blob* (lambda () ,@forms)))
+
+(define (with-input-from-blob* sha1 thunk)
+ (with-input-from-string (git "show" sha1) thunk))
+
+(define-macro (with-input-from-blob sha1 . forms)
+ `(,with-input-from-blob* ,sha1 (lambda () ,@forms)))