Commits

evhan committed c109abc Merge

Merge branch 'development'

  • Participants
  • Parent commits 8dd2d0a, db48c1d

Comments (0)

Files changed (7)

File git-exports.scm

+(export
+ blob*
+ blob*-binary?
+ blob*-content
+ blob*-id
+ blob*-size
+ blob*?
+ blob
+ blob-binary?
+ blob-content
+ blob-id
+ blob-length
+ blob?
+ branch
+ branch-delete
+ branch-head?
+ branch-name
+ branch-rename
+ branches
+ branches-fold
+ checkout
+ clone
+ commit
+ commit-ancestor
+ commit-author
+ commit-committer
+ commit-id
+ commit-message
+ commit-message-encoding
+ commit-parent
+ commit-parent-id
+ commit-parentcount
+ commit-parents
+ commit-time
+ commit-time-offset
+ commit-tree
+ commit-tree-id
+ commit?
+ commits
+ commits-fold
+ config-get
+ config-open
+ config-path
+ config-set!
+ config-unset!
+ config?
+ create-blob*
+ create-blob
+ create-branch
+ create-commit
+ create-note
+ create-reference
+ create-repository
+ create-tag
+ create-tree
+ delete-note
+ diff
+ diff-file-flags
+ diff-file-id
+ diff-file-mode
+ diff-file-path
+ diff-file-size
+ diff-file?
+ diff-new-file
+ diff-old-file
+ diff-path
+ diff-similarity
+ diff-status
+ diff?
+ file-ignored?
+ file-status
+ file-statuses
+ file-statuses-fold
+ index-add
+ index-clear
+ index-entry-ctime
+ index-entry-dev
+ index-entry-extended
+ index-entry-flags
+ index-entry-gid
+ index-entry-id
+ index-entry-ino
+ index-entry-mode
+ index-entry-mtime
+ index-entry-path
+ index-entry-size
+ index-entry-stage
+ index-entry-uid
+ index-entry?
+ index-entrycount
+ index-find
+ index-open
+ index-read
+ index-ref
+ index-remove
+ index-write
+ index?
+ make-signature
+ make-tree-builder
+ merge-base
+ note
+ note-id
+ note-message
+ notes
+ notes-fold
+ object-id
+ object-owner
+ object-sha
+ object-type
+ object=?
+ odb-has-object?
+ odb-hash
+ odb-object-data
+ odb-object-id
+ odb-object-size
+ odb-object-type
+ odb-object?
+ odb-open
+ odb-read
+ odb-write
+ odb?
+ oid->path
+ oid->string
+ oid=?
+ oid?
+ parse-revision-specification
+ reference
+ reference-branch?
+ reference-delete
+ reference-name
+ reference-remote?
+ reference-rename
+ reference-resolve
+ reference-target
+ reference-target-set!
+ reference-type
+ reference?
+ references
+ references-fold
+ refspec-destination
+ refspec-direction
+ refspec-force
+ refspec-source
+ refspec-string
+ refspec?
+ remote
+ remote-connect
+ remote-connected?
+ remote-disconnect
+ remote-download
+ remote-name
+ remote-pushurl
+ remote-pushurl-set!
+ remote-refspecs
+ remote-stats
+ remote-update-fetchhead-set!
+ remote-update-tips
+ remote-url
+ remote-url-set!
+ remote-url-supported?
+ remote-url-valid?
+ remote?
+ remotes
+ repository-bare?
+ repository-empty?
+ repository-head
+ repository-head-detached?
+ repository-head-orphan?
+ repository-open
+ repository-path
+ repository-ref
+ repository-working-directory
+ repository?
+ signature-email
+ signature-name
+ signature-time
+ signature-time-offset
+ signature?
+ string->oid
+ tag
+ tag-delete
+ tag-id
+ tag-message
+ tag-name
+ tag-peel
+ tag-tagger
+ tag-target
+ tag?
+ tags
+ tags-fold
+ transfer-progress-indexed-objects
+ transfer-progress-received-bytes
+ transfer-progress-received-objects
+ transfer-progress-total-objects
+ transfer-progress?
+ tree
+ tree-builder-clear
+ tree-builder-insert
+ tree-builder-ref
+ tree-builder-remove
+ tree-builder-write
+ tree-entries
+ tree-entry->object
+ tree-entry-id
+ tree-entry-name
+ tree-entry-type
+ tree-entry?
+ tree-entrycount
+ tree-fold
+ tree-id
+ tree-ref
+ tree?)

File git-lolevel-exports.scm

+(export
+ blob*-create-frombuffer
+ blob*-create-fromdisk
+ blob*-create-fromworkdir
+ blob*-free
+ blob*-id
+ blob*-is-binary
+ blob*-lookup
+ blob*-rawcontent
+ blob*-rawsize
+ blob-create-frombuffer
+ blob-create-fromdisk
+ blob-create-fromworkdir
+ blob-free
+ blob-id
+ blob-is-binary
+ blob-lookup
+ blob-rawcontent
+ blob-rawsize
+ branch-create
+ branch-delete
+ branch-foreach
+ branch-is-head
+ branch-lookup
+ branch-move
+ branch-name
+ checkout-head
+ checkout-index
+ checkout-tree
+ clone
+ commit-author
+ commit-committer
+ commit-create
+ commit-free
+ commit-id
+ commit-lookup
+ commit-message
+ commit-message-encoding
+ commit-nth-gen-ancestor
+ commit-parent
+ commit-parent-id
+ commit-parentcount
+ commit-time
+ commit-time-offset
+ commit-tree
+ commit-tree-id
+ config-delete-entry
+ config-entry-level
+ config-entry-name
+ config-entry-value
+ config-find-global
+ config-find-system
+ config-find-xdg
+ config-free
+ config-get-bool
+ config-get-int64
+ config-get-string
+ config-open-default
+ config-open-ondisk
+ config-set-bool
+ config-set-int64
+ config-set-string
+ diff-delta-new-file
+ diff-delta-old-file
+ diff-delta-similarity
+ diff-delta-status
+ diff-file-flags
+ diff-file-mode
+ diff-file-oid
+ diff-file-path
+ diff-file-size
+ diff-foreach
+ diff-index-to-workdir
+ diff-tree-to-index
+ diff-tree-to-tree
+ diff-tree-to-workdir
+ error
+ index-add
+ index-add-bypath
+ index-clear
+ index-entry-dev
+ index-entry-extended
+ index-entry-flags
+ index-entry-gid
+ index-entry-ino
+ index-entry-mode
+ index-entry-mtime
+ index-entry-oid
+ index-entry-path
+ index-entry-size
+ index-entry-stage
+ index-entry-uid
+ index-entrycount
+ index-find
+ index-free
+ index-get-byindex
+ index-get-bypath
+ index-open
+ index-read
+ index-remove
+ index-time-seconds
+ index-write
+ index-write-tree
+ libgit2-version
+ merge-base
+ note-create
+ note-default-ref
+ note-foreach
+ note-free
+ note-message
+ note-oid
+ note-read
+ note-remove
+ object-id
+ object-lookup
+ object-owner
+ object-type
+ odb-exists
+ odb-free
+ odb-hash
+ odb-object-data
+ odb-object-free
+ odb-object-id
+ odb-object-size
+ odb-object-type
+ odb-open
+ odb-read
+ odb-write
+ oid-cpy
+ oid-equal
+ oid-fromstr
+ oid-pathfmt
+ oid-tostr
+ reference-create
+ reference-delete
+ reference-foreach-name
+ reference-free
+ reference-is-branch
+ reference-is-remote
+ reference-lookup
+ reference-name
+ reference-rename
+ reference-resolve
+ reference-set-target
+ reference-symbolic-create
+ reference-target
+ reference-type
+ refspec-direction
+ refspec-dst
+ refspec-force
+ refspec-src
+ refspec-string
+ remote-connect
+ remote-connected
+ remote-disconnect
+ remote-download
+ remote-free
+ remote-get-refspec
+ remote-is-valid-name
+ remote-list
+ remote-load
+ remote-name
+ remote-pushurl
+ remote-rename
+ remote-save
+ remote-set-pushurl
+ remote-set-url
+ remote-stats
+ remote-stop
+ remote-supported-url
+ remote-update-fetchhead
+ remote-update-tips
+ remote-url
+ remote-valid-url
+ repository-config
+ repository-discover
+ repository-free
+ repository-head
+ repository-head-detached
+ repository-head-orphan
+ repository-index
+ repository-init
+ repository-is-bare
+ repository-is-empty
+ repository-odb
+ repository-open
+ repository-path
+ repository-workdir
+ revparse
+ revparse-single
+ revspec-from
+ revspec-to
+ revwalk-free
+ revwalk-hide
+ revwalk-new
+ revwalk-next
+ revwalk-push
+ revwalk-push-head
+ revwalk-sorting
+ signature-dup
+ signature-email
+ signature-free
+ signature-name
+ signature-new
+ signature-now
+ signature-time
+ status-file
+ status-foreach
+ status-should-ignore
+ tag-create
+ tag-delete
+ tag-foreach
+ tag-free
+ tag-id
+ tag-lookup
+ tag-message
+ tag-name
+ tag-peel
+ tag-tagger
+ tag-target
+ time-offset
+ time-time
+ transfer-progress-indexed-objects
+ transfer-progress-received-bytes
+ transfer-progress-received-objects
+ transfer-progress-total-objects
+ tree-builder-clear
+ tree-builder-create
+ tree-builder-free
+ tree-builder-get
+ tree-builder-insert
+ tree-builder-remove
+ tree-builder-write
+ tree-entry-byindex
+ tree-entry-byname
+ tree-entry-byoid
+ tree-entry-bypath
+ tree-entry-dup
+ tree-entry-free
+ tree-entry-id
+ tree-entry-name
+ tree-entry-to-object
+ tree-entry-type
+ tree-entrycount
+ tree-free
+ tree-id
+ tree-lookup
+ tree-walk)

