Commits

evhan committed 63871f0 Merge

Merge branch 'development'

  • Participants
  • Parent commits 5d17e01, d80fdb8

Comments (0)

Files changed (7)

File git-exports.scm

 (export
+ blob
  blob*
  blob*-binary?
  blob*-content
  blob*-id
  blob*-size
  blob*?
- blob
  blob-binary?
  blob-content
  blob-id
  config-set!
  config-unset!
  config?
- create-blob*
  create-blob
+ create-blob*
  create-branch
  create-commit
  create-note
  create-reference
+ create-remote
  create-repository
  create-tag
  create-tree
  delete-note
  diff
+ diff->string
  diff-delta-hunks
  diff-delta-new-file
  diff-delta-old-file
  diff-delta-path
  diff-delta-status
  diff-delta?
+ diff-deltas
  diff-file-flags
  diff-file-id
  diff-file-mode
  diff-fold
  diff-hunk-header
  diff-hunk-header-len
+ diff-hunk-header-length
  diff-hunk-lines
  diff-hunk-new-lines
  diff-hunk-new-start
  diff-hunk?
  diff-line-content
  diff-line-content-len
+ diff-line-content-length
  diff-line-content-offset
  diff-line-new-lineno
  diff-line-num-lines
  diff-line-origin
  diff-line?
  diff-num-deltas
+ diff-patch
+ diff-patches
+ diff-repository
  diff?
  diffs
  file-ignored?
  note-id
  note-message
  note-repository
+ note?
  notes
  notes-fold
  object-id
  oid=?
  oid?
  parse-revision-specification
+ patch
+ patch->string
+ patch-size
+ patch-stats
+ patch?
  reference
  reference-branch?
  reference-delete
  reference-rename
  reference-repository
  reference-resolve
+ reference-tag?
  reference-target
  reference-target-set!
  reference-type
  refspec-destination
  refspec-direction
  refspec-force
+ refspec-force?
  refspec-source
  refspec-string
  refspec?
  remote
- remote-connect
+ remote-connect!
  remote-connected?
- remote-disconnect
- ;remote-download
+ remote-disconnect!
+ remote-download!
+ remote-fetch!
  remote-name
  remote-pushurl
  remote-pushurl-set!
+ remote-refspec-add!
  remote-refspecs
  remote-repository
  remote-stats
  remote-update-fetchhead-set!
- remote-update-tips
+ remote-update-tips!
  remote-url
  remote-url-set!
  remote-url-supported?
  tree-builder-ref
  tree-builder-remove
  tree-builder-write
+ tree-builder?
  tree-entries
  tree-entry->object
+ tree-entry-attributes
  tree-entry-id
  tree-entry-name
  tree-entry-owner

File git-lolevel-exports.scm

  diff-num-deltas
  diff-num-deltas-of-type
  diff-hunk-header
- diff-hunk-header-len
+ diff-hunk-header-length
  diff-hunk-new-lines
  diff-hunk-new-start
  diff-hunk-old-lines
  diff-hunk-old-start
  diff-index-to-workdir
  diff-line-content
- diff-line-content-len
+ diff-line-content-length
  diff-line-content-offset
  diff-line-new-lineno
  diff-line-num-lines
  diff-tree-to-tree
  diff-tree-to-workdir
  error
+ filemode->int ; Used by `tree-entry-attributes`.
  index-add
  index-add-bypath
  index-clear
  oid-fromstr
  oid-pathfmt
  oid-tostr
+ patch-free
+ patch-from-blob-and-buffer
+ patch-from-blobs
+ patch-from-diff
+ patch-get-delta
+ patch-get-hunk
+ patch-get-line-in-hunk
+ patch-line-stats
+ patch-num-hunks
+ patch-num-lines-in-hunk
+ patch-size
+ patch-to-str
  reference-create
  reference-delete
  reference-foreach-name
+ reference-foreach-glob
  reference-free
  reference-is-branch
  reference-is-remote
+ reference-is-tag
  reference-lookup
  reference-name
  reference-name-to-id
  refspec-force
  refspec-src
  refspec-string
+ remote-add-fetch
+ remote-add-push
+ remote-clear-refspecs
  remote-connect
  remote-connected
+ remote-create
  remote-disconnect
  remote-download
+ remote-fetch
  remote-free
  remote-get-refspec
  remote-is-valid-name
  remote-name
  remote-pushurl
  remote-rename
+ remote-refspec-count
  remote-save
  remote-set-pushurl
  remote-set-url
  tree-entry-byoid
  tree-entry-bypath
  tree-entry-dup
+ tree-entry-filemode
  tree-entry-free
  tree-entry-id
  tree-entry-name

File git-lolevel.scm

 (define-foreign-type odb              (c-pointer "git_odb"))
 (define-foreign-type odb-object       (c-pointer "git_odb_object"))
 (define-foreign-type oid-shorten      (c-pointer "git_oid_shorten"))
+(define-foreign-type patch            (c-pointer "git_patch"))
 (define-foreign-type push             (c-pointer "git_push"))
 (define-foreign-type note             (c-pointer "git_note"))
 (define-foreign-type reference        (c-pointer "git_reference"))
   (int              old_lines    diff-hunk-old-lines)
   (int              new_start    diff-hunk-new-start)
   (int              new_lines    diff-hunk-new-lines)
-  (size_t           header_len   diff-hunk-header-len)
+  (size_t           header_len   diff-hunk-header-length)
   (nonnull-c-string header       diff-hunk-header))
 
 (define-foreign-record-type (diff-line git_diff_line)
   (int              old_lineno     diff-line-old-lineno)
   (int              new_lineno     diff-line-new-lineno)
   (int              num_lines      diff-line-num-lines)
-  (size_t           content_len    diff-line-content-len)
+  (size_t           content_len    diff-line-content-length)
   (off-t            content_offset diff-line-content-offset)
   ;; XXX Not null-terminated, and doesn't outlive the call to
   ;; git_diff_line_cb that produces it -- must be copied out first.
       str            (+ n 1) id)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; patch.h
+;;;
+
+(define patch-free       (foreign-lambda void git_patch_free patch))
+(define patch-from-diff  (foreign-lambda/allocate patch git_patch_from_diff diff size_t))
+(define patch-from-blobs (foreign-lambda/allocate patch git_patch_from_blobs blob* c-string blob* c-string diff-options))
+(define patch-get-delta  (foreign-lambda diff-delta git_patch_get_delta patch))
+(define patch-get-hunk   (foreign-lambda/allocate (const diff-hunk) git_patch_get_hunk (c-pointer size_t) patch size_t))
+(define patch-num-hunks  (foreign-lambda size_t git_patch_num_hunks patch))
+(define patch-size       (foreign-lambda size_t git_patch_size patch bool bool bool))
+(define patch-to-str     (foreign-lambda/allocate c-string git_patch_to_str patch))
+
+(define patch-get-line-in-hunk  (foreign-lambda/allocate (const diff-line) git_patch_get_line_in_hunk patch size_t size_t))
+(define patch-num-lines-in-hunk (foreign-lambda int git_patch_num_lines_in_hunk patch size_t))
+
+(define patch-from-blob-and-buffer
+  (foreign-lambda/allocate patch git_patch_from_blob_and_buffer blob* c-string c-pointer size_t c-string diff-options))
+
+(define (patch-line-stats patch)
+  (let-location ((context   size_t)
+                 (additions size_t)
+                 (deletions size_t))
+    ((foreign-lambda/retval git_patch_line_stats
+      (c-pointer size_t) (c-pointer size_t)   (c-pointer size_t)   patch)
+      (location context) (location additions) (location deletions) patch)
+    (vector context additions deletions)))
+
+;(define patch-print (foreign-lambda/retval git_patch_print ...))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; reflog.h
 ;;
 ;; TODO
 (define reference-owner               (foreign-lambda repository git_reference_owner reference))
 (define reference-is-branch           (foreign-lambda bool git_reference_is_branch reference))
 (define reference-is-remote           (foreign-lambda bool git_reference_is_remote reference))
+(define reference-is-tag              (foreign-lambda bool git_reference_is_tag reference))
 
 (define-foreign-type reference-foreach-name-cb (function int ((const c-string) c-pointer)))
 (define-git-callback (reference_foreach_name_cb (c-string name) (c-pointer i))
        repository reference-foreach-name-cb            c-pointer)
        repo       (location reference_foreach_name_cb) callback))))
 
+(define (reference-foreach-glob repo glob f)
+  (let-handle ((callback f))
+    (guard-errors git_reference_foreach_name
+     ((foreign-safe-lambda int git_reference_foreach_glob
+       repository nonnull-c-string reference-foreach-name-cb            c-pointer)
+       repo       glob             (location reference_foreach_name_cb) callback))))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; refspec.h
 
 
 ;; TODO git_refspec_transform
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; repository.h
 
 (define repository-open          (foreign-lambda/allocate repository git_repository_open nonnull-c-string))
   ((struct oid) loid  remote-head-local-id)
   (c-string     name  remote-head-name))
 
+(define remote-add-fetch            (foreign-lambda/retval git_remote_add_fetch remote nonnull-c-string))
+(define remote-add-push             (foreign-lambda/retval git_remote_add_push remote nonnull-c-string))
+(define remote-fetch                (foreign-lambda/retval git_remote_fetch remote))
 (define remote-list                 (foreign-lambda/allocate strarray git_remote_list repository))
 (define remote-get-push-refspecs    (foreign-lambda/allocate strarray git_remote_get_push_refspecs remote))
 (define remote-get-fetch-refspecs   (foreign-lambda/allocate strarray git_remote_get_fetch_refspecs remote))
 (define tree-entry-name       (foreign-lambda c-string git_tree_entry_name tree-entry))
 (define tree-entry-id         (foreign-lambda oid git_tree_entry_id tree-entry))
 (define tree-entry-type       (foreign-lambda object-type git_tree_entry_type tree-entry))
+(define tree-entry-filemode   (foreign-lambda filemode git_tree_entry_filemode tree-entry))
 (define tree-entry-dup        (foreign-lambda tree-entry git_tree_entry_dup tree-entry))
 (define tree-entry-free       (foreign-lambda void git_tree_entry_free tree-entry))
 (define tree-builder-free     (foreign-lambda void git_treebuilder_free tree-builder))
           (only git-lolevel git-error)
           (only posix current-directory regular-file?)
           (only files normalize-pathname make-pathname)
-          (only lolevel record-instance-slot number-of-bytes move-memory!)
+          (only lolevel record-instance-slot record-instance?
+                        make-locative move-memory! number-of-bytes)
           (rename (only data-structures o) (o compose))
           (prefix (except git-lolevel git-error) git-)
-          (rename (only chicken make-blob)
+          (rename (only chicken make-blob blob?)
+                  (blob? chicken-blob?)
                   (make-blob make-chicken-blob)))
   (include "git-exports.scm")
 
              (free      (cddddr e))
              (slots     (cdar spec))
              (attrs     (cdr spec))