File git-lolevel.scm

 ;;; See git.scm for a cleaner, high-level API.
 ;;;
 
-(module git-lolevel *
-  (import scheme lolevel foreign foreigners srfi-69
-    (except chicken repository-path)
-    (only srfi-13 string-index))
-  (require-library srfi-13 lolevel)
+(require-library foreigners lolevel srfi-13 srfi-69)
+
+(module git-lolevel ()
+  (import scheme foreigners lolevel srfi-69)
+  (import foreign)
+  (import (only srfi-13 string-index))
+  (import (except chicken repository-path))
+  (include "git-lolevel-exports.scm")
 
 ;; Errors are composite conditions of properties (exn git).
 (define (git-error loc msg . args)
       `(lambda ,formals
          ,(case type*
             ((oid)
-             `(let ((oid (make-oid)))
-                ((foreign-lambda/retval ,name ,type ,@args) oid ,@formals)
-                (set-finalizer! oid oid-free)))
+             `(let ((object (make-oid)))
+                ((foreign-lambda/retval ,name ,type ,@args) object ,@formals)
+                object))
+            ((revspec)
+             `(let ((object (make-revspec)))
+                ((foreign-lambda/retval ,name ,type ,@args) object ,@formals)
+                object))
             ((strarray)
-             `(let ((sa (make-strarray)))
-                ((foreign-lambda/retval ,name ,type ,@args) sa ,@formals)
-                (strarray-retrieve sa)))
+             `(let-location ((strarray ,type))
+                ((foreign-lambda/retval ,name ,type ,@args) (location strarray) ,@formals)
+                (strarray-retrieve (location strarray))))
             (else
              `(let-location ((object ,type*))
                 ((foreign-lambda/retval ,name (c-pointer ,type) ,@args) (location object) ,@formals)
 ;;;
 ;;; We have to make sure procedures passed to C as callbacks aren't
 ;;; moved by the GC while in use, so we store them in a lookup table and
-;;; pass pointer keys to the libgit2 functions that need them.
+;;; pass integer keys to the libgit2 functions that need them.
 ;;;
 
 (define-values (callback-lookup callback-unregister! callback-register!)
   ((struct time) when signature-time))
 
 (define-foreign-record-type (oid git_oid)
-  (constructor: make-oid)
-  (destructor: oid-free)
   (unsigned-char (id (foreign-value GIT_OID_RAWSZ int)) oid-id))
 
 (define-foreign-record-type (index-time git_index_time)
 (define-foreign-type config           (c-pointer "git_config"))
 (define-foreign-type blob*            (c-pointer "git_blob")) ; clash w/ built-in
 (define-foreign-type index            (c-pointer "git_index"))
+(define-foreign-type iterator         (c-pointer "git_iterator"))
 (define-foreign-type object           (c-pointer "git_object"))
 (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 push             (c-pointer "git_push"))
+(define-foreign-type note             (c-pointer "git_note"))
 (define-foreign-type reference        (c-pointer "git_reference"))
 (define-foreign-type refspec          (c-pointer "git_refspec"))
 (define-foreign-type remote           (c-pointer "git_remote"))
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; blob.h
 
-(define blob*-lookup             (foreign-lambda/allocate blob* git_blob_lookup repository oid))
-(define blob*-lookup-prefix      (foreign-lambda/allocate blob* git_blob_lookup_prefix repository oid unsigned-int))
-(define blob*-create-fromdisk    (foreign-lambda/allocate oid git_blob_create_fromdisk repository nonnull-c-string))
-(define blob*-create-fromworkdir (foreign-lambda/allocate oid git_blob_create_fromworkdir repository nonnull-c-string))
-(define blob*-create-frombuffer  (foreign-lambda/allocate oid git_blob_create_frombuffer repository nonnull-c-string unsigned-int))
-(define blob*-free               (foreign-lambda void git_blob_free blob*))
-(define blob*-rawcontent         (foreign-lambda c-pointer git_blob_rawcontent blob*))
-(define blob*-rawsize            (foreign-lambda size_t git_blob_rawsize blob*))
-(define blob*-is-binary          (foreign-lambda bool git_blob_is_binary blob*))
+(define blob-lookup             (foreign-lambda/allocate blob* git_blob_lookup repository oid))
+(define blob-lookup-prefix      (foreign-lambda/allocate blob* git_blob_lookup_prefix repository oid unsigned-int))
+(define blob-create-fromdisk    (foreign-lambda/allocate oid git_blob_create_fromdisk repository nonnull-c-string))
+(define blob-create-fromworkdir (foreign-lambda/allocate oid git_blob_create_fromworkdir repository nonnull-c-string))
+(define blob-create-frombuffer  (foreign-lambda/allocate oid git_blob_create_frombuffer repository nonnull-c-string unsigned-int))
+(define blob-id                 (foreign-lambda oid git_blob_id blob*))
+(define blob-free               (foreign-lambda void git_blob_free blob*))
+(define blob-rawcontent         (foreign-lambda c-pointer git_blob_rawcontent blob*))
+(define blob-rawsize            (foreign-lambda size_t git_blob_rawsize blob*))
+(define blob-is-binary          (foreign-lambda bool git_blob_is_binary blob*))
+
+(define blob*-lookup             blob-lookup)
+(define blob*-create-frombuffer  blob-create-fromdisk)
+(define blob*-create-fromdisk    blob-create-fromworkdir)
+(define blob*-create-fromworkdir blob-create-frombuffer)
+(define blob*-free               blob-free)
+(define blob*-id                 blob-id)
+(define blob*-is-binary          blob-is-binary)
+(define blob*-rawcontent         blob-rawcontent)
+(define blob*-rawsize            blob-rawsize)
+
+(: blob*-lookup             deprecated)
+(: blob*-create-frombuffer  deprecated)
+(: blob*-create-fromdisk    deprecated)
+(: blob*-create-fromworkdir deprecated)
+(: blob*-free               deprecated)
+(: blob*-id                 deprecated)
+(: blob*-is-binary          deprecated)
+(: blob*-rawcontent         deprecated)
+(: blob*-rawsize            deprecated)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; branch.h
 (define branch-create       (foreign-lambda/allocate reference git_branch_create repository nonnull-c-string commit bool))
 (define branch-move         (foreign-lambda/allocate reference git_branch_move reference nonnull-c-string bool))
 (define branch-upstream     (foreign-lambda/allocate reference git_branch_upstream reference))
+(define branch-name         (foreign-lambda/allocate (const c-string) git_branch_name reference))
 (define branch-set-upstream (foreign-lambda/retval git_branch_set_upstream reference nonnull-c-string))
 (define branch-delete       (foreign-lambda/retval git_branch_delete reference))
 (define branch-is-head      (foreign-lambda bool git_branch_is_head reference))
 
-(define (branch-name ref)
-  (let-location ((str nonnull-c-string))
-    ((foreign-lambda/retval git_branch_name (c-pointer (const c-string)) reference) (location str) ref)
-    str))
-
 (define-foreign-type branch-foreach-cb (function int ((const c-string) branch-type c-pointer)))
 (define-external (branch_foreach_cb (c-string name) (branch-type type) (c-pointer fn)) int
   ((callback-lookup fn) name type) 0)
         repo       flags       (location branch_foreach_cb) callback)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; checkout.h
+
+(define-foreign-type checkout-options (c-pointer "git_checkout_opts")) ; TODO.
+
+(define checkout-head  (foreign-lambda/retval git_checkout_head repository checkout-options))
+(define checkout-index (foreign-lambda/retval git_checkout_index repository index checkout-options))
+(define checkout-tree  (foreign-lambda/retval git_checkout_tree repository object checkout-options))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; commit.h
 
 (define commit-lookup           (foreign-lambda/allocate commit git_commit_lookup repository oid))
 (define commit-lookup-prefix    (foreign-lambda/allocate commit git_commit_lookup_prefix repository oid unsigned-int))
 (define commit-tree             (foreign-lambda/allocate tree git_commit_tree commit))
 (define commit-parent           (foreign-lambda/allocate commit git_commit_parent commit unsigned-int))
+(define commit-nth-gen-ancestor (foreign-lambda/allocate commit git_commit_nth_gen_ancestor commit unsigned-int))
 (define commit-free             (foreign-lambda void git_commit_free commit))
 (define commit-id               (foreign-lambda oid git_commit_id commit))
 (define commit-message          (foreign-lambda c-string git_commit_message commit))
 ;; common.h
 
 (define-foreign-record-type (strarray git_strarray)
-  (constructor: make-strarray)
-  ((c-pointer c-string) strings %strarray-strings)
+  ((c-pointer c-string) strings strarray-strings)
   (unsigned-int count strarray-count))
 
 ;; Get a GC'd list of strings from a strarray.
-(define strarray-strings
+(define strarray-retrieve
   (foreign-lambda* c-string-list* ((strarray sa))
     "int i, l;
      char **t = malloc(sizeof(char *) * (sa->count + 1));
        strncpy(t[i], sa->strings[i], l);
      }
      t[i] = NULL;
+     git_strarray_free(sa);
      C_return(t);"))
 
-(define strarray-free
-  (foreign-lambda void git_strarray_free strarray))
-
-(define (strarray-retrieve sa)
-  (let ((lst (strarray-strings sa)))
-    (strarray-free sa)
-    lst))
-
 (define (libgit2-version)
   (let-location ((major int) (minor int) (rev int))
     ((foreign-lambda void git_libgit2_version (c-pointer int) (c-pointer int) (c-pointer int))
     (vector major minor rev)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; clone.h
+
+(define-foreign-type clone-options (c-pointer "git_clone_options"))
+
+(define clone (foreign-lambda/allocate repository git_clone nonnull-c-string nonnull-c-string clone-options))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; config.h
 
 (define-foreign-record-type (config-entry git_config_entry)
 (define merge-base (foreign-lambda/allocate oid git_merge_base repository oid oid))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; notes.h
+
+(define note-create  (foreign-lambda/allocate oid git_note_create repository signature signature c-string oid nonnull-c-string bool))
+(define note-read    (foreign-lambda/allocate note git_note_read repository c-string oid))
+(define note-remove  (foreign-lambda/retval git_note_remove repository c-string signature signature oid))
+(define note-message (foreign-lambda c-string git_note_message note))
+(define note-oid     (foreign-lambda oid git_note_oid note))
+(define note-free    (foreign-lambda void git_note_free note))
+
+(define (note-default-ref repo)
+  (let-location ((str c-string))
+    ((foreign-lambda/retval git_note_default_ref (c-pointer (const c-string)) repository) (location str) repo)
+    str))
+
+(define-foreign-type note-foreach-cb (function int ((const oid) (const oid) c-pointer)))
+(define-external (note_foreach_cb (oid bid) (oid oid) (c-pointer fn)) int
+  ((callback-lookup fn) bid oid) 0)
+
+(define (note-foreach repo ref fn)
+  (call-with-callback fn
+   (lambda (callback)
+     (guard-errors git_note_foreach
+      ((foreign-safe-lambda int git_note_foreach
+        repository c-string note-foreach-cb            c-pointer)
+        repo       ref      (location note_foreach_cb) callback)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; object.h
 
 (define object-lookup      (foreign-lambda/allocate object git_object_lookup repository oid object-type))
 
 (define oid-fromstr      (foreign-lambda/allocate oid git_oid_fromstr nonnull-c-string))
 (define oid-shorten-add  (foreign-lambda/retval git_oid_shorten_add oid-shorten nonnull-c-string))
-(define oid-cpy          (foreign-lambda void git_oid_cpy oid oid))
 (define oid-cmp          (foreign-lambda int git_oid_cmp oid oid))
 (define oid-ncmp         (foreign-lambda int git_oid_ncmp oid oid unsigned-int))
 (define oid-equal        (foreign-lambda bool git_oid_equal oid oid))
 (define oid-shorten-free (foreign-lambda void git_oid_shorten_free oid-shorten))
 (define oid-allocfmt     (foreign-lambda c-string git_oid_allocfmt oid))
 
+(define (make-oid)
+  (make-locative (make-blob (foreign-value GIT_OID_RAWSZ int))))
+
+(define (oid-cpy oid1)
+  (let ((oid2 (make-oid)))
+    ((foreign-lambda void git_oid_cpy oid oid) oid2 oid1)
+    oid2))
+
 (define (oid-fmt oid)
   (let ((str (make-string 40)))
     ((foreign-lambda void git_oid_fmt scheme-pointer oid) str oid)
 
 (define reference-list                (foreign-lambda/allocate strarray git_reference_list repository))
 (define reference-lookup              (foreign-lambda/allocate reference git_reference_lookup repository nonnull-c-string))
+(define reference-dwim                (foreign-lambda/allocate reference git_reference_dwim repository nonnull-c-string))
 (define reference-symbolic-create     (foreign-lambda/allocate reference git_reference_symbolic_create repository nonnull-c-string nonnull-c-string bool))
 (define reference-create              (foreign-lambda/allocate reference git_reference_create repository nonnull-c-string oid bool))
 (define reference-resolve             (foreign-lambda/allocate reference git_reference_resolve reference))
 (define reference-type                (foreign-lambda reference-type git_reference_type reference))
 (define reference-name                (foreign-lambda c-string git_reference_name reference))
 (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-foreign-type reference-foreach-name-cb (function int ((const c-string) c-pointer)))
 (define-external (reference_foreach_name_cb (c-string name) (c-pointer fn)) int
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; refspec.h
 
+(define-foreign-enum-type (direction int)
+  (direction->int int->direction)
+  ((fetch dir/fetch) GIT_DIRECTION_FETCH)
+  ((push  dir/push)  GIT_DIRECTION_PUSH))
+
 (define refspec-src         (foreign-lambda c-string git_refspec_src refspec))
 (define refspec-dst         (foreign-lambda c-string git_refspec_dst refspec))
+(define refspec-string      (foreign-lambda c-string git_refspec_string refspec))
+(define refspec-direction   (foreign-lambda direction git_refspec_direction refspec))
+(define refspec-force       (foreign-lambda bool git_refspec_force refspec))
 (define refspec-src-matches (foreign-lambda bool git_refspec_src_matches refspec nonnull-c-string))
 (define refspec-dst-matches (foreign-lambda bool git_refspec_dst_matches refspec nonnull-c-string))
 
 (define repository-head-detached (foreign-lambda bool git_repository_head_detached repository))
 (define repository-head-orphan   (foreign-lambda bool git_repository_head_orphan repository))
 
+(define (repository-discover path)
+  (let* ((len (foreign-value GIT_PATH_MAX int))
+         (str (make-string len)))
+    ((foreign-lambda/retval git_repository_discover scheme-pointer size_t nonnull-c-string bool c-string) str len path #f #f)
+    (substring str 0 (string-index str #\x00))))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; remote.h/net.h
 
-(define-foreign-enum-type (direction int)
-  (direction->int int->direction)
-  ((fetch dir/fetch) GIT_DIRECTION_FETCH)
-  ((push  dir/push)  GIT_DIRECTION_PUSH))
-
 (define-foreign-record-type (remote-head git_remote_head)
   (bool         local remote-head-local?)
   ((struct oid) oid   remote-head-id)
   (c-string     name  remote-head-name))
 
 (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 remote-load                 (foreign-lambda/allocate remote git_remote_load repository nonnull-c-string))
 (define remote-create               (foreign-lambda/allocate remote git_remote_create repository nonnull-c-string nonnull-c-string))
 (define remote-create-inmemory      (foreign-lambda/allocate remote git_remote_create_inmemory repository nonnull-c-string nonnull-c-string))
+(define remote-get-refspec          (foreign-lambda refspec git_remote_get_refspec remote int))
 (define remote-connect              (foreign-lambda/retval git_remote_connect remote direction))
 (define remote-save                 (foreign-lambda/retval git_remote_save remote))
 (define remote-set-url              (foreign-lambda/retval git_remote_set_url remote nonnull-c-string))
 (define remote-set-pushurl          (foreign-lambda/retval git_remote_set_pushurl remote nonnull-c-string))
 (define remote-update-tips          (foreign-lambda/retval git_remote_update_tips remote))
 (define remote-update-fetchhead     (foreign-lambda/retval git_remote_update_fetchhead remote))
+(define remote-remove-refspec       (foreign-lambda/retval git_remote_remove_refspec remote int))
+(define remote-clear-refspecs       (foreign-lambda void git_remote_clear_refspecs remote))
+(define remote-refspec-count        (foreign-lambda int git_remote_refspec_count remote))
+(define remote-stats                (foreign-lambda transfer-progress git_remote_stats remote))
 (define remote-free                 (foreign-lambda void git_remote_free remote))
 (define remote-stop                 (foreign-lambda void git_remote_stop remote))
 (define remote-disconnect           (foreign-lambda void git_remote_disconnect remote))
 (define remote-connected            (foreign-lambda bool git_remote_connected remote))
 (define remote-valid-url            (foreign-lambda bool git_remote_valid_url nonnull-c-string))
 (define remote-supported-url        (foreign-lambda bool git_remote_supported_url nonnull-c-string))
+(define remote-is-valid-name        (foreign-lambda bool git_remote_is_valid_name nonnull-c-string))
 
 (define (remote-download remote fn)
   (if (not fn)
 (define revwalk-repository  (foreign-lambda repository git_revwalk_repository revwalk))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; revparse.h
+
+(define-foreign-record-type (revspec git_revspec)
+  (object from revspec-from)
+  (object to revspec-to)
+  (unsigned-int flags revspec-flags))
+
+(define (make-revspec)
+  (make-locative (make-blob (foreign-value "sizeof(git_revspec)" size_t))))
+
+(define revparse-single (foreign-lambda/allocate object git_revparse_single repository nonnull-c-string))
+(define revparse        (foreign-lambda/allocate revspec git_revparse repository nonnull-c-string))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; signature.h
 
 (define signature-new  (foreign-lambda/allocate signature git_signature_new nonnull-c-string nonnull-c-string time-t int))
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; status.h
 
-(define-foreign-enum-type (status unsigned-int #f)
+(define-foreign-enum-type (status int #f)
   (status->int int->status)
   ((current           status/current)           GIT_STATUS_CURRENT)
   ((index/new         status/index/new)         GIT_STATUS_INDEX_NEW)
 ;;; Please report bugs (see README).
 ;;;
 
-(module git
-  (object-id object-type object-sha object-owner object=?
-   string->oid oid->string oid->path oid? oid=? merge-base
-   repository? create-repository repository-open repository-path repository-working-directory
-   repository-empty? repository-bare? repository-head-orphan? repository-head-detached?
-   repository-ref repository-head
-   reference? reference references references-fold create-reference reference-resolve
-   reference-name reference-target reference-type
-   reference-target-set! reference-rename reference-delete
-   branch branches branches-fold create-branch branch-name
-   branch-rename branch-delete branch-is-head?
-   commit? commit commits commits-fold create-commit commit-id
-   commit-message commit-message-encoding
-   commit-time commit-time-offset commit-parentcount
-   commit-author commit-committer commit-parent commit-tree commit-tree-id
-   blob*? blob* create-blob* blob*-content blob*-size blob*-binary?
-   index? index-open index-find index-ref
-   index-clear index-add index-remove index-read index-write index-entrycount
-   index-entry? index-entry-dev index-entry-ino index-entry-mode
-   index-entry-uid index-entry-gid index-entry-size index-entry-stage
-   index-entry-flags index-entry-extended index-entry-path
-   index-entry-id index-entry-ctime index-entry-mtime
-   odb? odb-open odb-has-object? odb-read odb-write odb-hash
-   odb-object? odb-object-id odb-object-data odb-object-size odb-object-type
-   remote remotes remote-name remote-url remote-pushurl
-   remote-connect remote-connected? remote-disconnect remote-download
-   remote-url-set! remote-pushurl-set! remote-update-fetchhead-set!
-   remote-url-valid? remote-url-supported?
-   refspec-source refspec-destination
-   transfer-progress-total-objects transfer-progress-indexed-objects
-   transfer-progress-received-objects transfer-progress-received-bytes
-   signature? make-signature signature-name signature-email signature-time signature-time-offset
-   tag? tag tags tags-fold create-tag tag-id tag-name tag-message tag-delete tag-tagger tag-target tag-peel
-   tree? tree tree-id tree-entrycount create-tree tree-ref tree-fold tree-entries
-   tree-entry? tree-entry-id tree-entry-name tree-entry-type tree-entry->object
-   make-tree-builder tree-builder-ref tree-builder-insert tree-builder-remove tree-builder-clear tree-builder-write
-   diff? diff diff-similarity diff-status diff-path diff-old-file diff-new-file
-   diff-file? diff-file-id diff-file-mode diff-file-path diff-file-size diff-file-flags
-   config? config-open config-path config-get config-set! config-unset!
-   file-status file-statuses file-statuses-fold file-ignored?)
-  (import scheme
-    (except chicken repository-path)
-    (only extras format)
-    (only posix current-directory regular-file?)
-    (only files normalize-pathname make-pathname)
-    (only lolevel record-instance-slot number-of-bytes move-memory! tag-pointer)
-    (rename (only data-structures o) (o compose))
-    (prefix (except git-lolevel git-error) git-)
-    (only git-lolevel git-error))
-  (require-library extras posix files lolevel data-structures git-lolevel)
+(require-library extras posix files lolevel data-structures git-lolevel)
+
+(module git ()
+  (import scheme)
+  (import (except chicken repository-path make-blob blob?))
+  (import (only extras format)
+          (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! tag-pointer)
+          (rename (only data-structures o) (o compose))
+          (prefix (except git-lolevel git-error) git-)
+          (rename (only chicken make-blob)
+                  (make-blob make-chicken-blob)))
+  (include "git-exports.scm")
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 ;;; Syntax helpers.
 ;;;
 
-(define-for-syntax (s+ . args)
-  (string->symbol (apply string-append (map symbol->string args))))
-
 (define-syntax define-git-record-type
-  (lambda (e . c)
-    (let* ((name (caadr e))
-           (attr (cdadr e))
-           (free (cdddr e))
-           (printer (caddr e))
-           (make (s+ 'make- name))
-           (%make (s+ '%make- name))
-           (->pointer (s+ name '->pointer))
-           (pointer-> (s+ 'pointer-> name)))
-      `(begin
-         (define-record ,name >pointer) ; XXX this is lazy
-         (define ,%make ,make)
-         (define-record-printer (,name ,name out)
-           (display ,printer out))
-         (define (,pointer-> ptr)
-           (and-let* ((ptr)
-                      (obj (,%make ptr)))
-             ,(if (null? free)
-                  'obj
-                  `(set-finalizer! obj
-                    (lambda (o)
-                      (,(caar free) (,->pointer o)))))))
-         ,@(map (lambda (attr)
-                  (let ((getter (s+ name '- attr)))
-                    `(define (,getter obj)
-                       ,(case attr
-                          ((id)
-                           `(pointer->oid (,(s+ 'git- getter) (,->pointer obj))))
-                          (else
-                           `(,(s+ 'git- getter) (,->pointer obj)))))))
-                attr)))))
-
-(define ((partial f . a) . b) (apply f (append a b)))
+  (let ((s+ symbol-append))
+    (lambda (e . c)
+      (let* ((name (caadr e))
+             (attr (cdadr e))
+             (free (cdddr e))
+             (printer (caddr e))
+             (make (s+ 'make- name))
+             (%make (s+ '%make- name))
+             (->pointer (s+ name '->pointer))
+             (pointer-> (s+ 'pointer-> name)))
+        `(begin
+           (define-record ,name >pointer) ; XXX this is lazy
+           (define ,%make ,make)
+           (define-record-printer (,name ,name out)
+             (display ,printer out))
+           (define (,pointer-> ptr)
+             (and-let* ((ptr)
+                        (obj (,%make ptr)))
+               ,(if (null? free)
+                    'obj
+                    `(set-finalizer! obj
+                      (lambda (o)
+                        (,(caar free) (,->pointer o)))))))
+           ,@(map (lambda (attr)
+                    (let ((getter (s+ name '- attr)))
+                      (case attr
+                        ((id oid)
+                         `(define (,(s+ name '-id) obj)
+                            (pointer->oid (,(s+ 'git- getter) (,->pointer obj)))))
+                        (else
+                         `(define (,getter obj)
+                            (,(s+ 'git- getter) (,->pointer obj)))))))
+                  attr))))))
+
 (define ((pointer-tagger t) p) (and p (tag-pointer p t)))
+(define ((git-record-attribute-setter f) r v) (f (object->pointer r) v))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
 
 (define oid->path   (compose git-oid-pathfmt oid->pointer))
 (define string->oid (compose pointer->oid git-oid-fromstr))
-(define object-id   (compose pointer->oid git-object-id object->pointer))
+(define object-id   (compose pointer->oid git-oid-cpy git-object-id object->pointer))
 
 (define (->oid obj)
   (cond ((oid? obj) obj)
         ((reference? obj) (reference-target obj))
         (else (object-id obj))))
 
+(define ->oid->pointer
+  (compose oid->pointer ->oid))
+
 (define (->reference-name obj)
   (cond ((string? obj) obj)
         ((reference? obj) (reference-name obj))
 
 (define (pointer->object ptr)
   (case (git-object-type ptr)
-    ((blob)   (pointer->blob* ptr))
+    ((blob)   (pointer->blob ptr))
     ((commit) (pointer->commit ptr))
     ((tag)    (pointer->tag ptr))
     ((tree)   (pointer->tree ptr))
 (define repository-working-directory repository-workdir)
 
 (define (repository-open #!optional (path (current-directory)))
-  ;; Try opening path as a "normal" repo first (i.e. a workdir with a
-  ;; `.git` directory), and if that doesn't work try as a "bare" repo.
   (let ((path (normalize-pathname path)))
     (pointer->repository
      (condition-case
-      (git-repository-open (make-pathname path ".git"))
-      ((git) (git-repository-open path))))))
+       (git-repository-open path)
+       ((git) (git-repository-open (git-repository-discover path)))))))
 
 (define (repository-ref repo ref #!optional (type 'any))
   (condition-case
     (pointer->object
      (git-object-lookup
       (repository->pointer repo)
-      (oid->pointer (->oid ref))
+      (->oid->pointer ref)
       type))
     ((git) #f)))
 
 (define object-owner (compose pointer->repository git-object-owner object->pointer))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Revspec
+
+(define (parse-revision-specification repo str)
+  (condition-case
+    ;; Try a single revision first.
+    (let ((revspec (git-revparse-single (repository->pointer repo) str)))
+      (values (pointer->object revspec) #f))
+    ((git)
+     (condition-case
+       ;; If str didn't specify a single revision, try parsing it as a range.
+       (let ((revspec (git-revparse (repository->pointer repo) str)))
+         (values (pointer->object (git-revspec-from revspec))
+                 (pointer->object (git-revspec-to revspec))))
+       ((git)
+        ;; Neither a single revision nor a range, return falses.
+        ;; XXX Error here?
+        (values #f #f))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; References
 
 (define-git-record-type
 (define repository-head   (compose pointer->reference git-repository-head repository->pointer))
 (define reference-resolve (compose pointer->reference git-reference-resolve reference->pointer))
 
+(define reference-branch? (compose git-reference-is-branch reference->pointer))
+(define reference-remote? (compose git-reference-is-remote reference->pointer))
+
 (define (reference-target ref)
   ;; We have to dig out the intermediate reference in order to free it.
-  (let* ((ref*   (git-reference-resolve (reference->pointer ref)))
-         ; TODO Should use `oid-cpy` here.
-         (target (git-oid-fromstr (git-oid-fmt (git-reference-target ref*)))))
+  (let* ((ref* (git-reference-resolve (reference->pointer ref)))
+         (oid* (git-oid-cpy (git-reference-target ref*))))
     (git-reference-free ref*)
-    (pointer->oid target)))
+    (pointer->oid oid*)))
 
 (define (reference repo name)
   (pointer->reference (git-reference-lookup (repository->pointer repo) name)))
     (pointer->reference
      (if (not symbolic)
          ;; Direct references are created by OID.
-         (git-reference-create repo* name (oid->pointer (->oid target)) force)
+         (git-reference-create repo* name (->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)))))
 
 (define (reference-target-set! ref target)
   (git-reference-set-target
    (reference->pointer ref)
-   (oid->pointer (->oid target))))
+   (->oid->pointer target)))
 
 (define (reference-rename ref name #!optional force)
   (pointer->reference (git-reference-rename (reference->pointer ref) name force)))
 
 (define-git-record-type
   (tree id entrycount)
-  (format "#<tree ~S>" (oid->string (->oid tree) 7))
+  (format "#<tree ~S>" (oid->string (tree-id tree) 7))
   (git-tree-free))
 
 (define-git-record-type
   (pointer->tree
    (git-tree-lookup
     (repository->pointer repo)
-    (oid->pointer (->oid ref)))))
+    (->oid->pointer ref))))
 
 (define (tree-ref tree key)
   (pointer->tree-entry
     (git-tree-walk
      (tree->pointer tree)
      (lambda (path entry*)
-       (void tree) ; Prevent GC.
        (set! state
          (kons path (pointer->tree-entry (git-tree-entry-dup entry*)) state)))
      mode)
     (git-tree-builder-insert
      (tree-builder->pointer builder)
      path
-     (oid->pointer (->oid obj))
+     (->oid->pointer obj)
      attributes))))
 
 (define (tree-builder-write repo tb)
 
 (define branch-name     (compose git-branch-name reference->pointer))
 (define branch-delete   (compose git-branch-delete reference->pointer))
-(define branch-is-head? (compose git-branch-is-head reference->pointer))
+(define branch-head?    (compose git-branch-is-head reference->pointer))
 
 (define (branches-fold kons knil repo #!optional (type 'all))
   (let ((state knil))
   (branches-fold cons '() repo type))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Checkout
+
+(define (checkout repo #!optional object)
+  (cond
+    ((not object)
+     (git-checkout-head (repository->pointer repo) #f))
+    ((index? object)
+     (git-checkout-index (repository->pointer repo) (index->pointer object) #f))
+    (else
+     (git-checkout-tree (repository->pointer repo) (object->pointer object) #f))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Commits
 
 (define-git-record-type
   (commit id message message-encoding time time-offset parentcount)
-  (format "#<commit ~S>" (oid->string (->oid commit) 7))
+  (format "#<commit ~S>" (oid->string (commit-id commit) 7))
   (git-commit-free))
 
 (define commit-tree      (compose pointer->tree git-commit-tree commit->pointer))
-(define commit-tree-id   (compose pointer->oid git-commit-tree-id commit->pointer))
+(define commit-tree-id   (compose pointer->oid git-oid-cpy git-commit-tree-id commit->pointer))
+(define commit-parent-id (compose pointer->oid git-oid-cpy git-commit-parent-id commit->pointer))
 (define commit-author    (compose pointer->signature git-signature-dup git-commit-author commit->pointer))
 (define commit-committer (compose pointer->signature git-signature-dup git-commit-committer commit->pointer))
 
     (pointer->commit (git-commit-parent (commit->pointer cmt) n))
     ((git) #f)))
 
+(define (commit-parents c)
+  (let ((n (commit-parentcount c)))
+    (let lp ((i 0)
+             (p '()))
+      (if (= i n)
+          (reverse p)
+          (lp (+ i 1)
+              (cons (commit-parent c i) p))))))
+
+(define (commit-ancestor cmt #!optional (n 1))
+  (condition-case
+    (pointer->commit (git-commit-nth-gen-ancestor (commit->pointer cmt) n))
+    ((git) #f)))
+
 (define (commit repo ref)
   (pointer->commit
    (git-commit-lookup
     (repository->pointer repo)
-    (oid->pointer (->oid ref)))))
+    (->oid->pointer ref))))
 
 (define (commits-fold kons knil repo #!key initial (hide '()) (sort 'none))
-  (let ((walker #f))
-    (dynamic-wind
-     (lambda ()
-       (set! walker (git-revwalk-new (repository->pointer repo))))
-     (lambda ()
+  (let* ((repo*  (repository->pointer repo))
+         (walker (set-finalizer! (git-revwalk-new repo*) git-revwalk-free)))
+    (call-with-current-continuation
+     (lambda (k)
        ;; Sort mode, one of '(none topo time rev)
        (git-revwalk-sorting walker sort)
        ;; Set hidden commits. These exclude full branches from the
        ;; traversal, rather than just the commits.
-       (for-each (compose (partial git-revwalk-hide walker) oid->pointer ->oid) hide)
-       ;; Set initial revision.
+       (for-each (lambda (h) (git-revwalk-hide walker (->oid->pointer h))) hide)
        (condition-case
-         (begin
-           (if initial
-               (git-revwalk-push walker (oid->pointer (->oid initial)))
-               (git-revwalk-push-head walker))
-           (let lp ((state knil))
-             (condition-case
-               (lp (kons (pointer->commit
-                          (git-commit-lookup
-                           (repository->pointer repo)
-                           (git-revwalk-next walker)))
-                         state))
-               ((git) state))))
-         ((git) '())))
-     (lambda ()
-       (git-revwalk-free walker)))))
+         (if initial ; Set the initial revision.
+             (git-revwalk-push walker (->oid->pointer initial))
+             (git-revwalk-push-head walker))
+         ((git) (k '())))
+       (let loop ((state knil))
+         (loop (kons (pointer->commit
+                      (git-commit-lookup
+                       repo*
+                       (condition-case
+                         (git-revwalk-next walker)
+                         ((git) (k state)))))
+                     state)))))))
 
 (define (commits repo #!rest rest)
   (apply commits-fold cons '() repo rest))
 ;; Blobs
 
 (define-git-record-type
-  (blob* rawsize rawcontent is-binary)
-  (format "#<blob* ~S>" (oid->string (->oid blob*) 7))
-  (git-blob*-free))
-
-(define blob*-size    blob*-rawsize)
-(define blob*-content blob*-rawcontent)
-(define blob*-binary? blob*-is-binary)
-
-(define (blob*-content blob*)
-  (let* ((size (blob*-size blob*))
-         (dest (make-blob size)))
-    (move-memory! (blob*-rawcontent blob*) dest size)
+  (blob id rawsize rawcontent is-binary)
+  (format "#<blob ~S>" (oid->string (blob-id blob) 7))
+  (git-blob-free))
+
+(define blob-length  blob-rawsize)
+(define blob-content blob-rawcontent)
+(define blob-binary? blob-is-binary)
+
+(define (blob-content blob)
+  (let* ((size (blob-rawsize blob))
+         (dest (make-chicken-blob size)))
+    (move-memory! (blob-rawcontent blob) dest size)
     dest))
 
-(define (blob* repo ref)
-  (pointer->blob*
-   (git-blob*-lookup
+(define (blob repo ref)
+  (pointer->blob
+   (git-blob-lookup
     (repository->pointer repo)
-    (oid->pointer (->oid ref)))))
+    (->oid->pointer ref))))
 
-(define (create-blob* repo source)
+(define (create-blob repo source)
   (let ((repo* (repository->pointer repo)))
-    (pointer->blob*
-     (git-blob*-lookup
+    (pointer->blob
+     (git-blob-lookup
       repo*
       (cond ((blob? source)
-             (git-blob*-create-frombuffer repo* source))
+             (git-blob-create-frombuffer repo* source))
             ((string? source)
              (if (regular-file? source)
-                 (git-blob*-create-fromdisk repo* source)
-                 (git-blob*-create-fromworkdir repo* source)))
+                 (git-blob-create-fromdisk repo* source)
+                 (git-blob-create-fromworkdir repo* source)))
             (else
-             (git-error 'create-blob* "Invalid blob* source" source)))))))
+             (git-error 'create-blob "Invalid blob source" source)))))))
+
+(define blob* blob)
+(define blob*-binary? blob-binary?)
+(define blob*-content blob-content)
+(define blob*-id blob-id)
+(define blob*-size blob-length)
+(define blob*? blob?)
+(define create-blob* create-blob)
+
+(: blob*         deprecated)
+(: blob*-binary? deprecated)
+(: blob*-content deprecated)
+(: blob*-id      deprecated)
+(: blob*-size    deprecated)
+(: blob*?        deprecated)
+(: create-blob*  deprecated)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Index
   (index-entry dev oid ino mode uid gid size stage flags extended path)
   (format "#<index-entry ~S>" (index-entry-path index-entry)))
 
-(define index-entry-id    (compose pointer->oid index-entry-oid))
 (define index-entry-ctime (compose git-index-time-seconds git-index-entry-mtime index-entry->pointer))
 (define index-entry-mtime (compose git-index-time-seconds git-index-entry-mtime index-entry->pointer))
 
    repo))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Notes
+
+(define-git-record-type
+  (note message oid)
+  "#<note>"
+  (git-note-free))
+
+(define (note repo object #!optional reference)
+  (pointer->note
+   (git-note-read
+    (repository->pointer repo)
+    reference
+    (->oid->pointer object))))
+
+(define (delete-note repo #!key target reference author (committer author))
+  (git-note-remove
+   (repository->pointer repo)
+   reference
+   (signature->pointer author)
+   (signature->pointer committer)
+   (->oid->pointer target)))
+
+(define (create-note repo #!key message target reference author (committer author) force)
+  (let ((repo* (repository->pointer repo))
+        (oid*  (->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...
+    (git-note-create
+     repo*
+     (signature->pointer author)
+     (signature->pointer committer)
+     reference
+     oid*
+     message
+     force)
+    ;; ... Then retrieve it by reading it from the target object.
+    (pointer->note
+     (git-note-read
+      repo*
+      reference
+      oid*))))
+
+(define notes-fold
+  (let ((reference-exists?
+         (lambda (repo* ref)
+           (condition-case
+             (and (git-reference-free (git-reference-lookup repo* ref)) #t)
+             ((git) #f)))))
+    (lambda (kons knil repo #!optional reference)
+      (let ((repo* (repository->pointer repo)))
+        ;; If the notes reference doesn't exist, return knil immediately.
+        (if (not (reference-exists? repo* (or reference (git-note-default-ref repo*))))
+            knil
+            (let ((state knil))
+              (git-note-foreach
+               repo*
+               reference
+               (lambda (bid* oid*)
+                 (set! state
+                   (kons (pointer->note (git-note-read repo* reference oid*)) state))))
+              state))))))
+
+(define (notes repo #!optional reference)
+  (notes-fold cons '() repo reference))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; ODB
 
 (define-git-record-type
 (define (odb-has-object? odb obj)
   (git-odb-exists
    (odb->pointer odb)
-   (oid->pointer (->oid obj))))
+   (->oid->pointer obj)))
 
 (define (odb-open loc)
   (pointer->odb
   (pointer->odb-object
    (git-odb-read
     (odb->pointer odb)
-    (oid->pointer (->oid obj)))))
+    (->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)))
 (define (odb-object-data obj)
   (let* ((data (git-odb-object-data (odb-object->pointer obj)))
          (size (odb-object-size obj))
-         (dest (make-blob size)))
+         (dest (make-chicken-blob size)))
     (move-memory! data dest size)
     dest))
 
   (pointer->tag
    (git-tag-lookup
     (repository->pointer repo)
-    (oid->pointer (->oid ref)))))
+    (->oid->pointer ref))))
 
 (define (tags-fold kons knil repo)
   (let ((state knil))
 (define diff-similarity diff-delta-similarity)
 (define diff-old-file   (compose pointer->diff-file diff-delta-old-file))
 (define diff-new-file   (compose pointer->diff-file diff-delta-new-file))
-(define diff-file-id    (compose pointer->oid diff-file-oid))
 
 (define (diff-path diff)
   (diff-file-path
   (git-remote-free))
 
 (define-git-record-type
-  (refspec src dst)
+  (refspec src dst string direction force)
   (format "#<refspec ~S ~S>" (refspec-src refspec) (refspec-dst refspec)))
 
 (define-git-record-type
 (define remote-connected? remote-connected)
 (define remote-url-valid? git-remote-valid-url)
 (define remote-url-supported? git-remote-supported-url)
-
-(define ((remote-attribute-setter fn) rem arg) (fn (refspec->pointer rem) arg))
-(define remote-url-set!              (remote-attribute-setter git-remote-set-url))
-(define remote-pushurl-set!          (remote-attribute-setter git-remote-set-pushurl))
-(define remote-update-fetchhead-set! (remote-attribute-setter git-remote-update-fetchhead))
+(define remote-name-valid? git-remote-is-valid-name)
+
+(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-refspecs rem)
+  (let ((tag (compose pointer->refspec (pointer-tagger rem))))
+    (let lp ((i 0)
+             (a '()))
+      (cond ((git-remote-get-refspec (remote->pointer rem) i) =>
+             (lambda (refspec)
+               (lp (fx+ i 1)
+                   (cons (tag refspec) a))))
+            (else a)))))
 
 (define (remote repo name)
   (pointer->remote (git-remote-load (repository->pointer repo) name)))
 
 (define (remotes repo)
-  (map (cut remote repo <>) (git-remote-list (repository->pointer repo))))
+  (map (lambda (name) (remote repo name))
+       (git-remote-list (repository->pointer repo))))
 
 (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
+   (tag-pointer (git-remote-stats (remote->pointer rem)) rem)))
+
 (define (remote-download rem #!optional callback)
   (if (remote-connected? rem)
       (git-remote-download
        (remote->pointer rem)
        (and callback (compose callback pointer->transfer-progress)))
       (dynamic-wind
-       (lambda () (remote-connect rem))
-       (lambda () (remote-download rem callback))
-       (lambda () (remote-disconnect rem)))))
+       (lambda ()
+         (remote-connect rem))
+       (lambda ()
+         (remote-download rem callback)
+         (remote-update-tips rem)
+         (remote-stats rem))
+       (lambda ()
+         (remote-disconnect rem)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; Cloning
+;;;
+
+(define (clone url path) (pointer->repository (git-clone url path #f)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Configs
     ((xdg)
      (git-config-find-xdg))
     (else
-     (git-error 'config-open "Invalid configuration file path type" type))))
+     (git-error 'config-path "Invalid configuration file path type" type))))
 
 (define (config-open #!optional target)
   (pointer->config
 (define *required-chicken* '("4.7.0"  . #(4 7 0)))
 (define *required-libgit2* '("0.19.0" . #(0 19 0)))
 
+;; Verify the CHICKEN version.
 (when (string<? (chicken-version) (car *required-chicken*))
   (fprintf (current-error-port)
            "\n  git: This extension requires Chicken version ~a or later."
            (car *required-chicken*))
   (exit 1))
 
-(compile -s -O2 -d0 -J -lgit2 git-lolevel.scm)
-(compile -s -O2 -d0 -J -lgit2 git.scm)
-(compile -s -O3 -d0 git-lolevel.import.scm)
-(compile -s -O3 -d0 git.import.scm)
-
-(load "./git-lolevel.so")
-(load "./git-lolevel.import.so")
-(import (only git-lolevel libgit2-version))
+;; Verify the libgit2 version.
+;;
+;; If pkg-config is available, consult it first.
+(define pkg-config-version
+  (string-trim-both
+   (with-input-from-pipe "pkg-config --modversion libgit2" read-string)))
 
-(when (not (equal? (libgit2-version) (cdr *required-libgit2*)))
+(unless (or (string-null? pkg-config-version)
+            (string=? pkg-config-version (car *required-libgit2*)))
   (fprintf (current-error-port)
-           "\n  git: This extension requires libgit2 ~a."
+           "\n  git: This extension requires libgit2 version ~a or later."
            (car *required-libgit2*))
   (exit 1))
 
+;; Shared libraries.
+(compile -sJ -O3 -d0 git-lolevel.scm -lgit2)
+(compile -sJ -O3 -d0 git.scm)
+
+;; Static libraries.
+(compile -unit git-lolevel -cJ -O3 -d0 git-lolevel.scm -o git-lolevel.o)
+(compile -unit git -uses git-lolevel -cJ -O3 -d0 git.scm -o git.o)
+(run (ar -rc git.a git.o git-lolevel.o))
+
+;; Import libraries.
+(compile -s -O3 -d0 git-lolevel.import.scm)
+(compile -s -O3 -d0 git.import.scm)
+
+;; If we couldn't detect libgit2's version via pkg-config, check it
+;; directly now.
+(when (string-null? pkg-config-version)
+  (load "./git-lolevel.so")
+  (load "./git-lolevel.import.so")
+  (import (only git-lolevel libgit2-version))
+  (when (not (equal? (libgit2-version) (cdr *required-libgit2*)))
+    (fprintf (current-error-port)
+             "\n  git: This extension requires libgit2 ~a."
+             (car *required-libgit2*))
+    (exit 1)))
+
 (install-extension 'git
   '("git-lolevel.so" "git-lolevel.import.so"
-    "git.so" "git.import.so")
+    "git.so" "git.import.so"
+    "git.a" "git.o" "git-lolevel.o")
   '((version "0.0.21")))
 <procedure>(repository-ref repository ref) => object</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
-object will be of type {{blob*}}, {{commit}}, {{tag}} or {{tree}}, or {{#f}} if
+{{oid}}, {{reference}}, {{blob}}, {{commit}}, {{tag}} or {{tree}}. The returned
+object will be of type {{blob}}, {{commit}}, {{tag}} or {{tree}}, or {{#f}} if
 no object matching the given {{ref}} is found.
 
 <procedure>(repository-empty? repository) => boolean</procedure>
 
 Indicates whether the two {{oid}}s are the equivalent.
 
+==== Revspec
+
+<procedure>(parse-revision-specification string) => (values object object)</procedure>
+
+Attempts to parse a string as a Git revision specification, returning
+two values:
+
+* If {{string}} specifies a range of revisions, the start & end
+  {{commit}} objects.
+* If {{string}} specifies a single revision, the {{commit}} object and
+  {{#f}}.
+* If {{string}} is invalid or fails to match a revision, {{#f}} for both
+  values.
+
+For more information about specifying revisions, see
+[[http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions]].
+
 ==== Reference
 
 <record>reference</record>
 as {{"HEAD"}}, {{"ref/heads/master"}}, or {{"refs/tags/v0.1.0"}}. An error is
 signalled if the reference doesn't exists.
 
+<procedure>(reference-name reference) => string</procedure>
+<procedure>(reference-id reference) => oid</procedure>
+
+Returns the name of the given {{reference}} and the {{oid}} of the
+object referred to by the given {{reference}}, respectively.
+
+<procedure>(reference-type reference) => symbol</procedure>
+
+Returns the type symbol of the given {{reference}}, which will be either
+{{'oid}} or {{'symbolic}}.
+
+<procedure>(reference-remote? reference) => boolean</procedure>
+<procedure>(reference-branch? reference) => boolean</procedure>
+
+These procedures return boolean values indicating whether the given
+reference is a remote- or branch-type reference, respectively.
+
 <procedure>(references-fold kons knil repository [type]) => object</procedure>
 
 Folds over the given {{repository}}'s references in unspecified order.
 Returns a reference to this repository's HEAD (resolved to an OID), of
 {{#f}} if it doesn't exist.
 
-<procedure>(reference-name reference) => string</procedure>
-<procedure>(reference-id reference) => oid</procedure>
-
-Returns the name of the given {{reference}} and the {{oid}} of the
-object referred to by the given {{reference}}, respectively.
-
 <procedure>(reference-resolve reference) => reference</procedure>
 
-Dereferences the given (possibly symbolic) {{reference}}, returning a new
-non-symbolic {{reference}} pointing refering directly to an {{oid}}.
+Recursively dereferences the given (possibly symbolic) {{reference}},
+returning a new non-symbolic {{reference}} pointing refering directly to
+an {{oid}}.
 
 <procedure>(reference-target reference) => string</procedure>
 
 
 On success, the on-disk repository is updated immediately.
 
+<procedure>(reference-delete reference) => void</procedure>
+
+Deletes the given {{reference}} from its repository.
+
+On success, the on-disk repository is updated immediately.
+
 ==== Branch
 
 <procedure>(create-branch repository name target [force]) => reference</procedure>
 <procedure>(object-id object) => oid</procedure>
 
 Returns the {{oid}} of the given object, which must be a {{commit}},
-{{tree}}, {{tag}} or {{blob*}}.
+{{tree}}, {{tag}} or {{blob}}.
 
 <procedure>(object-sha object [length]) => string</procedure>
 
 Returns the SHA1 identifier corresponding to the given object, which may be a
-{{commit}}, {{tree}}, {{tag}} {{blob*}}, {{reference}}, {{oid}} or {{string}}.
+{{commit}}, {{tree}}, {{tag}} {{blob}}, {{reference}}, {{oid}} or {{string}}.
 
 <procedure>(object-type object) => symbol</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
+a {{commit}}, {{tree}}, {{tag}} or {{blob}}. {{#f}} is returned if the
 object's type cannot be determined.
 
 <procedure>(object=? object1 object2) => boolean</procedure>
 
 Indicates whether the two Git objects represent the same thing (judging
-by their respective {{oid}}s). Each object of type {{commit}}, {{tree}},
-{{tag}} or {{blob*}}.
+by their respective {{oid}}s). Each object should be of type {{commit}},
+{{tree}}, {{tag}} or {{blob}}.
+
+==== Blob
 
-==== Blob*
+<record>blob</record>
+<procedure>(blob? obj) => boolean</procedure>
 
-<record>blob*</record>
-<procedure>(blob*? obj) => boolean</procedure>
+A {{blob}} corresponds to Git's Blob object type. It represents a chunk
+of data, generally a file.
 
-A {{blob*}} corresponds to Git's Blob object type, renamed in order to avoid
-name clashes with Chicken's built-in {{blob}} type. It represents a file.
+Beware name collisions with CHICKEN's built-in {{blob}} data type.
 
-<procedure>(blob* repository ref) => blob*</procedure>
+<procedure>(blob repository ref) => blob</procedure>
 
-Returns the {{blob*}} specified by the given {{ref}} from the repository.
-{{ref}} may be a SHA1 string, {{oid}}, or {{blob*}}. An error is signaled if
-no such {{blob*}} exists.
+Returns the {{blob}} specified by the given {{ref}} from the repository.
+{{ref}} may be a SHA1 string, {{oid}}, or {{blob}}. An error is signaled if
+no such {{blob}} exists.
 
-<procedure>(blob*-content blob*) => blob</procedure>
+<procedure>(blob-id commit) => oid</procedure>
+
+Returns the {{oid}} of the given {{blob}}.
+
+<procedure>(blob-content blob) => blob</procedure>
 
 Returns a blob (of the CHICKEN built-in type) of the contents of the
-given {{blob*}} (of the Git object type).
+given {{blob}} (of the Git object type).
 
-<procedure>(blob*-size blob*) => int</procedure>
+<procedure>(blob-length blob) => int</procedure>
 
-Returns the size in bytes of the given {{blob*}}.
+Returns the size in bytes of the given {{blob}}.
 
-<procedure>(blob*-binary? blob*) => boolean</procedure>
+<procedure>(blob-binary? blob) => boolean</procedure>
 
-Indicates whether the given {{blob*}} is (likely to be) a binary chunk
+Indicates whether the given {{blob}} is (likely to be) a binary chunk
 or not.
 
-<procedure>(create-blob* repository source) => blob*</procedure>
+<procedure>(create-blob repository source) => blob</procedure>
 
-Creates a new {{blob*}} in the given {{repository}}.
+Creates a new {{blob}} in the given {{repository}}.
 
-{{source}} is the data source for the {{blob*}}, and may be a blob (of
+{{source}} is the data source for the {{blob}}, and may be a blob (of
 the CHICKEN built-in type) or a pathname string indicating a file on the
 local disk or, if such a file isn't present, a file relative to the
 repository's working directory.
 
 <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>
 
-{{commit-parentcount}} returns the number of parents for a given {{commit}}.
+{{commit-parentcount}} returns the number of parents of the given
+{{commit}}.
 
 {{commit-parent}} returns the {{n}}th parent of the given {{commit}}, or
 the first parent if no {{n}} is given. If the {{commit}} has no parent,
 {{#f}} is returned.
 
+{{commit-parents}} returns the possibly-empty list of all parents of the
+given {{commit}}. The order of this list is unspecified.
+
+{{commit-ancestor}} returns the {{n}}th ancestor of the given commit, or
+{{#f}} is no such ancestor exists. {{n}} defaults to {{1}}, meaning the
+{{commit}}'s first parent (making {{(commit-ancestor c 1)}} equivalent
+to {{(commit-parent c)}}).
+
 <procedure>(create-commit repository #!key message author [committer] [tree] [parents] [reference]) => commit</procedure>
 
 Creates & returns a new commit in the given {{repository}}. The string
 Creates & returns a new tag in the given {{repository}} for the specified
 {{name}}, {{message}} and {{target}}. {{name}} and {{message}} must be strings,
 {{tagger}} must be a {{signature}},and {{target}} must be a {{commit}},
-{{tree}} or {{blob*}}. If a tag of the given {{name}} exists and {{force}} is
+{{tree}} or {{blob}}. If a tag of the given {{name}} exists and {{force}} is
 not given or {{#f}}, an error is signalled.  Otherwise, creation is forced and
 the old tag will be overwritten.
 
 
 <procedure>(tree-entry->object repository tree-entry) => object</procedure>
 
-Returns an object of type {{tree}} or {{blob*}} from the given {{tree-entry}}
+Returns an object of type {{tree}} or {{blob}} from the given {{tree-entry}}
 and {{repository}}, which must be the owner of the {{tree-entry}}.
 
 ==== Tree Builder
 <procedure>(tree-builder-insert tree-builder object name attributes) => tree-entry</procedure>
 
 Inserts {{object}} into the {{tree-builder}}'s tree under the given filename
-{{name}}. The inserted object must be a {{tree}} or {{blob*}}, and will have
+{{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>
 
 Removes the object at the given {{path}} from the {{tree-builder}}'s tree.
 
-<procedure>(tree-builder-write repo tree-builder) => tree</procedure>
+<procedure>(tree-builder-write repository tree-builder) => tree</procedure>
 
 Writes the {{tree-builder}}'s current state to the given {{repository}},
 modifying the on-disk repository on success. The resulting {{tree}} is
 Returns a boolean indicating whether the given {{path}} in {{repository}} is
 ignored by Git or not.
 
+==== Note
+
+<record>note</record>
+<procedure>(note? obj) => boolean</procedure>
+
+A {{note}} is a type of reference, stored under the {{refs/notes}}
+namespace. It's used to associate some data (accessible via
+{{note-message}}) with an object.
+
+<procedure>(note-message note) => string</procedure>
+<procedure>(note-id note) => oid</procedure>
+
+{{note-message}} and {{note-id}} return the string content and {{oid}}
+of the given {{note}}, respectively.
+
+<procedure>(note repository object [reference]) => note</procedure>
+
+Returns the {{note}} associated with the given {{object}} in the
+{{repository}}. {{reference}} may be a string specifying an alternative
+notes reference namespace, which defaults to {{"refs/notes/commits"}}.
+An error is signaled if no such {{note}} exists.
+
+<procedure>(delete-note repository #!key target author [committer] [reference]) => void</procedure>
+
+Deletes all notes for the given object {{target}} by the given
+{{author}} in the {{repository}}. {{author}} and {{committer}} should be a
+{{signature}}s, while {{reference}} may specify an alternative notes
+reference namespace, which defaults to {{"refs/notes/commits"}}.
+
+On success, the note is removed from disk immediately. An error is
+signaled if no such {{note}} exists.
+
+<procedure>(create-note repository #!key message target reference author [committer] [force]) => note</procedure>
+
+Creates & returns a new {{note}} in the given {{repository}} with the
+specified {{message}} and {{target}} object. {{author}} and
+{{committer}} should be {{signature}}s, while {{reference}} may specify
+an alternative notes reference namespace, which defaults to
+{{"refs/notes/commits"}}. If {{force}} is given and not {{#f}}, an
+existing note for the same {{target}} and {{author}}/{{committer}} will
+be overwritten; otherwise, an error will be signaled if such a {{note}}
+already exists.
+
+On success, the on-disk repository is updated immediately.
+
+<procedure>(notes-fold kons knil repository [reference]) => object</procedure>
+
+Folds over the given {{repository}}'s notes in unspecified order.
+
+{{reference}} may specify an alternative notes reference namespace,
+which defaults to {{"refs/notes/commits"}}.
+
+<procedure>(notes repository [reference]) => list</procedure>
+
+Returns a list of all notes in the given {{repository}}.
+
+{{reference}} may specify an alternative notes reference namespace,
+which defaults to {{"refs/notes/commits"}}.
+
 ==== Index
 
 <record>index</record>
 to a given {{index}} exist only in memory until written to disk using
 {{index-write}}.
 
-<procedure>(index-open repo-or-path) => index</procedure>
+<procedure>(index-open repository-or-path) => index</procedure>
 
-It {{repo-or-path}} is a {{repository}}, returns the repository's index.  If it
-is a string, creates and returns the index at the given path, signaling an
-error if such an index doesn't exist. It is not possible to open the index of a
-bare repository, and doing so will result in an exception.
+It {{repository-or-path}} is a {{repository}}, {{index-open}} returns
+the repository's index. If it's a string, {{index-open}} creates and
+returns the index at the given path, signaling an error if such an index
+doesn't exist. It is not possible to open the index of a bare
+repository, and doing so will result in an exception.
 
 <procedure>(index-entrycount index) => int</procedure>
 
 
 An {{odb}} offers a direct interface to Git's internal object database.
 
-<procedure>(odb-open repo-or-path) => odb</procedure>
+<procedure>(odb-open repository-or-path) => odb</procedure>
 
-It {{repo-or-path}} is a {{repository}}, returns the repository's object
-database. If it is a string, creates and returns the object database at the
-given path, signaling an error if no such database exists.
+It {{repository-or-path}} is a {{repository}}, returns the repository's
+object database. If it is a string, creates and returns the object
+database at the given path, signaling an error if no such database
+exists.
 
 <procedure>(odb-has-object? odb ref) => boolean</procedure>
 
 Determines if the given {{odb}} contains the given object {{ref}}, which should
-be a SHA1 string, {{oid}} or Git object of type {{commit}}, {{blob*}}, {{tree}}
+be a SHA1 string, {{oid}} or Git object of type {{commit}}, {{blob}}, {{tree}}
 or {{tag}}.
 
 <procedure>(odb-read odb ref) => odb-object</procedure>
 
 Reads the given object {{ref}} from the database, signaling an error if it
 doesn't exist. {{ref}} should be a SHA1 string, {{oid}} or Git object of type
-{{commit}}, {{blob*}}, {{tree}} or {{tag}}.
+{{commit}}, {{blob}}, {{tree}} or {{tag}}.
 
 <procedure>(odb-write odb data [type]) => oid</procedure>
 
 {{signature-time}} and {{signature-time-offset}} return the timestamp of the
 given {{signature}} and its UTC offset in minutes, respectively.
 
+==== Clone
+
+<procedure>(clone url path) => void</procedure>
+
+Clones the remote Git repository specified by {{url}} into the local
+pathname {{path}}.
+
+On success, the branch indicated by the "HEAD" reference of the remote
+repository is checked out as a normal (i.e. non-bare) Git working
+directory.
+
+An error is signaled if the clone fails for any reason.
+
 ==== Config
 
 <record>config</record>

File tests/run.scm

 
 (define repo #f)
 (define repo-path "repo")
+(define cloned-repo #f)
+(define cloned-repo-path "cloned-repo")
 (define sha1 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
 (define sha1-path "aa/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
 (define reference-name-oid "refs/heads/mimsy")
 (define tag-name-two "0.0.1-β")
 (define tag-message-one "tagged the Jabberwock slain")
 (define tag-message-two "tagged the Jabberwock slain twice")
+(define note-message-one "beware the Jubjub bird on line 1871")
 (define name "Bandersnatch")
 (define email "banders@example.com")
 (define files
 
 (test-group "status"
   (parameterize ((current-directory repo-path))
+    (test-error "file-status on nonexistent file" (file-status "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))))
+      (test-assert "file-statuses (path)" (member (car s) files))
+      (test "file-statuses (status)" 'index/new (cdr s)))
     (let ((file    (car files))
           (content (car content)))
-      (test-error "file-status on nonexistent file" (file-status "not-a-file"))
-      (test-assert "file-ignored?" (not (file-ignored? repo file)))
       (test "file-status (single status)" 'index/new (file-status repo file))
       (with-output-to-file file newline #:append)
       (test "file-status (multiple statuses)"
-            '(index/new worktree/modified) (file-status repo file))
+            '(index/new worktree/modified)
+            (file-status repo file))
       (with-output-to-file file (cut display content)))))
 
 (test-group "commit"
           (oid->string (commit-id commit))
           (oid->string (reference-target ref)))
     (test-assert "reference?" (reference? ref))
+    (test-assert "reference-branch?" (reference-branch? ref))
+    (test-assert "reference-remote?" (not (reference-remote? ref)))
     (test-assert "reference-target" (oid? (reference-target ref)))
     (test-assert "reference-resolve" (reference? (reference-resolve ref)))
     (test "reference-name" reference-name-symbolic (reference-name ref))
 
 (set! reference (car (references repo)))
 
+(test-group "note"
+  (test-error "note with nonexistent sha" (note repo sha1))
+  (test "notes with no notes" 0 (length (notes repo)))
+  (test-assert "create-note (default reference)"
+               (create-note repo
+                   message: note-message-one
+                    target: commit
+                    author: (make-signature name email)))
+  (test-assert "note with commit" (note repo commit))
+  (test-error "note with commit without note" (note repo (commit-parent commit)))
+  (test-error "note with alternate reference" (note repo commit "refs/notes/jubjub"))
+  (test "notes with one note" 1 (length (notes repo)))
+  (test "notes with alternate reference" 0 (length (notes repo "refs/notes/jubjub")))
+  (let ((note (note repo commit)))
+    (test "note-message" note-message-one (note-message note))
+    (test-assert "note-id" (oid? (note-id note))))
+  (test-assert "delete-note (default reference)"
+               (delete-note repo
+                    target: commit
+                    author: (make-signature name email)))
+  (test-error "note after delete" (note repo commit))
+  (test "notes after delete" 0 (length (notes repo))))
+
 (test-group "branch"
   (test-error "branch with nonexistent oid" (branch repo sha1))
   (test-error "branch with nonexistent name" (branch repo branch-name-one))
     (test-assert "branch-rename" (branch-rename b branch-name-two))
     (test-error "branch with new branch name" (branch repo branch-name-one))
     (test-assert "branch with old branch name" (branch repo branch-name-two)))
-  (test-assert "branch-is-head? on head branch"
-               (branch-is-head? (branch repo "master")))
-  (test-assert "branch-is-head? on not head branch"
-               (not (branch-is-head? (branch repo branch-name-two))))
+  (test-assert "branch-head? on head branch"
+               (branch-head? (branch repo "master")))
+  (test-assert "branch-head? on not head branch"
+               (not (branch-head? (branch repo branch-name-two))))
   (let ((b (branch repo branch-name-two)))
     (test-assert "branch-delete" (branch-delete b))
     (test-error "deleted branch is deleted" (branch repo branch-name-two))))
 
 (set! tag (car (tags repo)))
 
-(test-group "blob*"
-  (test-error "blob* with nonexistent sha" (blob* repo (make-string 40 #\a)))
+(test-group "blob"
+  (test-error "blob with nonexistent sha" (blob repo (make-string 40 #\a)))
   (let ((t (commit-tree commit)))
     (parameterize ((current-directory repo-path))
       (for-each
        (lambda (file content)
          (let ((b (tree-entry->object repo (tree-ref t file))))
-           (test-assert "blob*?" (blob*? b))
+           (test-assert "blob?" (blob? b))
            (test-assert "object-id (blob)" (oid? (object-id b)))
            (test "object-type (blob)" 'blob (object-type b))
-           (test "blob*-size" (file-size file) (blob*-size b))
-           (test-assert "blob* with blob" (blob* repo b))
-           (test-assert "blob* with oid" (blob* repo (object-id b)))
-           (test "blob*-content" content (blob->string (blob*-content b)))))
+           (test "blob-length" (file-size file) (blob-length b))
+           (test-assert "blob with blob" (blob repo b))
+           (test-assert "blob with oid" (blob repo (object-id b)))
+           (test "blob-content" content (blob->string (blob-content b)))))
        files
        content))))
 
            (test-assert "tree-builder-ref" (tree-builder-ref tb file))
            (test "tree-entry-name" file (tree-entry-name e))
            (test-assert "tree-entry-id" (oid? (tree-entry-id e)))
-           (test-assert "tree-entry->object" (blob*? (tree-entry->object repo e)))
+           (test-assert "tree-entry->object" (blob? (tree-entry->object repo e)))
            (test "tree-entry-type" 'blob (tree-entry-type e))))
        (drop files 1)
        (drop content 1))
            (test-assert "tree-entry?" (tree-entry? e))
            (test-assert "tree-entry-id" (oid? (tree-entry-id e)))
            (test "tree-entry-name" file (tree-entry-name e))
-           (test-assert "tree-entry->object" (blob*? (tree-entry->object repo e)))
+           (test-assert "tree-entry->object" (blob? (tree-entry->object repo e)))
            (test "tree-entry-type" 'blob (tree-entry-type e))))
        files
        content))))
      files
      content)))
 
+(test-group "revision parsing"
+  (let ((revparse
+         (lambda (s)
+           (receive (parse-revision-specification repo s)))))
+    (test "revparse with invalid revision" '(#f #f) (revparse "foo"))
+    (test "revparse with invalid revisions" '(#f #f) (revparse "foo..bar"))
+    (let ((revspec (revparse "HEAD")))
+      (test-assert "revparse with HEAD (1st value)"
+                   (object=? (car (commits repo sort: 'rev))
+                             (car (revparse "HEAD"))))
+      (test-assert "revparse with HEAD (2nd value)"
+                   (not (cadr (revparse "HEAD")))))
+    (let ((revspec (revparse (oid->string (commit-id commit)))))
+      (test-assert "revparse with sha1 (1st value)"
+                   (object=? commit (car revspec)))
+      (test-assert "revparse with sha1 (2nd value)"
+                   (not (cadr revspec))))
+    (let ((revspec (revparse (string-append
+                              (oid->string (commit-id commit))
+                              ".."
+                              (oid->string (commit-id (commit-parent commit)))))))
+      (test-assert "revparse with sha1 range (1st value)"
+                   (object=? commit (car revspec)))
+      (test-assert "revparse with sha1 range (2nd value)"
+                   (object=? (commit-parent commit) (cadr revspec))))))
+
 (test-group "generic object lookup"
   (test "repository-ref on nonexistent sha" #f (repository-ref repo sha1))
   (test "repository-ref on nonexistent oid" #f (repository-ref repo (string->oid sha1)))
   (test-assert "repositroy-ref with tag" (tag? (repository-ref repo tag)))
   (test-assert "repository-ref with tree" (tree? (repository-ref repo (commit-tree commit)))))
 
+(test-group "clone"
+  (test-assert "clone" (clone (repository-path repo) cloned-repo-path))
+  (test-assert "repository-open after clone" (repository-open cloned-repo-path)))
+
+(set! cloned-repo (repository-open cloned-repo-path))
+
+(test-group "remote"
+  (test-assert "remote" (remote cloned-repo "origin"))
+  (test "remotes" 1 (length (remotes cloned-repo)))
+  (let ((r (remote cloned-repo "origin")))
+    (test-assert "remote?" (remote? r))
+    (test "remote-name" "origin" (remote-name r))
+    (test "remote-url" (repository-path repo) (remote-url r))
+    (test "remote-pushurl" #f (remote-pushurl r))
+    (test-assert "remote-url-set!" (remote-url-set! r (repository-working-directory repo)))
+    (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-connected? (connected)" (remote-connected? r))
+    (test-assert "remote-disconnect" (remote-disconnect r))
+    (test-assert "remote-connected? (disconnected)" (not (remote-connected? r)))))
+
+(set! remote (remote cloned-repo "origin"))
+
+(test-group "refspec"
+  (test "remote-refspecs" 1 (length (remote-refspecs remote)))
+  (let ((r (car (remote-refspecs remote))))
+    (test-assert "refspec?" (refspec? r))
+    (test "refspec-direction" 'fetch (refspec-direction r))
+    (test "refspec-source" "refs/heads/*" (refspec-source r))
+    (test "refspec-destination" "refs/remotes/origin/*" (refspec-destination r))))
+
 (test-end "git")
 (delete-directory repo-path 'recursively)
+(delete-directory cloned-repo-path 'recursively)
 (test-exit)