-             (make      (s+ 'make- name))
-             (%make     (s+ '%make- name))
+             (make      (s+ '%make- name))
+             (pred?     (s+ name '?))
              (->pointer (s+ name '->pointer))
              (pointer-> (s+ 'pointer-> name)))
         `(begin
-           (define-record ,name >pointer ,@slots)
-           (define ,%make ,make)
+           (define-record-type ,name
+             (,make pointer ,@slots)
+             ,pred?
+             (pointer ,->pointer)
+             ,@(map (lambda (s)
+                      `(,s ,(symbol-append name '- s)
+                           ,(symbol-append name '- s '-set!)))
+                    slots))
            (define-record-printer (,name ,name out)
              (display ,printer out))
            (define (,pointer-> ,@slots ptr)
-             (and-let* ((ptr)
-                        (obj (,%make ptr ,@slots)))
+             (and-let* ((ptr) ; TODO This check should go away.
+                        (obj (,make ptr ,@slots)))
                ,(if (null? free)
                     'obj
                     `(set-finalizer! obj
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
+;;; Types
+;;;
+
+(define-type blob*             (struct blob))
+(define-type commit            (struct commit))
+(define-type config            (struct config))
+(define-type config-entry      (struct config-entry))
+(define-type diff              (struct diff))
+(define-type diff-delta        (struct diff-delta))
+(define-type diff-file         (struct diff-file))
+(define-type diff-hunk         (struct diff-hunk))
+(define-type diff-line         (struct diff-line))
+(define-type index             (struct index))
+(define-type index-entry       (struct index-entry))
+(define-type note              (struct note))
+(define-type odb               (struct odb))
+(define-type odb-object        (struct odb-object))
+(define-type oid               (struct oid))
+(define-type patch             (struct patch))
+(define-type reference         (struct reference))
+(define-type refspec           (struct refspec))
+(define-type repository        (struct repository))
+(define-type remote            (struct remote))
+(define-type signature         (struct signature))
+(define-type tag               (struct tag))
+(define-type transfer-progress (struct transfer-progress))
+(define-type tree              (struct tree))
+(define-type tree-builder      (struct tree-builder))
+(define-type tree-entry        (struct tree-entry))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
 ;;; Generics & OIDs
 ;;;
 
+(define-type object  (or blob* commit tag tree))
+(define-type oid-ish (or oid string reference object))
+
+(: merge-base (repository oid oid -> commit))
+(: object->oid (object -> oid))
+(: object-id (object -> oid))
+(: object-sha (object -> string))
+(: object-type (object -> (or symbol false)))
+(: object=? (object object -> boolean))
+(: oid->path (oid -> string))
+(: oid->string (oid #!optional fixnum -> string))
+(: oid=? (oid oid -> boolean))
+(: string->oid (string -> oid))
+
 ;; OIDs are allocated/freed by git-lolevel.scm.
 (define-git-record-type oid
   ((oid))
 ;; Only valid for the Commit, Tree, Blob & Tag types.
 (define (object-type obj)
   (let ((type (git-object-type (object->pointer obj))))
-    (and (symbol? type) type)))
+    (and (not (memq type '(ext1 ext2))) type)))
 
 (define (object=? obj1 obj2)
   (oid=? (object-id obj1) (object-id obj2)))
 
 (define (object-sha obj #!optional (len 40))
-  (oid->string (->oid obj) len))
+  (oid->string (object->oid obj) len))
 
 (define (oid=? oid1 oid2)
   (git-oid-equal (oid->pointer oid1) (oid->pointer oid2)))
 (define string->oid (compose pointer->oid git-oid-fromstr))
 (define object-id   (compose pointer->oid git-oid-cpy git-object-id object->pointer))
 
-(define (->oid obj)
+(define (object->oid obj)
   (cond ((oid? obj) obj)
         ((string? obj) (string->oid obj))
         ((reference? obj) (reference-target obj))
-        (else (object-id obj))))
+        ((record-instance? obj) (object-id obj))
+        (else (error 'object->oid "Can't convert to OID" obj))))
 
-(define ->oid->pointer
-  (compose oid->pointer ->oid))
+(define object->oid->pointer
+  (compose oid->pointer object->oid))
 
-(define (->reference-name obj)
+(define (object->reference-name obj)
   (cond ((string? obj) obj)
         ((reference? obj) (reference-name obj))
-        (else (git-error '->reference-name "Not a valid reference" obj))))
+        (else (git-error 'object->reference-name "Not a valid reference" obj))))
 
 (define (pointer->object repo ptr)
   (case (git-object-type ptr)
 ;;; Signatures
 ;;;
 
+(: make-signature (string string #!optional fixnum fixnum -> signature))
+(: signature-email (signature -> string))
+(: signature-name (signature -> string))
+(: signature-time (signature -> fixnum))
+(: signature-time-offset (signature -> fixnum))
+(: signature? (* --> boolean : signature))
+
 (define-git-record-type signature
   ((signature) name email)
   (format "#<signature \"~A <~A>\">" (signature-name signature) (signature-email signature))
 ;;; Repositories
 ;;;
 
+(: create-repository (#!optional string * -> repository))
+(: repository-bare? (repository -> boolean))
+(: repository-empty? (repository -> boolean))
+(: repository-head (repository -> reference))
+(: repository-head-detached? (repository -> boolean))
+(: repository-head-unborn? (repository -> boolean))
+(: repository-open (#!optional string -> repository))
+(: repository-path (repository -> string))
+(: repository-ref (repository oid-ish #!optional symbol -> (or object false)))
+(: repository-working-directory (repository -> string))
+(: repository? (* --> boolean : repository))
+
 (define-git-record-type repository
   ((repository) is-empty is-bare path workdir head-unborn head-detached)
   (format "#<repository ~S>" (repository-path repository))
      repo
      (git-object-lookup
       (repository->pointer repo)
-      (->oid->pointer ref)
+      (object->oid->pointer ref)
       type))
     ((git) #f)))
 
   (pointer->repository (git-repository-init path bare)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
 ;;; Revspec
+;;;
+
+(: parse-revision-specification
+   (repository string -> (or object false) (or object false)))
 
 (define (parse-revision-specification repo str)
   (condition-case
         (values #f #f))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; References
+;;;
+;;; References
+;;;
+
+(: create-reference (repository #!rest -> reference))
+(: reference (repository string -> reference))
+(: reference-branch? (reference -> boolean))
+(: reference-delete (reference -> void))
+(: reference-name (reference -> string))
+(: reference-remote? (reference -> boolean))
+(: reference-rename (reference string #!optional * -> reference))
+(: reference-repository (reference -> repository))
+(: reference-resolve (reference -> reference))
+(: reference-tag? (reference -> boolean))
+(: reference-target (reference -> oid))
+(: reference-target-set! (reference oid-ish -> void))
+(: reference-type (reference -> symbol))
+(: reference? (* --> boolean : reference))
+(: references (repository #!optional string -> (list-of reference)))
+(: references-fold
+   (forall (a b)
+           ((reference a -> b) a repository #!optional string -> (or a b))))
 
 (define-git-record-type reference
   ((reference repository) type name delete)
 
 (define reference-branch? (compose git-reference-is-branch reference->pointer))
 (define reference-remote? (compose git-reference-is-remote reference->pointer))
+(define reference-tag?    (compose git-reference-is-tag    reference->pointer))
 
 (define (reference-target ref)
   ;; We have to dig out the intermediate reference in order to free it.
 (define (reference repo name)
   (pointer->reference repo (git-reference-lookup (repository->pointer repo) name)))
 
-(define (references-fold kons knil repo #!optional (type 'all))
+(define (references-fold kons knil repo #!optional glob)
   (begin0-let ((state knil))
-    (git-reference-foreach-name
-     (repository->pointer repo)
-     (lambda (name)
-       (set! state
-         (kons (pointer->reference
-                repo
-                (git-reference-lookup
-                 (repository->pointer repo)
-                 name))
-               state))))))
-
-(define (references repo #!optional (type 'all))
-  (references-fold cons '() repo type))
+    (let* ((*repo    (repository->pointer repo))
+           (callback (lambda (name)
+                       (set! state
+                         (kons (pointer->reference
+                                repo
+                                (git-reference-lookup *repo name))
+                               state)))))
+      (cond
+        (glob (git-reference-foreach-glob *repo glob callback))
+        (else (git-reference-foreach-name *repo callback))))))
+
+(define (references repo #!optional glob)
+  (references-fold cons '() repo glob))
 
 (define (create-reference repo #!key name target symbolic force)
   (let ((repo* (repository->pointer repo)))
      repo
      (if (not symbolic)
          ;; Direct references are created by OID.
-         (git-reference-create repo* name (->oid->pointer target) force)
+         (git-reference-create repo* name (object->oid->pointer target) force)
          ;; Symbolic references require the target to be given by a string.
-         (git-reference-symbolic-create repo* name (->reference-name target) force)))))
+         (git-reference-symbolic-create repo* name (object->reference-name target) force)))))
 
 (define (reference-target-set! ref target)
   (git-reference-set-target
    (reference->pointer ref)
-   (->oid->pointer target)))
+   (object->oid->pointer target)))
 
 (define (reference-rename ref name #!optional force)
   (pointer->reference
    (git-reference-rename (reference->pointer ref) name force)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Trees
+;;;
+;;; Trees
+;;;
+
+(: create-tree (repository #!optional index -> tree))
+(: tree (repository oid-ish -> tree))
+(: tree-entries (tree -> (list-of (pair string tree-entry))))
+(: tree-entrycount (tree -> fixnum))
+(: tree-fold (forall (a b) ((string tree-entry a -> b) a tree #!optional symbol -> (or a b))))
+(: tree-id (tree -> oid))
+(: tree-ref (tree (or fixnum oid string) -> (or tree-entry false)))
+(: tree-repository (tree -> repository))
+(: tree? (* --> boolean : tree))
 
 (define-git-record-type tree
   ((tree repository) id entrycount)
   (format "#<tree ~S>" (oid->string (tree-id tree) 7))
   (git-tree-free))
 
+(: tree-entry->object ((or repository tree-entry) #!optional tree-entry -> object))
+(: tree-entry-attributes (tree-entry -> fixnum))
+(: tree-entry-id (tree-entry -> oid))
+(: tree-entry-name (tree-entry -> string))
+(: tree-entry-owner (tree-entry -> (or tree tree-builder)))
+(: tree-entry-type (tree-entry -> symbol))
+(: tree-entry? (* --> boolean : tree-entry))
+
 (define-git-record-type tree-entry
-  ((tree-entry owner) id name type)
+  ((tree-entry owner) id name type filemode)
   (format "#<tree-entry ~S>" (tree-entry-name tree-entry))
   (git-tree-entry-free))
 
+(define (tree-entry-attributes e)
+  (git-filemode->int (tree-entry-filemode e)))
+
 (define (tree repo ref)
   (pointer->tree
    repo
    (git-tree-lookup
     (repository->pointer repo)
-    (->oid->pointer ref))))
+    (object->oid->pointer ref))))
 
 (define (tree-ref tree key)
   (pointer->tree-entry
   (tree-fold (lambda (path entry acc) (cons (cons path entry) acc)) '() tree))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tree Builders
+;;;
+;;; Tree Builders
+;;;
+
+(: make-tree-builder (#!optional tree -> tree-builder))
+(: tree-builder-clear (tree-builder -> void))
+(: tree-builder-insert (tree-builder oid-ish string fixnum -> tree-entry))
+(: tree-builder-ref (tree-builder string -> (or tree-entry false)))
+(: tree-builder-remove (tree-builder string -> void))
+(: tree-builder-write (repository tree-builder -> tree))
+(: tree-builder? (* --> boolean : tree-builder))
 
 (define-git-record-type tree-builder
   ((tree-builder) clear)
     (git-tree-builder-insert
      (tree-builder->pointer tb)
      path
-     (->oid->pointer obj)
+     (object->oid->pointer obj)
      attributes))))
 
 (define (tree-builder-write repo tb)
   (git-tree-builder-remove (tree-builder->pointer tb) path))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Branches
+;;;
+;;; Branches
+;;;
+
+(define-type branch reference)
+
+(: branch (repository string #!optional symbol -> branch))
+(: branch-delete (branch -> void))
+(: branch-head? (branch -> boolean))
+(: branch-name (branch -> string))
+(: branch-rename (branch string #!optional boolean -> branch))
+(: branches (repository #!optional symbol -> (list-of branch)))
+(: branches-fold (forall (a b) ((branch a -> b) a repository #!optional symbol -> (or a b))))
+(: create-branch (repository #!rest -> branch))
 
 (define (branch repo name #!optional (type 'all))
   (pointer->reference repo (git-branch-lookup (repository->pointer repo) name type)))
   (branches-fold cons '() repo type))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Checkout
+;;;
+;;; Checkout
+;;;
+
+(: checkout (repository #!optional (or index object) -> void))
 
 (define (checkout repo #!optional object)
   (cond
     ((memq (object-type object) '(commit tag tree))
      (git-checkout-tree (repository->pointer repo) (object->pointer object) git-checkout-options-default))
     (else
-     (git-checkout-tree (repository->pointer repo) (->oid->pointer object) git-checkout-options-default))))
+     (git-checkout-tree (repository->pointer repo) (object->oid->pointer object) git-checkout-options-default))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Commits
+;;;
+;;; Commits
+;;;
+
+(: commit (repository oid-ish -> commit))
+(: commit-ancestor (commit #!optional fixnum -> (or commit false)))
+(: commit-author (commit -> signature))
+(: commit-committer (commit -> signature))
+(: commit-header (commit -> string))
+(: commit-id (commit -> oid))
+(: commit-message (commit -> string))
+(: commit-message-encoding (commit -> string))
+(: commit-message-raw (commit -> string))
+(: commit-parent (commit #!optional fixnum -> (or commit false)))
+(: commit-parent-id (commit -> oid))
+(: commit-parentcount (commit -> fixnum))
+(: commit-parents (commit -> (list-of commit)))
+(: commit-repository (commit -> repository))
+(: commit-time (commit -> fixnum))
+(: commit-time-offset (commit -> fixnum))
+(: commit-tree (commit -> tree))
+(: commit-tree-id (commit -> oid))
+(: commit? (* --> boolean : commit))
+(: commits (repository #!rest -> (list-of commit)))
+(: commits-fold (forall (a b) ((commit a -> b) a repository #!rest -> (or a b))))
+(: create-commit (repository #!rest -> commit))
 
 (define-git-record-type commit
   ((commit repository) id parentcount message message-raw message-encoding time time-offset raw-header)
    repo
    (git-commit-lookup
     (repository->pointer repo)
-    (->oid->pointer ref))))
+    (object->oid->pointer ref))))
 
 (define (commits-fold kons knil repo #!key initial (hide '()) (sort 'none))
   (let ((repo* (repository->pointer repo)))
         '() ; No HEAD means no commits.
         (let ((iter (set-finalizer! (git-revwalk-new repo*) git-revwalk-free)))
           (if initial ; Set the initial revision.
-              (git-revwalk-push iter (->oid->pointer initial))
+              (git-revwalk-push iter (object->oid->pointer initial))
               (git-revwalk-push-head iter))
           ;; Set sort mode, from '(none topo time rev).
           (git-revwalk-sorting iter sort)
           ;; Set hidden commits.
-          (for-each (lambda (h) (git-revwalk-hide iter (->oid->pointer h))) hide)
+          (for-each (lambda (h) (git-revwalk-hide iter (object->oid->pointer h))) hide)
           ;; Iterate commits.
           (let loop ((state knil))
             (let ((c (condition-case
     (repository->pointer repo)
     (git-commit-create
      (repository->pointer repo)
-     (and reference (->reference-name reference))
+     (and reference (object->reference-name reference))
      (signature->pointer author)
      (signature->pointer committer)
      message
      (map commit->pointer parents)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Blobs
+;;;
+;;; Blobs
+;;;
+
+(: blob (repository oid-ish -> blob*))
+(: blob-binary? (blob* -> boolean))
+(: blob-content (blob* -> blob))
+(: blob-id (blob* -> oid))
+(: blob-length (blob* -> fixnum))
+(: blob-repository (blob* -> repository))
+(: blob? (* --> boolean : blob*))
+(: create-blob (repository (or blob string) -> blob*))
 
 (define-git-record-type blob
   ((blob repository) id rawsize rawcontent is-binary)
    repo
    (git-blob-lookup
     (repository->pointer repo)
-    (->oid->pointer ref))))
+    (object->oid->pointer ref))))
 
 (define (create-blob repo source)
   (let ((repo* (repository->pointer repo)))
      repo
      (git-blob-lookup
       repo*
-      (cond ((blob? source)
+      (cond ((chicken-blob? source)
              (git-blob-create-frombuffer repo* source))
             ((string? source)
              (if (regular-file? source)
 (define create-blob* create-blob)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Index
+;;;
+;;; Index
+;;;
+
+(: index-add (index (or index-entry string) -> void))
+(: index-clear (index -> void))
+(: index-entrycount (index -> fixnum))
+(: index-find (index string -> (or fixnum false)))
+(: index-open ((or string repository) -> index))
+(: index-read (index #!optional boolean -> void))
+(: index-ref (index (or fixnum string) -> (or index-entry false)))
+(: index-remove (index fixnum -> void))
+(: index-write (index -> fixnum))
+(: index? (* --> boolean : index))
 
 (define-git-record-type index
   ((index) entrycount write clear)
   "#<index>"
   (git-index-free))
 
+(: index-entry-ctime (index-entry -> fixnum))
+(: index-entry-dev (index-entry -> fixnum))
+(: index-entry-extended (index-entry -> fixnum))
+(: index-entry-flags (index-entry -> fixnum))
+(: index-entry-gid (index-entry -> fixnum))
+(: index-entry-id (index-entry -> oid))
+(: index-entry-ino (index-entry -> fixnum))
+(: index-entry-mode (index-entry -> fixnum))
+(: index-entry-mtime (index-entry -> fixnum))
+(: index-entry-owner (index-entry -> index))
+(: index-entry-path (index-entry -> string))
+(: index-entry-size (index-entry -> fixnum))
+(: index-entry-stage (index-entry -> fixnum))
+(: index-entry-uid (index-entry -> fixnum))
+(: index-entry? (* --> boolean : index-entry))
+
 (define-git-record-type index-entry
   ((index-entry owner) dev oid ino mode uid gid size stage flags extended path)
   (format "#<index-entry ~S>" (index-entry-path index-entry)))
             (git-error 'index-ref "Invalid key" key))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Status
+;;;
+;;; File status
+;;;
+
+(define-type status (or symbol (list-of symbol)))
+
+(: file-ignored? (repository string -> boolean))
+(: file-status (repository string -> status))
+(: file-statuses (repository -> (list-of (pair string status))))
+(: file-statuses-fold
+   (forall (a b) ((string status a -> b) a repository -> (or a b))))
 
 (define (file-status repo path)
   (git-status-file (repository->pointer repo) path))
    repo))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Notes
+;;;
+;;; Notes
+;;;
+
+(: create-note (repository #!rest -> note))
+(: delete-note (repository #!rest -> void))
+(: note (repository oid-ish #!optional string -> note))
+(: note-id (note -> oid))
+(: note-message (note -> string))
+(: note-repository (note -> repository))
+(: note? (* --> boolean : note))
+(: notes (repository #!optional reference -> (list-of note)))
+(: notes-fold
+   (forall (a b) ((note a -> b) a repository #!optional reference -> (or a b))))
 
 (define-git-record-type note
   ((note repository) message oid)
    (git-note-read
     (repository->pointer repo)
     reference
-    (->oid->pointer object))))
+    (object->oid->pointer object))))
 
 (define (delete-note repo #!key target reference author (committer author))
   (git-note-remove
    reference
    (signature->pointer author)
    (signature->pointer committer)
-   (->oid->pointer target)))
+   (object->oid->pointer target)))
 
 (define (create-note repo #!key message target reference author (committer author) force)
   (let ((repo* (repository->pointer repo))
-        (oid*  (->oid->pointer target)))
+        (oid*  (object->oid->pointer target)))
     ;; git-note-create returns the new note's OID, but AFAIK there's no
     ;; way to look up a note by OID. So, to return the new note, we
     ;; first create it...
   (notes-fold cons '() repo reference))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; ODB
+;;;
+;;; ODB
+;;;
+
+(: odb-has-object? (odb oid-ish -> boolean))
+(: odb-hash (* #!optional symbol -> oid))
+(: odb-object-data (odb-object -> blob))
+(: odb-object-id (odb-object -> oid))
+(: odb-object-owner (odb-object -> odb))
+(: odb-object-size (odb-object -> fixnum))
+(: odb-object-type (odb-object -> symbol))
+(: odb-object? (* --> boolean : odb-object))
+(: odb-open ((or string repository) -> odb))
+(: odb-read (odb oid-ish -> odb-object))
+(: odb-write (odb * #!optional symbol -> oid))
+(: odb? (* --> boolean : odb))
 
 (define-git-record-type odb
   ((odb))
 (define (odb-has-object? odb obj)
   (git-odb-exists
    (odb->pointer odb)
-   (->oid->pointer obj)))
+   (object->oid->pointer obj)))
 
 (define (odb-open loc)
   (pointer->odb
    odb
    (git-odb-read
     (odb->pointer odb)
-    (->oid->pointer obj))))
+    (object->oid->pointer obj))))
 
 (define (odb-write odb data #!optional (type 'blob))
   (pointer->oid (git-odb-write (odb->pointer odb) data (number-of-bytes data) type)))
     dest))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Tags
+;;;
+;;; Tags
+;;;
+
+(: create-tag (repository #!rest -> tag))
+(: tag (repository oid-ish -> tag))
+(: tag-delete (tag -> void))
+(: tag-id (tag -> oid))
+(: tag-message (tag -> string))
+(: tag-name (tag -> string))
+(: tag-peel (tag -> object))
+(: tag-repository (tag -> repository))
+(: tag-tagger (tag -> signature))
+(: tag-target (tag -> object))
+(: tag? (* --> boolean : tag))
+(: tags (repository -> (list-of tag)))
+(: tags-fold (forall (a b) ((tag a -> b) a repository -> (or a b))))
 
 (define-git-record-type tag
   ((tag repository) id name message)
    repo
    (git-tag-lookup
     (repository->pointer repo)
-    (->oid->pointer ref))))
+    (object->oid->pointer ref))))
 
 (define (tags-fold kons knil repo)
   (begin0-let ((state knil)
      force))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Diffs
+;;;
+;;; Diffs
+;;;
+
+(: diff (repository #!optional (or index tree) (or index tree) -> diff))
+(: diff->string (diff -> string))
+(: diff-delta-hunks (diff-delta -> (list-of diff-hunk)))
+(: diff-delta-new-file (diff-delta -> (or diff-file false)))
+(: diff-delta-old-file (diff-delta -> (or diff-file false)))
+(: diff-delta-path (diff-delta -> string))
+(: diff-delta-status (diff-delta -> symbol))
+(: diff-delta? (* --> boolean : diff-delta))
+(: diff-deltas (diff -> (list-of diff-delta)))
+(: diff-file-flags (diff-file -> fixnum))
+(: diff-file-id (diff-file -> oid))
+(: diff-file-mode (diff-file -> fixnum))
+(: diff-file-path (diff-file -> string))
+(: diff-file-size (diff-file -> fixnum))
+(: diff-file? (* --> boolean : diff-file))
+(: diff-fold (forall (a b) ((diff-delta a -> b) a diff -> (or a b))))
+(: diff-hunk-header (diff-hunk -> string))
+(: diff-hunk-header-len (deprecated diff-hunk-header-length))
+(: diff-hunk-header-length (diff-hunk -> fixnum))
+(: diff-hunk-lines (diff-hunk -> (list-of diff-line)))
+(: diff-hunk-new-lines (diff-hunk -> fixnum))
+(: diff-hunk-new-start (diff-hunk -> fixnum))
+(: diff-hunk-old-lines (diff-hunk -> fixnum))
+(: diff-hunk-old-start (diff-hunk -> fixnum))
+(: diff-hunk? (* --> boolean : diff-hunk))
+(: diff-line-content (diff-line -> string))
+(: diff-line-content-len (deprecated diff-line-content-length))
+(: diff-line-content-length (diff-line -> fixnum))
+(: diff-line-content-offset (diff-line -> fixnum))
+(: diff-line-new-lineno (diff-line -> fixnum))
+(: diff-line-num-lines (diff-line -> fixnum))
+(: diff-line-old-lineno (diff-line -> fixnum))
+(: diff-line-origin (diff-line -> char))
+(: diff-line? (* --> boolean : diff-line))
+(: diff-num-deltas (diff -> fixnum))
+(: diff-patch (diff fixnum -> patch))
+(: diff-patches (diff -> (list-of patch)))
+(: diff-repository (diff -> repository))
+(: diff? (* --> boolean : diff))
+(: diffs (deprecated diff-deltas))
 
 (define-git-record-type diff
   ((diff repository) num-deltas)
   (format "#<diff-delta (~a) ~s>" (diff-delta-status diff-delta) (diff-delta-path diff-delta)))
 
 (define-git-record-type diff-hunk
-  ((diff-hunk diff lines) old-start old-lines new-start new-lines header-len header)
+  ((diff-hunk diff lines) old-start old-lines new-start new-lines header-length header)
   (format "#<diff-hunk ~s>" (diff-hunk-header diff-hunk)))
 
 (define-git-record-type diff-line
-  ((diff-line diff content) origin old-lineno new-lineno num-lines content-len content-offset)
+  ((diff-line diff content) origin old-lineno new-lineno num-lines content-length content-offset)
   (format "#<diff-line (~a)>" (diff-line-origin diff-line)))
 
 (define diff-delta-old-file (preserve-owner pointer->diff-file diff-delta-old-file))
 (define diff-delta-new-file (preserve-owner pointer->diff-file diff-delta-new-file))
 
+(define diff-hunk-header-len diff-hunk-header-length)
+(define diff-line-content-len diff-line-content-length)
+
 (define (diff-delta-path delta)
   (diff-file-path
    (or (diff-delta-new-file delta)
          (set! hunk (pointer->diff-hunk delta '() *hunk))
          (diff-delta-hunks-set! delta (cons hunk (diff-delta-hunks delta))))
        (lambda (*delta *hunk *line)
-         (let* ((len  (git-diff-line-content-len *line))
+         (let* ((len  (git-diff-line-content-length *line))
                 (str  (make-string len))
                 (line (pointer->diff-line hunk str *line)))
            (move-memory! (git-diff-line-content *line) str len)
               (else
                (git-error 'diff "Undiffable object" object))))))))
 
-(define (diffs . args)
-  (reverse (diff-fold cons '() (apply diff args))))
+(define diff-deltas
+  (case-lambda
+    ((diff)
+     (diff-fold cons '() diff))
+    (args ; Deprecated.
+     (diff-fold cons '() (apply diff args)))))
+
+;; Deprecated (above).
+(define diffs diff-deltas)
+
+(define (diff-patch diff i)
+  (pointer->patch
+   (diff-repository diff)
+   (git-patch-from-diff (diff->pointer diff) i)))
+
+(define (diff-patches diff)
+  (do ((n (fx- (diff-num-deltas diff) 1)
+          (fx- n 1))
+       (a (list)
+          (cons (diff-patch diff n) a)))
+      ((fx< n 0) a)))
+
+(define (diff->string diff)
+  (do ((n (fx- (diff-num-deltas diff) 1)
+          (fx- n 1))
+       (s (string)
+          (string-append (patch->string (diff-patch diff n)) s)))
+      ((fx= n 0) s)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; Patch
+;;;
+
+(: patch (blob* (or string blob) -> patch))
+(: patch->string (patch -> string))
+(: patch-size (patch -> fixnum))
+(: patch-stats (patch -> (vector fixnum fixnum fixnum)))
+(: patch? (* --> boolean : patch))
+
+(define-git-record-type patch
+  ((patch repository) num-hunks line-stats)
+  (format "#<patch (~a)>" (patch-num-hunks patch))
+  (git-patch-free))
+
+(define patch-stats patch-line-stats)
+
+(define patch->string
+  (compose git-patch-to-str patch->pointer))
+
+(define (patch-size patch)
+  (git-patch-size
+   (patch->pointer patch)
+   #t   ; Context.
+   #t   ; Hunk headers.
+   #t)) ; Line headers.
+
+;; TODO How best to pass the path arguments?
+(define (patch blob1 object2)
+  (pointer->patch
+   (blob-repository blob1)
+   (cond
+     ((blob? object2)
+      (git-patch-from-blobs
+       (blob->pointer blob1)
+       #f      ; blob1 pathname.
+       (blob->pointer blob1)
+       #f      ; blob2 pathname.
+       #f))    ; diff options.
+     ((or (string? object2)
+          (chicken-blob? object2))
+      (git-patch-from-blob-and-buffer
+       (blob->pointer blob1)
+       #f      ; blob1 pathname.
+       (make-locative object2)
+       (number-of-bytes object2)
+       #f      ; object2 pathname.
+       #f))))) ; diff options.
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 ;;;
 
 (define-git-record-type remote
-  ((remote repository) name url pushurl connect connected disconnect update-tips update-fetchhead stop save)
+  ((remote repository) clear-refspecs connected disconnect fetch name pushurl refspec-count stop save update-fetchhead update-tips url)
   (format "#<remote ~S (~a)>"
           (remote-name remote)
           (if (remote-connected? remote) "connected" "disconnected"))
 
 (define-git-record-type refspec
   ((refspec remote) src dst string direction force)
-  (format "#<refspec ~S ~S>" (refspec-src refspec) (refspec-dst refspec)))
+  (format "#<refspec ~S ~S>" (refspec-source refspec) (refspec-destination refspec)))
 
 (define-git-record-type transfer-progress
   ((transfer-progress remote) total-objects indexed-objects received-objects received-bytes)
 
 (define refspec-source refspec-src)
 (define refspec-destination refspec-dst)
+(define refspec-force? refspec-force)
 
+(define remote-clear-refspecs! remote-clear-refspecs)
 (define remote-connected? remote-connected)
 (define remote-url-valid? git-remote-valid-url)
 (define remote-url-supported? git-remote-supported-url)
 (define remote-name-valid? git-remote-is-valid-name)
+(define remote-disconnect! remote-disconnect)
+(define remote-fetch! remote-fetch)
+(define remote-update-tips! remote-update-tips)
+(define remote-update-fetchhead? remote-update-fetchhead)
 
 (define remote-url-set!              (git-record-attribute-setter git-remote-set-url))
 (define remote-pushurl-set!          (git-record-attribute-setter git-remote-set-pushurl))
 (define remote-update-fetchhead-set! (git-record-attribute-setter git-remote-update-fetchhead))
 
+(define (remote-refspec-add! rem spec #!optional (direction 'fetch))
+  (case direction
+    ((fetch) (git-remote-add-fetch (remote->pointer rem) spec))
+    ((push)  (git-remote-add-push  (remote->pointer rem) spec))
+    (else    (git-error 'remote-refspec-add! "Invalid refspec direction" direction))))
+
 (define (remote-refspecs rem)
   (let lp ((i 0)
            (a '()))
   (map (lambda (name) (remote repo name))
        (git-remote-list (repository->pointer repo))))
 
-(define (remote-connect rem #!optional (direction 'fetch))
+(define (remote-connect! rem #!optional (direction 'fetch))
   (git-remote-connect (remote->pointer rem) direction))
 
 (define (remote-rename rem name #!optional callback)
   (git-remote-rename (remote->pointer rem) name callback))
 
 (define (remote-stats rem)
-  (pointer->transfer-progress
-   rem
-   (git-remote-stats (remote->pointer rem))))
-
-;(define remote-download
-;  (let ((make-remote-download-callback
-;         (lambda (rem fn)
-;           (and fn (lambda (tp*)
-;                     (fn (pointer->transfer-progress rem tp*)))))))
-;    (lambda (rem #!optional fn)
-;      (if (remote-connected? rem)
-;          (git-remote-download
-;           (remote->pointer rem)
-;           (make-remote-download-callback rem fn))
-;          (dynamic-wind
-;           (lambda ()
-;             (remote-connect rem))
-;           (lambda ()
-;             (remote-download rem (make-remote-download-callback rem fn))
-;             (remote-update-tips rem)
-;             (remote-stats rem))
-;           (lambda ()
-;             (remote-disconnect rem)))))))
+  (pointer->transfer-progress rem (git-remote-stats (remote->pointer rem))))
+
+(define (remote-download! rem)
+  (cond ((remote-connected? rem)
+         (git-remote-download (remote->pointer rem))
+         (remote-stats rem))
+        (else
+         (remote-connect! rem)
+         (remote-download! rem)
+         (remote-disconnect! rem)
+         (remote-stats rem))))
+
+(define (create-remote repo name url)
+  (pointer->remote repo (git-remote-create (repository->pointer repo) name url)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 ;;; Cloning
 ;;;
 
+(: clone (string string -> repository))
+
 (define (clone url path) (pointer->repository (git-clone url path #f)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Configs
+;;;
+;;; Configs
+;;;
+
+(define-type config-value (or string boolean number))
+
+(: config-get (config string #!optional symbol -> config-value))
+(: config-open (#!optional (or string symbol repository) -> config))
+(: config-path (#!optional symbol -> string))
+(: config-set! (config string config-value -> void))
+(: config-unset! (config string -> void))
+(: config? (* --> boolean : config))
 
 (define-git-record-type config
   ((config))
    name
    value))
 
-(define (config-unset! cfg name)
-  (git-config-delete-entry (config->pointer cfg) name)))
+(define (config-unset! config name)
+  (git-config-delete-entry (config->pointer config) name)))
 
 ;; Static libraries.
 (compile -unit git-lolevel -cJ -O3 -d0 -specialize git-lolevel.scm -o git-lolevel.o)
-(compile -unit git -uses git-lolevel -cJ -O3 -d0 -specialize git.scm -o git.o)
+(compile -unit git -uses git-lolevel -ot git.types -cJ -O3 -d0 -specialize git.scm -o git.o)
 (run (ar -rc git.a git.o git-lolevel.o))
 
 ;; Import libraries.
 
 (install-extension 'git
  '("git-lolevel.so" "git-lolevel.import.so"
-   "git.so" "git.import.so"
+   "git.so" "git.import.so" "git.types"
    "git.a" "git.o" "git-lolevel.o")
  '((version "0.0.25")))
 either the topmost directory in the project (if the repository is bare)
 or the ".git" subdirectory.
 
-<procedure>(repository-working-directory repository) => string</procedure>
+<procedure>(repository-working-directory repository) => (or string false)</procedure>
 
 Returns the absolute path to the given {{repository}}'s working directory,
 or {{#f}} if the repository is bare.
 
-<procedure>(repository-ref repository ref) => object</procedure>
+<procedure>(repository-ref repository ref) => (or object false)</procedure>
 
 Looks up a Git object in the given {{repository}}. {{ref}} may be a SHA1 string,
 {{oid}}, {{reference}}, {{blob}}, {{commit}}, {{tag}} or {{tree}}. The returned
 
 <procedure>(reference-remote? reference) => boolean</procedure>
 <procedure>(reference-branch? reference) => boolean</procedure>
+<procedure>(reference-tag? reference) => boolean</procedure>
 
 These procedures return boolean values indicating whether the given
-reference is a remote- or branch-type reference, respectively.
+reference is a {{remote}}, {{branch}}, or {{tag}}-type reference,
+respectively.
 
-<procedure>(references-fold kons knil repository [type]) => object</procedure>
+<procedure>(references-fold kons knil repository [glob]) => object</procedure>
 
 Folds over the given {{repository}}'s references in unspecified order.
 
-An optional {{type}} symbol may be provided to limit the types of
-references returnd, and may be one of the symbols {{oid}},
-{{symbolic}}, or {{all}}. If no {{type}} is given, {{all}} is used.
+An optional {{glob}} pattern string may be used to filter the references
+returned. If given, it will be matched in "glob" style where a {{#\?}}
+matches any letter, a {{#\*}} matches any sequence of letters, and
+square brackets match a range of characters (such as "[0-9]" for
+digits). If no {{glob}} is given, all references will be provided to
+{{kons}}.
 
-<procedure>(references repository [type]) => list</procedure>
+<procedure>(references repository [type]) => (list-of reference)</procedure>
 
 Returns a list of all references in the given {{repository}}. A type
 symbol may be given to filter the references, as in {{references-fold}}.
+The order of the resulting list is unspecified.
 
-<procedure>(repository-head repository) => reference</procedure>
+<procedure>(repository-head repository) => (or reference false)</procedure>
 
 Returns a reference to this repository's HEAD (resolved to an OID), of
 {{#f}} if it doesn't exist.
 
 <procedure>(branches-fold kons knil repository [type]) => object</procedure>
 
-Folds over the given {{repository}}'s branches in unspecified order.
+Folds over the given {{repository}}'s branches.
 
 A {{type}} symbol may be given, either {{local}}, {{remote}}, or
 {{all}}, to specify what type of branches should be listed; by default,
 {{all}} is used.
 
-<procedure>(branches repository [type]) => list</procedure>
+<procedure>(branches repository [type]) => (list-of branch)</procedure>
 
 Returns a list {{reference}} objects for each branch in the given
 {{repository}}. A type symbol may be given to filter the branches, as in
-{{branches-fold}}.
+{{branches-fold}}. The order of the resulting list is unspecified.
 
 <procedure>(branch-name reference) => string</procedure>
 
 Returns the SHA1 identifier corresponding to the given object, which may be a
 {{commit}}, {{tree}}, {{tag}} {{blob}}, {{reference}}, {{oid}} or {{string}}.
 
-<procedure>(object-type object) => symbol</procedure>
+<procedure>(object-type object) => (or symbol false)</procedure>
 
 Returns a symbol specifying the type of the given object, which must be
 a {{commit}}, {{tree}}, {{tag}} or {{blob}}. {{#f}} is returned if the
 
 <procedure>(commits-fold kons knil repository #!key [initial] [hide] [sort]) => object</procedure>
 
-Folds over the given {{repository}}'s commits. If an {{initial}} {{commit}} or
-SHA1 is given, only commits that are ancestors of that revision will be
-returned. If a list of revisions to {{hide}} is given, they and their ancestors
-will be ignored. If a {{sort}} is specified, commits will be collected in the
-given order; this sort order must be one of the symbols {{none}}, {{topo}},
-{{time}} or {{reverse}}.
+Folds over the given {{repository}}'s commits. If an {{initial}}
+{{commit}} or SHA1 is given, only commits that are ancestors of that
+revision will be returned. If a list of revisions to {{hide}} is given,
+they and their ancestors will be ignored. If a {{sort}} order is
+specified, commits will be collected in the given order; this sort order
+must be one of the symbols {{none}}, {{topo}}, {{time}} or {{reverse}},
+or a list of such symbols.
 
-<procedure>(commits repository #!rest args) => list</procedure>
+<procedure>(commits repository #!rest args) => (list-of commit)</procedure>
 
-Returns a list of the {{commit}}s in the given {{repository}}. {{args}} follows
-the {{#!key}} arguments specification for {{commits-fold}}.
+Returns a list of the {{commit}}s in the given {{repository}}. {{args}}
+follows the {{#!key}} arguments specification for {{commits-fold}}. The
+order of the resulting list is opposite that of a call to
+{{commits-fold}} with the same arguments.
 
 <procedure>(commit-id commit) => oid</procedure>
 
 respective {{signature}}s.
 
 <procedure>(commit-parentcount commit) => int</procedure>
-<procedure>(commit-parent commit [n]) => commit</procedure>
-<procedure>(commit-parents commit) => list</procedure>
-<procedure>(commit-ancestor commit [n]) => commit</procedure>
+<procedure>(commit-parent commit [n]) => (or commit false)</procedure>
+<procedure>(commit-parents commit) => (list-of commit)</procedure>
+<procedure>(commit-ancestor commit [n]) => (or commit false)</procedure>
 
 {{commit-parentcount}} returns the number of parents of the given
 {{commit}}.
 
 Folds over the given repository's {{tag}}s in unspecified order.
 
-<procedure>(tags repository) => list</procedure>
+<procedure>(tags repository) => (list-of tag)</procedure>
 
-Returns a list of all tags in the given {{repository}}.
+Returns a list of all tags in the given {{repository}}. The order of
+the resulting list is unspecified.
 
 <procedure>(tag-id tag) => oid</procedure>
 
 Returns the number of entries in the given {{tree}}. This count does not
 include entries of contained directories.
 
-<procedure>(tree-ref tree key) => tree-entry</procedure>
+<procedure>(tree-ref tree key) => (or tree-entry false)</procedure>
 
 Returns a {{tree-entry}} object for the given {{key}} from the tree, or
 {{#f}} if no such tree entry is found. {{key}} may be a zero-based
 
 <procedure>(tree-fold kons knil tree) => object</procedure>
 
-Folds over each path and {{tree-entry}} objects in the given {{tree}}.
+Folds over each path and accompanying {{tree-entry}} in the given
+{{tree}} in unspecified order.
 
 Note that {{kons}} should be a function of three arguments; the path to
 the current {{tree-entry}} (a string, relative to the repository's working
 directory), the current {{tree-entry}}, and the current state of the
 fold.
 
-<procedure>(tree-entries tree) => list</procedure>
+<procedure>(tree-entries tree) => (list-of (pair string tree-entry))</procedure>
 
 Returns an alist of all {{tree-entry}} objects in the given {{tree}},
-whose keys are the pathname strings indicating the {{tree-entry}}'s
-location in the repository and whose keys are the {{tree-entry}} objects
-themselves.
+whose keys are pathname strings indicating each {{tree-entry}}'s
+location in the tree and whose values are the {{tree-entry}} objects
+themselves. The order of the resulting list is unspecified.
 
 <procedure>(create-tree repository [index]) => tree</procedure>
 
 {{name}}. The inserted object must be a {{tree}} or {{blob}}, and will have
 the given {{attributes}} (an integer file mode).
 
-<procedure>(tree-builder-ref tree-builder path) => tree-entry</procedure>
+<procedure>(tree-builder-ref tree-builder path) => (or tree-entry false)</procedure>
 
 Returns the {{tree-entry}} from the given {{tree-builder}} at {{path}}, which
 should be a filename string. If the requested file doesn't exist, {{#f}} is
 
 <procedure>(diff-fold kons knil diff) => object</procedure>
 
-Folds over the given {{diff}}'s {{diff-delta}}s in unspecified (but
-consistent) order.
+Folds over the given {{diff}}'s {{diff-delta}}s in unspecified order.
 
-<procedure>(diffs repository [object1 [object2]]) => (list-of diff-delta)</procedure>
+<procedure>(diff-deltas {{diff}}) => (list-of diff-delta)</procedure>
 
-Returns a list of {{diff-deltas}} for the {{diff}} between two objects
-in the given {{repository}}. This is equivalent to calling {{(diff
-repository object1 object2)}}, then immediately building a list from the
-resulting {{diff}} record with {{diff-fold}}.
+Returns the list of {{diff-delta}}s for all changed files in the given
+{{diff}}.
 
 <record>diff-delta</record>
 <procedure>(diff-delta-path diff) => string</procedure>
     ignored
     untracked
 
-<procedure>(diff-old-file diff) => diff-file</procedure>
-<procedure>(diff-new-file diff) => diff-file</procedure>
+<procedure>(diff-patch diff n) => patch</procedure>
+
+Returns a {{patch}} for the {{n}}th {{diff-delta}} in the given
+{{diff}}.
+
+<procedure>(diff-patches diff) => patch</procedure>
+
+Returns a list of {{patch}}es for all {{diff-delta}}s in the given
+{{diff}}.
+
+<procedure>(diff->string diff) => string</procedure>
+
+Returns a string representation of the given {{diff}} as a patch.
+
+This is equivalent to concatenating the string representations of each
+{{patch}} given by {{diff-patches}}.
+
+<procedure>(diff-old-file diff) => (or diff-file false)</procedure>
+<procedure>(diff-new-file diff) => (or diff-file false)</procedure>
 
 Returns the {{diff-file}} object for the old and new file of the given
 {{diff}}, respectively. If the file is added or deleted, {{#f}} will be
 these a given {{diff-line}} corresponds to is indicated by its
 {{diff-line-origin}}, one of {{#\+}}, {{#\-}} or {{#\space}}.
 
+==== Patch
+
+<record>patch</record>
+<procedure>(patch? obj) => boolean</procedure>
+
+A {{patch}} is a textual representation of a {{diff-delta}}.
+
+<procedure>(patch->string patch) => string</procedure>
+
+Returns the string representation of the given {{patch}}.
+
+<procedure>(patch-size patch) => integer</procedure>
+
+Returns the total number of bytes in the patch, including hunk and line
+headers and context lines.
+
+<procedure>(patch-stats patch) => (vector-of integer integer integer)</procedure>
+
+Returns a three-element vector containing the number of lines of
+context, number of lines added, and number of lines deleted in the
+patch.
+
 ==== Status
 
 <procedure>(file-status repository path) => symbol</procedure>
 
 <procedure>(file-statuses-fold kons knil repository) => object</procedure>
 
-Folds over each path and corresponding file status in the given
-{{repository}}'s working directory.
+Folds over each path and the corresponding file's status in the given
+{{repository}}'s working directory in unspecified order.
 
 Note that {{kons}} should be a function of three arguments; the pathname
 of the current file (a string, relative to the repository's working
 directory), the current status symbol, and the current state of the
 fold.
 
-<procedure>(file-statuses repository) => list</procedure>
+<procedure>(file-statuses repository) => (list-of (pair string symbol))</procedure>
 
-Returns an alist all file statuses in the given {{repository}}, whose
-keys are the pathnames of the file  in the repository and whose keys are
-the status symbols of the files.
+Returns an alist of all file statuses in the given {{repository}}, whose
+keys are pathnames to each file and whose values are the status symbols
+of those files. The order of the resulting list is unspecified.
 
 <procedure>(file-ignored? repository path) => boolean</procedure>
 
 {{reference}} may specify an alternative notes reference namespace,
 which defaults to {{"refs/notes/commits"}}.
 
-<procedure>(notes repository [reference]) => list</procedure>
+<procedure>(notes repository [reference]) => (list-of note)</procedure>
 
-Returns a list of all notes in the given {{repository}}.
+Returns a list of all notes in the given {{repository}}. The order of
+the resulting list is unspecified.
 
 {{reference}} may specify an alternative notes reference namespace,
 which defaults to {{"refs/notes/commits"}}.
 Returns the zero-based integer index of the file specified by {{path}} in the
 given {{index}}, signaling an error if it doesn't exist.
 
-<procedure>(index-ref index key [type]) => index-entry</procedure>
+<procedure>(index-ref index key [type]) => (or index-entry false)</procedure>
 
 Returns the {{index-entry}} from the {{index}} for the given {{key}}, which may
 be an zero-based integer index or a pathname string, or {{#f}} if no such entry

File tests/run.scm

 
 (test-group "status"
   (parameterize ((current-directory repo-path))
-    (test-error "file-status on nonexistent file" (file-status "not-a-file"))
+    (test-error "file-status on nonexistent file" (file-status repo "not-a-file"))
     (test-assert "file-ignored?" (not (find (lambda (f) (file-ignored? repo f)) files)))
     (test "file-statuses" 4 (length (file-statuses repo)))
     (let ((s (car (file-statuses repo))))
     (let ((file (car files))
           (line (car content)))
       (test-assert "diff (clean workdir)" (diff? (diff repo)))
-      (test-assert "diffs (clean workdir)" (null? (diffs repo)))
-      (test-assert "diffs (index to clean workdir)" (null? (diffs repo (index-open repo))))
+      (test "diff (clean workdir)" 0 (diff-num-deltas (diff repo)))
+      (test "diff (index to clean workdir)" 0 (diff-num-deltas (diff repo (index-open repo))))
       ;; Modify workdir state slightly.
       (with-output-to-file file newline #:append)
       (test-assert "diff (dirty workdir)" (diff? (diff repo)))
-      (test "diffs (dirty workdir)" 1 (length (diffs repo)))
-      (test "diffs (index to dirty workdir)" 1 (length (diffs repo (index-open repo))))
+      (test "diff (dirty workdir)" 1 (diff-num-deltas (diff repo)))
+      (test "diff (index to dirty workdir)" 1 (diff-num-deltas (diff repo (index-open repo))))
       (let* ((ts (map commit-tree (commits repo sort: 'rev)))
              (t1 (car ts))
              (t2 (cadr ts)))
-        (test "diffs (tree to workdir)" 1 (length (diffs repo t2)))
-        (test "diffs (tree to index)" 1 (length (diffs repo t1 (index-open repo))))
-        (test "diffs (tree to tree)" 1 (length (diffs repo t1 t2)))
-        (let ((d (car (diffs repo))))
+        (test "diff (tree to workdir)" 1 (diff-num-deltas (diff repo t2)))
+        (test "diff (tree to index)" 1 (diff-num-deltas (diff repo t1 (index-open repo))))
+        (test "diff (tree to tree)" 1 (diff-num-deltas (diff repo t1 t2)))
+        (let ((d (car (diff-deltas (diff repo)))))
           (test-assert "diff-delta?" (diff-delta? d))
           (test "diff-delta-path" file (diff-delta-path d))
           (test "diff-delta-status" 'modified (diff-delta-status d))
     (test-assert "remote-pushurl-set!" (remote-pushurl-set! r (repository-path repo)))
     (test "remote-url after set" (repository-working-directory repo) (remote-url r))
     (test "remote-pushurl after set" (repository-path repo) (remote-pushurl r))
-    (test-assert "remote-connect" (remote-connect r))
+    (test-assert "remote-connect!" (remote-connect! r))
     (test-assert "remote-connected? (connected)" (remote-connected? r))
-    (test-assert "remote-disconnect" (remote-disconnect r))
-    (test-assert "remote-connected? (disconnected)" (not (remote-connected? r)))))
+    (test-assert "remote-disconnect!" (remote-disconnect! r))
+    (test-assert "remote-connected? (disconnected)" (not (remote-connected? r)))
+    (test-assert "remote-download!" (remote-download! r))))
 
 (set! remote (remote cloned-repo "origin"))