From 5247b7f448f20b274b6d53dffeb68ec2c353d04b Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Sun, 9 Aug 2026 23:57:49 +0200 Subject: [PATCH 01/21] Problem with _git_iod solved. Problem with empty error codes solved. --- libgit2/include/diff.rkt | 31 +++++++++++--------- libgit2/private/base/errors.rkt | 25 +++++++++++++++- libgit2/test/test-diff.rkt | 52 +++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 libgit2/test/test-diff.rkt diff --git a/libgit2/include/diff.rkt b/libgit2/include/diff.rkt index f435eb2..ac92b0c 100644 --- a/libgit2/include/diff.rkt +++ b/libgit2/include/diff.rkt @@ -12,7 +12,9 @@ _git_blob/null _git_commit _git_index - _git_tree) + _git_index/null + _git_tree + _git_tree/null) "../private/buffer.rkt" "../private/base.rkt") @@ -52,10 +54,11 @@ [GIT_DIFF_SHOW_BINARY = #x40000000]) (define-bitmask _git_diff_flag_t - [GIT_DIFF_FLAG_BINARY = #x0000] - [GIT_DIFF_FLAG_NOT_BINARY = #x0001] - [GIT_DIFF_FLAG_VALID_ID = #x0002] - [GIT_DIFF_FLAG_EXISTS = #x0004]) + [GIT_DIFF_FLAG_BINARY = #x0001] + [GIT_DIFF_FLAG_NOT_BINARY = #x0002] + [GIT_DIFF_FLAG_VALID_ID = #x0004] + [GIT_DIFF_FLAG_EXISTS = #x0008] + [GIT_DIFF_FLAG_VALID_SIZE = #x0010]) (define-enum _git_delta_t GIT_DELTA_UNMODIFIED @@ -71,7 +74,7 @@ GIT_DELTA_CONFLICTED) (define-cstruct _git_diff_file - ([id _git_oid-pointer] + ([id _git_oid] [path _string] [size _git_off_t] [flags _uint32] @@ -92,7 +95,7 @@ (_fun _git_diff _string _string _bytes -> _int)) (define-cstruct _git_diff_opts - ([version _int] + ([version _uint] [flags _uint32] [ignore_submodules _git_submodule_ignore_t] [pathspec _git_strarray] @@ -250,7 +253,7 @@ (_fun _git_diff_find_options-pointer _uint -> _int)) (define-libgit2/check git_diff_find_similar - (_fun _git_diff _git_diff_opts-pointer/null -> _int)) + (_fun _git_diff _git_diff_find_options-pointer/null -> _int)) (define-libgit2/check git_diff_foreach (_fun _git_diff _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _bytes -> _int)) @@ -271,11 +274,11 @@ git_diff_free) (define-libgit2/alloc git_diff_index_to_workdir - (_fun _git_diff _git_repository _git_index _git_diff_opts-pointer -> _int) + (_fun _git_diff _git_repository _git_index/null _git_diff_opts-pointer/null -> _int) git_diff_free) (define-libgit2 git_diff_options_init - (_fun _git_diff_opts _uint -> _int)) + (_fun _git_diff_opts-pointer _uint -> _int)) (define-libgit2 git_diff_is_sorted_icase (_fun _git_diff -> _bool)) @@ -311,17 +314,17 @@ (_fun (_git_buf/bytes-or-null) _git_diff _git_diff_format_t -> _int)) (define-libgit2/alloc git_diff_tree_to_index - (_fun _git_diff _git_repository _git_tree _git_index _git_diff_opts-pointer -> _int) + (_fun _git_diff _git_repository _git_tree/null _git_index/null _git_diff_opts-pointer/null -> _int) git_diff_free) (define-libgit2/alloc git_diff_tree_to_tree - (_fun _git_diff _git_repository _git_tree _git_tree _git_diff_opts-pointer -> _int) + (_fun _git_diff _git_repository _git_tree/null _git_tree/null _git_diff_opts-pointer/null -> _int) git_diff_free) (define-libgit2/alloc git_diff_tree_to_workdir - (_fun _git_diff _git_repository _git_tree _git_diff_opts-pointer -> _int) + (_fun _git_diff _git_repository _git_tree/null _git_diff_opts-pointer/null -> _int) git_diff_free) (define-libgit2/alloc git_diff_tree_to_workdir_with_index - (_fun _git_diff _git_repository _git_tree _git_diff_opts-pointer -> _int) + (_fun _git_diff _git_repository _git_tree/null _git_diff_opts-pointer/null -> _int) git_diff_free) diff --git a/libgit2/private/base/errors.rkt b/libgit2/private/base/errors.rkt index ab20b97..7ec148a 100644 --- a/libgit2/private/base/errors.rkt +++ b/libgit2/private/base/errors.rkt @@ -18,9 +18,10 @@ code (cast code _fixint _git_error_code))]) (define err/null (git_error_last)) - (git_error_clear) (define e-klass (and err/null (git_error-klass err/null))) (define e-msg (and err/null (git_error-message err/null))) + ;; Copy the thread-local error details before clearing them in libgit2. + (git_error_clear) (define message (format "~a: ~a\n error code: ~e\n error class: ~e" who @@ -153,6 +154,28 @@ but it's worth investigating further. (_fun -> _void)) |# +(module+ test + (require rackunit) + + (define-libgit2 git_error_set_str/test + (_fun _git_error_t _string -> _int) + #:c-id git_error_set_str) + + (test-case + "foreign error details are copied before clearing" + (check-equal? (git_error_set_str/test 'GIT_ERROR_INVALID + "ffi regression test") + 0) + (check-exn + (lambda (e) + (and (exn:fail:libgit2:foreign? e) + (regexp-match? #rx"ffi regression test" (exn-message e)) + (equal? (exn:fail:libgit2:foreign-error e) + '(GIT_ERROR_INVALID . "ffi regression test")))) + (lambda () + (raise-libgit2-foreign-error 'git_error_test 'GIT_ERROR))) + (check-false (git_error_last)))) + (define ((check-git_error_code/symbol who #:handle [handle-syms null]) code) (cond [(memq code handle-syms) diff --git a/libgit2/test/test-diff.rkt b/libgit2/test/test-diff.rkt new file mode 100644 index 0000000..7157179 --- /dev/null +++ b/libgit2/test/test-diff.rkt @@ -0,0 +1,52 @@ +#lang racket/base + +(require "../main.rkt" + (submod "../include/repository.rkt" free) + ffi/unsafe + racket/file + rackunit) + +(module+ test + (test-case + "git diff compares the index with the working directory" + (define repo-dir (make-temporary-file "rkttmp-libgit2-diff~a" 'directory)) + (dynamic-wind + void + (lambda () + (define tracked-file (build-path repo-dir "tracked.txt")) + (define repo (git_repository_init repo-dir)) + (define index (git_repository_index repo)) + + (call-with-output-file tracked-file + #:exists 'truncate + (lambda (out) (display "before\n" out))) + (git_index_add_bypath index "tracked.txt") + (git_index_write index) + + (call-with-output-file tracked-file + #:exists 'truncate + (lambda (out) (display "after, with a different size\n" out))) + + (define diff (git_diff_index_to_workdir repo index #f)) + (check-equal? (git_diff_num_deltas diff) 1) + + (define delta (git_diff_get_delta diff 0)) + (check-equal? (git_diff_delta-status delta) 'GIT_DELTA_MODIFIED) + (check-equal? (git_diff_file-path (git_diff_delta-old_file delta)) + "tracked.txt") + (check-equal? (git_diff_file-path (git_diff_delta-new_file delta)) + "tracked.txt") + + (define opts + (cast (malloc _git_diff_opts 'raw) _pointer _git_diff_opts-pointer)) + (check-not-exn + (lambda () (git_diff_options_init opts GIT_DIFF_OPTS_VERSION))) + (check-equal? (git_diff_opts-version opts) GIT_DIFF_OPTS_VERSION) + (check-equal? (git_diff_opts-context_lines opts) 3) + + (free opts) + (git_diff_free diff) + (git_index_free index) + (git_repository_free repo)) + (lambda () + (delete-directory/files repo-dir #:must-exist? #f))))) From d190a621b5fb764b462b0be3060de6da7f80ad7b Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 09:23:44 +0200 Subject: [PATCH 02/21] Fixed cstructures. git_oid was a real 20 bytes struct, not a pointer to it. --- libgit2/include/blame.rkt | 8 ++++---- libgit2/include/net.rkt | 2 +- libgit2/include/odb.rkt | 2 +- libgit2/include/remote.rkt | 4 ++-- libgit2/include/tree.rkt | 2 +- libgit2/test/test-abi-layout.rkt | 19 +++++++++++++++++++ 6 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 libgit2/test/test-abi-layout.rkt diff --git a/libgit2/include/blame.rkt b/libgit2/include/blame.rkt index b042159..541bba9 100644 --- a/libgit2/include/blame.rkt +++ b/libgit2/include/blame.rkt @@ -24,8 +24,8 @@ ([version _uint] [flags _uint32] [min_match_chars _uint16] - [newest_commit _git_oid-pointer] - [oldest_commit _git_oid-pointer] + [newest_commit _git_oid] + [oldest_commit _git_oid] [min_line _size] [max_line _size])) @@ -33,10 +33,10 @@ (define-cstruct _git_blame_hunk ([lines_in_hunk _size] - [final_commit_id _git_oid-pointer] + [final_commit_id _git_oid] [final_start_line_number _size] [final_signature _git_signature-pointer] - [orig_commit_id _git_oid-pointer] + [orig_commit_id _git_oid] [orig_path _string] [orig_sart_line_number _size] [orig_signature _git_signature-pointer] diff --git a/libgit2/include/net.rkt b/libgit2/include/net.rkt index df97edc..0dcab50 100644 --- a/libgit2/include/net.rkt +++ b/libgit2/include/net.rkt @@ -14,7 +14,7 @@ (define-cstruct _git_remote_head ([local _int] - [oid _git_oid-pointer] + [oid _git_oid] [name _string] [symref_target _string])) diff --git a/libgit2/include/odb.rkt b/libgit2/include/odb.rkt index 99ef9ab..b785aa7 100644 --- a/libgit2/include/odb.rkt +++ b/libgit2/include/odb.rkt @@ -22,7 +22,7 @@ (_fun _git_oid-pointer _bytes -> _int)) (define-cstruct _git_odb_expand_id - ([id _git_oid-pointer] + ([id _git_oid] [length _ushort] [type _git_object_t])) (define _odb_expand_id _git_odb_expand_id-pointer) diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index a02ef96..f78b902 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -36,8 +36,8 @@ (define-cstruct _git_push_update ([src_name _string] [dst_name _string] - [src _git_oid-pointer] - [dst _git_oid-pointer])) + [src _git_oid] + [dst _git_oid])) (define _push_update _git_push_update-pointer) (define _git_push_negotiation diff --git a/libgit2/include/tree.rkt b/libgit2/include/tree.rkt index 2c158de..b11d055 100644 --- a/libgit2/include/tree.rkt +++ b/libgit2/include/tree.rkt @@ -34,7 +34,7 @@ (define-cstruct _git_tree_update ([action _git_tree_update_t] - [id _git_oid-pointer] + [id _git_oid] [filemode _git_filemode_t] [path _string])) diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt new file mode 100644 index 0000000..4080608 --- /dev/null +++ b/libgit2/test/test-abi-layout.rkt @@ -0,0 +1,19 @@ +#lang racket + +(require rackunit + ffi/unsafe + (submod "../include/oid.rkt" private) + "../include/blame.rkt" + "../include/odb.rkt" + "../include/remote.rkt" + "../include/tree.rkt") + +;; ABI regression checks against libgit2 1.4.2 on 64-bit platforms. +(when (= (ctype-sizeof _pointer) 8) + (test-case "embedded git_oid fields are stored by value" + (check-equal? (ctype-sizeof _git_oid) 20) + (check-equal? (ctype-sizeof _git_blame_opts) 72) + (check-equal? (ctype-sizeof _git_blame_hunk) 104) + (check-equal? (ctype-sizeof _git_odb_expand_id) 28) + (check-equal? (ctype-sizeof _git_push_update) 56) + (check-equal? (ctype-sizeof _git_tree_update) 40))) From 6c16dab68f7cbb975ce8d64ae6be314fd7ddf8de Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 09:26:17 +0200 Subject: [PATCH 03/21] Ommisions in cstructs fixed. --- libgit2/include/merge.rkt | 3 ++- libgit2/include/net.rkt | 1 + libgit2/include/rebase.rkt | 5 ++++- libgit2/include/remote.rkt | 18 +++++++++++++++++- libgit2/include/status.rkt | 7 +++++-- libgit2/include/transport.rkt | 20 ++++++++++++++++++-- libgit2/test/test-abi-layout.rkt | 21 +++++++++++++++++++++ 7 files changed, 68 insertions(+), 7 deletions(-) diff --git a/libgit2/include/merge.rkt b/libgit2/include/merge.rkt index bdd6d41..a137487 100644 --- a/libgit2/include/merge.rkt +++ b/libgit2/include/merge.rkt @@ -67,7 +67,8 @@ [our_label _string] [their_label _string] [favor _git_merge_file_favor_t] - [flags _git_merge_file_flag_t])) + [flags _git_merge_file_flag_t] + [marker_size _ushort])) (define GIT_MERGE_FILE_OPTS_VERSION 1) diff --git a/libgit2/include/net.rkt b/libgit2/include/net.rkt index 0dcab50..8d1ec38 100644 --- a/libgit2/include/net.rkt +++ b/libgit2/include/net.rkt @@ -15,6 +15,7 @@ (define-cstruct _git_remote_head ([local _int] [oid _git_oid] + [loid _git_oid] [name _string] [symref_target _string])) diff --git a/libgit2/include/rebase.rkt b/libgit2/include/rebase.rkt index 1775d51..e3cca35 100644 --- a/libgit2/include/rebase.rkt +++ b/libgit2/include/rebase.rkt @@ -25,7 +25,10 @@ [inmemory _int] [rewrite_notes_ref _string] [merge_options _git_merge_opts] - [checkout_options _git_checkout_opts])) + [checkout_options _git_checkout_opts] + [commit_create_cb _fpointer] + [signing_cb _fpointer] + [payload _pointer])) (define GIT_REBASE_OPTS_VERSION 1) diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index f78b902..ac8b4c4 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -43,6 +43,13 @@ (define _git_push_negotiation (_fun (_cpointer _push_update) _size _bytes -> _int)) +(define _git_remote_ready_cb + (_fun _git_remote _git_direction _bytes -> _int)) + +(define _git_url_resolve_cb + (_fun _pointer _string _git_direction _bytes -> _int)) + + (define-cstruct _git_remote_callbacks ([version _uint] [sideband_progress _git_transport_message_cb] @@ -56,7 +63,9 @@ [push_update_reference (_fun _string _string _bytes -> _int)] [push_negotiation _git_push_negotiation] [transport _git_transport_cb] - [payload _bytes])) + [remote_ready _git_remote_ready_cb] + [payload _bytes] + [resolve_url _git_url_resolve_cb])) (define GIT_REMOTE_CB_VERSION 1) @@ -65,6 +74,11 @@ GIT_FETCH_PRUNE_PRUNE GIT_FETCH_PRUNE_NO_PRUNE))) +(define _git_remote_redirect_t + (_bitmask '(GIT_REMOTE_REDIRECT_NONE = #x1 + GIT_REMOTE_REDIRECT_INITIAL = #x2 + GIT_REMOTE_REDIRECT_ALL = #x4))) + (define _git_remote_autotag_option_t (_enum '(GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED GIT_REMOTE_DOWNLOAD_TAGS_AUTO @@ -80,6 +94,7 @@ [update_fetchhead _int] [download_tags _git_remote_autotag_option_t] [proxy_opts _git_proxy_options] + [follow_redirects _git_remote_redirect_t] [custom_headers _git_strarray])) (define GIT_FETCH_OPTS_VERSION 1) @@ -91,6 +106,7 @@ [pb_parallelism _uint] [callbacks _git_remote_callbacks] [proxy_opts _git_proxy_options] + [follow_redirects _git_remote_redirect_t] [custom_headers _git_strarray])) (define GIT_PUSH_OPTS_VERSION 1) diff --git a/libgit2/include/status.rkt b/libgit2/include/status.rkt index 1965fe6..7be4abd 100644 --- a/libgit2/include/status.rkt +++ b/libgit2/include/status.rkt @@ -5,7 +5,8 @@ "strarray.rkt" (only-in "types.rkt" _git_repository - _git_status_list) + _git_status_list + _git_tree) "../private/base.rkt") (provide (all-defined-out)) @@ -65,7 +66,9 @@ ([version _uint] [show _git_status_show_t] [flags _uint] - [pathspec _git_strarray])) + [pathspec _git_strarray] + [baseline (_or-null _git_tree)] + [rename_threshold _uint16])) (define GIT_STATUS_OPTS_VERSION 1) diff --git a/libgit2/include/transport.rkt b/libgit2/include/transport.rkt index 1aa3fcc..de3f388 100644 --- a/libgit2/include/transport.rkt +++ b/libgit2/include/transport.rkt @@ -16,13 +16,29 @@ (define _git_cert_ssh_t (_bitmask '(GIT_CERT_SSH_MD5 = #x0001 - GIT_CERT_SSH_SHA1 = #x0002))) + GIT_CERT_SSH_SHA1 = #x0002 + GIT_CERT_SSH_SHA256 = #x0004 + GIT_CERT_SSH_RAW = #x0008))) + + +(define-enum _git_cert_ssh_raw_type_t + GIT_CERT_SSH_RAW_TYPE_UNKNOWN + GIT_CERT_SSH_RAW_TYPE_RSA + GIT_CERT_SSH_RAW_TYPE_DSS + GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 + GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 + GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 + GIT_CERT_SSH_RAW_TYPE_KEY_ED25519) (define-cstruct _git_cert_hostkey ([parent _git_cert] [type _git_cert_ssh_t] [hash_md5 (_array _uint8 16)] - [hash_sha1 (_array _uint8 20)])) + [hash_sha1 (_array _uint8 20)] + [hash_sha256 (_array _uint8 32)] + [raw_type _git_cert_ssh_raw_type_t] + [hostkey _pointer] + [hostkey_len _size])) (define-cstruct _git_cert_x509 ([parent _git_cert] diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt index 4080608..e9a4cda 100644 --- a/libgit2/test/test-abi-layout.rkt +++ b/libgit2/test/test-abi-layout.rkt @@ -17,3 +17,24 @@ (check-equal? (ctype-sizeof _git_odb_expand_id) 28) (check-equal? (ctype-sizeof _git_push_update) 56) (check-equal? (ctype-sizeof _git_tree_update) 40))) + +(require (prefix-in net: "../include/net.rkt") + (prefix-in transport: "../include/transport.rkt") + (prefix-in status: "../include/status.rkt") + (prefix-in rebase: "../include/rebase.rkt") + (prefix-in merge: "../include/merge.rkt") + (prefix-in clone: "../include/clone.rkt") + (prefix-in submodule: "../include/submodule.rkt")) + +(when (= (ctype-sizeof _pointer) 8) + (test-case "libgit2 1.4.2 option and network struct fields" + (check-equal? (ctype-sizeof net:_git_remote_head) 64) + (check-equal? (ctype-sizeof transport:_git_cert_hostkey) 96) + (check-equal? (ctype-sizeof status:_git_status_opts) 48) + (check-equal? (ctype-sizeof _git_remote_callbacks) 120) + (check-equal? (ctype-sizeof _git_fetch_opts) 208) + (check-equal? (ctype-sizeof _git_push_opts) 192) + (check-equal? (ctype-sizeof rebase:_git_rebase_opts) 240) + (check-equal? (ctype-sizeof merge:_git_merge_file_opts) 48) + (check-equal? (ctype-sizeof clone:_git_clone_opts) 408) + (check-equal? (ctype-sizeof submodule:_git_submodule_update_opts) 368))) From b6f176af84eb6732fc93d3dc2e05dba24aa1501f Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 09:27:41 +0200 Subject: [PATCH 04/21] c structures ommissions fixed. --- libgit2/include/config.rkt | 1 + libgit2/include/types.rkt | 3 ++- libgit2/test/test-abi-layout.rkt | 12 ++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libgit2/include/config.rkt b/libgit2/include/config.rkt index 5210a19..e02dcd6 100644 --- a/libgit2/include/config.rkt +++ b/libgit2/include/config.rkt @@ -24,6 +24,7 @@ (define-cstruct _git_config_entry ([name _string] [value _string] + [include_depth _uint] [level _git_config_level_t] [free (_fun _git_config_entry-pointer -> _void)] [payload _bytes])) diff --git a/libgit2/include/types.rkt b/libgit2/include/types.rkt index 5316c98..dff167a 100644 --- a/libgit2/include/types.rkt +++ b/libgit2/include/types.rkt @@ -117,7 +117,8 @@ (define-cstruct _git_time ([time _git_time_t] - [offset _int])) + [offset _int] + [sign _int8])) (define-cstruct _git_signature ([name _string] diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt index e9a4cda..dbb5d62 100644 --- a/libgit2/test/test-abi-layout.rkt +++ b/libgit2/test/test-abi-layout.rkt @@ -38,3 +38,15 @@ (check-equal? (ctype-sizeof merge:_git_merge_file_opts) 48) (check-equal? (ctype-sizeof clone:_git_clone_opts) 408) (check-equal? (ctype-sizeof submodule:_git_submodule_update_opts) 368))) + +(require (prefix-in config: "../include/config.rkt") + (prefix-in types: "../include/types.rkt")) + +(when (= (ctype-sizeof _pointer) 8) + (test-case "same-size structs retain all libgit2 1.4.2 fields" + ;; These structs had the right total size before, despite missing a field. + ;; Constructor arity protects the actual field set as well as sizeof. + (check-equal? (ctype-sizeof config:_git_config_entry) 40) + (check-true (procedure-arity-includes? config:make-git_config_entry 6)) + (check-equal? (ctype-sizeof types:_git_time) 16) + (check-true (procedure-arity-includes? types:make-git_time 3)))) From cb20d39f708e9cf57fb15a3ae51ab9825ae08f69 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 09:35:23 +0200 Subject: [PATCH 05/21] git attributes set. --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..31632e1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto +*.rkt text eol=lf +*.scrbl text eol=lf \ No newline at end of file From df8c940ed3dd7d0b533cecee9a9f595a501fc527 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 10:00:38 +0200 Subject: [PATCH 06/21] Fixed problem in function definition for remote. --- libgit2/include/remote.rkt | 14 ++++++------ libgit2/test/test-remote-signatures.rkt | 29 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 libgit2/test/test-remote-signatures.rkt diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index ac8b4c4..0b26cff 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -117,10 +117,10 @@ (_fun _git_remote -> _void)) (define-libgit2/check git_remote_add_fetch - (_fun _git_repository _git_remote _string -> _int)) + (_fun _git_repository _string _string -> _int)) (define-libgit2/check git_remote_add_push - (_fun _git_repository _git_remote _string -> _int)) + (_fun _git_repository _string _string -> _int)) (define-libgit2 git_remote_autotag (_fun _git_remote -> _git_remote_autotag_option_t)) @@ -209,10 +209,10 @@ git_remote_free) (define-libgit2 git_remote_ls - (_fun [out : (_ptr o _git_remote_head)] + (_fun [out : (_ptr o _pointer)] [size : (_ptr o _size)] - _git_error_code - -> [v : _int] + _git_remote + -> (_git_error_code/check) -> (values out size))) (define-libgit2 git_remote_name @@ -251,10 +251,10 @@ (_fun _git_repository _string _git_remote_autotag_option_t -> _int)) (define-libgit2/check git_remote_set_pushurl - (_fun _git_repository _git_remote _string -> _int)) + (_fun _git_repository _string _string -> _int)) (define-libgit2/check git_remote_set_url - (_fun _git_repository _git_remote _string -> _int)) + (_fun _git_repository _string _string -> _int)) (define-libgit2 git_remote_stats (_fun _git_remote -> _git_transfer_progress-pointer)) diff --git a/libgit2/test/test-remote-signatures.rkt b/libgit2/test/test-remote-signatures.rkt new file mode 100644 index 0000000..92a6251 --- /dev/null +++ b/libgit2/test/test-remote-signatures.rkt @@ -0,0 +1,29 @@ +#lang racket/base + +(require rackunit + racket/file + libgit2) + +(module+ test + (define tmp (make-temporary-file "libgit2-remote-test-~a" 'directory)) + (dynamic-wind + void + (lambda () + (define repo (git_repository_init tmp 0)) + (git_remote_create repo "origin" "https://example.invalid/repo.git") + + ;; These functions take a remote NAME, not a git_remote pointer. + (check-not-exn + (lambda () + (git_remote_add_fetch repo "origin" "+refs/heads/*:refs/remotes/origin/*"))) + (check-not-exn + (lambda () + (git_remote_add_push repo "origin" "refs/heads/main:refs/heads/main"))) + (check-not-exn + (lambda () + (git_remote_set_url repo "origin" "https://example.invalid/other.git"))) + (check-not-exn + (lambda () + (git_remote_set_pushurl repo "origin" "https://example.invalid/push.git")))) + (lambda () + (delete-directory/files tmp)))) From a8e3c05cbf20fd3b4528a1a75cd88bede6dc5e6e Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 10:01:54 +0200 Subject: [PATCH 07/21] Fixed problems with function parameter lists. --- libgit2/include/config.rkt | 5 +++-- libgit2/include/merge.rkt | 2 +- libgit2/include/submodule.rkt | 2 +- libgit2/test/test-function-arities.rkt | 15 +++++++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 libgit2/test/test-function-arities.rkt diff --git a/libgit2/include/config.rkt b/libgit2/include/config.rkt index e02dcd6..1cb50f1 100644 --- a/libgit2/include/config.rkt +++ b/libgit2/include/config.rkt @@ -4,6 +4,7 @@ (only-in "types.rkt" _git_config _git_config_backend + _git_repository _git_transaction) "../private/buffer.rkt" "../private/base.rkt") @@ -49,10 +50,10 @@ ; Functions (define-libgit2/check git_config_add_backend - (_fun _git_config _git_config_backend _git_config_level_t _bool -> _int)) + (_fun _git_config _git_config_backend _git_config_level_t (_or-null _git_repository) _bool -> _int)) (define-libgit2/check git_config_add_file_ondisk - (_fun _git_config _string _git_config_level_t _bool -> _int)) + (_fun _git_config _string _git_config_level_t (_or-null _git_repository) _bool -> _int)) (define-libgit2/check git_config_backend_foreach_match (_fun _git_config_backend _string _git_config_foreach_cb _bytes -> _int)) diff --git a/libgit2/include/merge.rkt b/libgit2/include/merge.rkt index a137487..b47c31c 100644 --- a/libgit2/include/merge.rkt +++ b/libgit2/include/merge.rkt @@ -128,7 +128,7 @@ (_fun _git_oidarray-pointer _git_repository _size (_cpointer _git_oid-pointer) -> _int)) (define-libgit2/alloc git_merge_commits - (_fun _git_index _git_repository _git_commit _git_commit _git_commit _git_merge_opts-pointer -> _int) + (_fun _git_index _git_repository _git_commit _git_commit _git_merge_opts-pointer -> _int) git_index_free) (define-libgit2/check git_merge_file diff --git a/libgit2/include/submodule.rkt b/libgit2/include/submodule.rkt index e1730ac..bbaf412 100644 --- a/libgit2/include/submodule.rkt +++ b/libgit2/include/submodule.rkt @@ -56,7 +56,7 @@ (_fun _git_submodule _git_repository _string _string _int -> _int)) (define-libgit2/check git_submodule_add_to_index - (_fun _git_submodule -> _int)) + (_fun _git_submodule _int -> _int)) (define-libgit2 git_submodule_branch (_fun _git_submodule -> _string)) diff --git a/libgit2/test/test-function-arities.rkt b/libgit2/test/test-function-arities.rkt new file mode 100644 index 0000000..383d185 --- /dev/null +++ b/libgit2/test/test-function-arities.rkt @@ -0,0 +1,15 @@ +#lang racket/base + +(require rackunit + libgit2) + +(define (check-exact-arity proc n) + (check-true (procedure-arity-includes? proc n)) + (check-false (procedure-arity-includes? proc (add1 n)))) + +(module+ test + ;; Public Racket arities exclude output pointers handled by alloc wrappers. + (check-exact-arity git_config_add_backend 5) + (check-exact-arity git_config_add_file_ondisk 5) + (check-exact-arity git_merge_commits 4) + (check-exact-arity git_submodule_add_to_index 2)) From 3726ef4a54a5834443bcceda9e60fa70ef3cdda6 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 10:02:43 +0200 Subject: [PATCH 08/21] Fixed indexer problems. --- libgit2/include/indexer.rkt | 13 ++++++++++++- libgit2/test/test-function-arities.rkt | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libgit2/include/indexer.rkt b/libgit2/include/indexer.rkt index 534ad0d..50ac365 100644 --- a/libgit2/include/indexer.rkt +++ b/libgit2/include/indexer.rkt @@ -14,6 +14,14 @@ (define _indexer (_cpointer 'git_indexer)) +(define-cstruct _git_indexer_options + ([version _uint] + [progress_cb _git_transfer_progress_cb] + [progress_cb_payload _bytes] + [verify _ubyte])) + +(define GIT_INDEXER_OPTIONS_VERSION 1) + ; Functions (define-libgit2/check git_indexer_append @@ -26,8 +34,11 @@ (_fun _indexer -> _void)) (define-libgit2/alloc git_indexer_new - (_fun _indexer _string _uint _git_odb _git_transfer_progress_cb _bytes -> _int) + (_fun _indexer _string _uint _git_odb (_or-null _git_indexer_options-pointer) -> _int) git_indexer_free) +(define-libgit2/check git_indexer_options_init + (_fun _git_indexer_options-pointer _uint -> _int)) + (define-libgit2 git_indexer_name (_fun _indexer -> _string/utf-8)) diff --git a/libgit2/test/test-function-arities.rkt b/libgit2/test/test-function-arities.rkt index 383d185..2b77232 100644 --- a/libgit2/test/test-function-arities.rkt +++ b/libgit2/test/test-function-arities.rkt @@ -1,6 +1,7 @@ #lang racket/base (require rackunit + ffi/unsafe libgit2) (define (check-exact-arity proc n) @@ -13,3 +14,8 @@ (check-exact-arity git_config_add_file_ondisk 5) (check-exact-arity git_merge_commits 4) (check-exact-arity git_submodule_add_to_index 2)) + +(module+ test + (check-equal? (ctype-sizeof _git_indexer_options) 32) + (check-exact-arity git_indexer_new 4) + (check-exact-arity git_indexer_options_init 2)) From 33fdcab205d455ab9dc537b7483c875896c9632f Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 12:25:05 +0200 Subject: [PATCH 09/21] Reading of stream metadata fix. --- libgit2/include/odb.rkt | 13 +++++++-- libgit2/scribblings/odb.scrbl | 4 ++- libgit2/test/test-output-parameters.rkt | 38 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 libgit2/test/test-output-parameters.rkt diff --git a/libgit2/include/odb.rkt b/libgit2/include/odb.rkt index b785aa7..7d69384 100644 --- a/libgit2/include/odb.rkt +++ b/libgit2/include/odb.rkt @@ -1,6 +1,7 @@ #lang racket (require ffi/unsafe + ffi/unsafe/alloc (submod "oid.rkt" private) (only-in "odb_backend.rkt" _git_odb_stream-pointer @@ -91,8 +92,16 @@ (define-libgit2/alloc git_odb_open (_fun _git_odb _string -> _int)) -(define-libgit2/alloc git_odb_open_rstream - (_fun _git_odb_stream-pointer _git_odb _git_oid-pointer -> _int)) +(define-libgit2 git_odb_open_rstream + (_fun [stream : (_ptr o _git_odb_stream-pointer)] + [len : (_ptr o _size)] + [type : (_ptr o _git_object_t)] + _git_odb + _git_oid-pointer + -> [code : (_git_error_code/check)] + -> (values (((allocator git_odb_stream_free) values) stream) + len + type))) (define-libgit2/alloc git_odb_open_wstream (_fun _git_odb_stream-pointer _git_odb _git_off_t _git_object_t -> _int)) diff --git a/libgit2/scribblings/odb.scrbl b/libgit2/scribblings/odb.scrbl index f1d6fa4..af2dc64 100644 --- a/libgit2/scribblings/odb.scrbl +++ b/libgit2/scribblings/odb.scrbl @@ -244,13 +244,15 @@ @defproc[(git_odb_open_rstream [db odb?] [oid oid?]) - odb_stream?]{ + (values odb_stream? exact-nonnegative-integer? symbol?)]{ Open a stream to read an object from the ODB Note that most backends do not support streaming reads because they store their objects as compressed/delta'ed blobs. It's recommended to use git_odb_read instead, which is assured to work on all backends. + Returns three values: the read stream, the uncompressed object size, and the object type. + The returned stream will be of type GIT_STREAM_RDONLY and will have the following methods: - stream->read: read `n` bytes from the stream - stream->free: free the stream diff --git a/libgit2/test/test-output-parameters.rkt b/libgit2/test/test-output-parameters.rkt new file mode 100644 index 0000000..e337f62 --- /dev/null +++ b/libgit2/test/test-output-parameters.rkt @@ -0,0 +1,38 @@ +#lang racket/base + +(require ffi/unsafe + racket/file + rackunit + libgit2/include/odb + libgit2/include/repository + (submod libgit2/include/repository free) + libgit2/include/types + (submod libgit2/include/oid private)) + +(module+ test + ;; git_odb_open_rstream has three C output parameters. They are returned + ;; as three Racket values, so callers only supply the database and oid. + (check-true (procedure-arity-includes? git_odb_open_rstream 2)) + (check-false (procedure-arity-includes? git_odb_open_rstream 5)) + + (test-case "git_odb_open_rstream returns stream metadata" + (define tmp (make-temporary-file "libgit2-odb-test-~a" 'directory)) + (dynamic-wind + void + (lambda () + (define repo (git_repository_init tmp)) + (define odb (git_repository_odb repo)) + (define oid + (cast (malloc _git_oid 'raw) _pointer _git_oid-pointer)) + (define data #"hello odb") + (git_odb_write oid odb data (bytes-length data) 'GIT_OBJECT_BLOB) + (define-values (stream len type) + (git_odb_open_rstream odb oid)) + (check-true (cpointer? stream)) + (check-equal? len (bytes-length data)) + (check-equal? type 'GIT_OBJECT_BLOB) + (free oid) + (git_odb_free odb) + (git_repository_free repo)) + (lambda () + (delete-directory/files tmp #:must-exist? #f))))) From 9e11246fd47246b6fb28b8527af943e2ed88f4d2 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 12:26:19 +0200 Subject: [PATCH 10/21] Buffer patches. Outputs as byte strings (can also be null). --- libgit2/include/branch.rkt | 9 ++++++--- libgit2/include/commit.rkt | 16 ++++++++++++--- libgit2/include/config.rkt | 21 +++++++++++++------- libgit2/include/describe.rkt | 3 ++- libgit2/include/diff.rkt | 6 ++++-- libgit2/include/filter.rkt | 6 ++++-- libgit2/include/message.rkt | 3 ++- libgit2/include/object.rkt | 3 ++- libgit2/include/pack.rkt | 3 ++- libgit2/include/patch.rkt | 3 ++- libgit2/include/refspec.rkt | 6 ++++-- libgit2/include/remote.rkt | 3 ++- libgit2/include/repository.rkt | 5 +++-- libgit2/include/submodule.rkt | 3 ++- libgit2/scribblings/branch.scrbl | 12 ++++++++++++ libgit2/scribblings/commit.scrbl | 10 +++------- libgit2/scribblings/config.scrbl | 29 +++++++++++----------------- libgit2/scribblings/describe.scrbl | 3 +-- libgit2/scribblings/diff.scrbl | 6 ++---- libgit2/scribblings/filter.scrbl | 6 ++---- libgit2/scribblings/message.scrbl | 3 +-- libgit2/scribblings/object.scrbl | 3 +-- libgit2/scribblings/patch.scrbl | 3 +-- libgit2/scribblings/refspec.scrbl | 6 ++---- libgit2/scribblings/remote.scrbl | 3 +-- libgit2/scribblings/repository.scrbl | 5 ++--- libgit2/scribblings/submodule.scrbl | 3 +-- libgit2/test/test-buffer-results.rkt | 25 ++++++++++++++++++++++++ libgit2/test/test-diff.rkt | 5 +++++ 29 files changed, 132 insertions(+), 80 deletions(-) create mode 100644 libgit2/test/test-buffer-results.rkt diff --git a/libgit2/include/branch.rkt b/libgit2/include/branch.rkt index 99b445e..6a5cd8d 100644 --- a/libgit2/include/branch.rkt +++ b/libgit2/include/branch.rkt @@ -70,10 +70,13 @@ ; see https://github.com/libgit2/libgit2/blob/v0.25.1/include/git2/branch.h (define-libgit2/check git_branch_upstream_name - (_fun (_git_buf/bytes-or-null) _git_repository _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_repository _string -> _int + -> out)) (define-libgit2/check git_branch_upstream_remote - (_fun (_git_buf/bytes-or-null) _git_repository _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_repository _string -> _int + -> out)) (define-libgit2/check git_branch_remote_name - (_fun (_git_buf/bytes-or-null) _git_repository _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_repository _string -> _int + -> out)) diff --git a/libgit2/include/commit.rkt b/libgit2/include/commit.rkt index 2990bdd..0334d93 100644 --- a/libgit2/include/commit.rkt +++ b/libgit2/include/commit.rkt @@ -39,7 +39,11 @@ (_fun _git_oid-pointer _git_repository _string _git_signature-pointer _git_signature-pointer _string _string _git_tree _size (_cpointer (_cpointer _git_commit)) -> _int)) (define-libgit2/check git_commit_create_buffer - (_fun (_git_buf/bytes-or-null) _git_repository _git_signature-pointer _git_signature-pointer _string _string _git_tree _size (_cpointer (_cpointer _git_commit)) -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] + _git_repository _git_signature-pointer _git_signature-pointer + _string _string _git_tree _size (_cpointer (_cpointer _git_commit)) + -> _int + -> out)) (define (git_commit_create_v id repo update_ref author committer encoding message tree parent_count . parents) (define itypes (append (list _git_oid-pointer _git_repository _string _git_signature-pointer _git_signature-pointer _string _string _git_tree _size) @@ -57,10 +61,16 @@ git_commit_free) (define-libgit2/check git_commit_extract_signature - (_fun (_git_buf/bytes-or-null) (_git_buf/bytes-or-null) _git_repository _git_oid-pointer _string -> _int)) + (_fun [signature : (_git_buf/bytes-or-null)] + [signed-data : (_git_buf/bytes-or-null)] + _git_repository _git_oid-pointer _string + -> _int + -> (values signature signed-data))) (define-libgit2/check git_commit_header_field - (_fun (_git_buf/bytes-or-null) _git_commit _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_commit _string + -> _int + -> out)) (define-libgit2 git_commit_id (_fun _git_commit -> _git_oid-pointer)) diff --git a/libgit2/include/config.rkt b/libgit2/include/config.rkt index 1cb50f1..a0d961b 100644 --- a/libgit2/include/config.rkt +++ b/libgit2/include/config.rkt @@ -68,16 +68,20 @@ (_fun _config_entry -> _void)) (define-libgit2/check git_config_find_global - (_fun (_git_buf/bytes-or-null) -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] -> _int + -> out)) (define-libgit2/check git_config_find_programdata - (_fun (_git_buf/bytes-or-null) -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] -> _int + -> out)) (define-libgit2/check git_config_find_system - (_fun (_git_buf/bytes-or-null) -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] -> _int + -> out)) (define-libgit2/check git_config_find_xdg - (_fun (_git_buf/bytes-or-null) -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] -> _int + -> out)) (define-libgit2/check git_config_foreach (_fun _git_config _git_config_foreach_cb _bytes -> _int)) @@ -108,13 +112,15 @@ (_fun _git_config _string _string _git_config_foreach_cb _bytes -> _int)) (define-libgit2/check git_config_get_path - (_fun (_git_buf/bytes-or-null) _git_config _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_config _string -> _int + -> out)) (define-libgit2/alloc git_config_get_string (_fun _string _git_config _string -> _int)) (define-libgit2/check git_config_get_string_buf - (_fun (_git_buf/bytes-or-null) _git_config _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_config _string -> _int + -> out)) (define-libgit2/check git_config_init_backend (_fun _git_config_backend _uint -> _int)) @@ -174,7 +180,8 @@ (_fun _int64 _string -> _int)) (define-libgit2/check git_config_parse_path - (_fun (_git_buf/bytes-or-null) _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _string -> _int + -> out)) (define-libgit2/check git_config_set_bool (_fun _git_config _string _bool -> _int)) diff --git a/libgit2/include/describe.rkt b/libgit2/include/describe.rkt index 8c4cf40..57bf717 100644 --- a/libgit2/include/describe.rkt +++ b/libgit2/include/describe.rkt @@ -49,7 +49,8 @@ git_describe_result_free) (define-libgit2/check git_describe_format - (_fun (_git_buf/bytes-or-null) _describe_result _git_describe_format_opts-pointer/null -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _describe_result _git_describe_format_opts-pointer/null -> _int + -> out)) (define-libgit2/alloc git_describe_workdir (_fun _describe_result _git_repository _git_describe_opts-pointer/null -> _int) diff --git a/libgit2/include/diff.rkt b/libgit2/include/diff.rkt index ac92b0c..32af345 100644 --- a/libgit2/include/diff.rkt +++ b/libgit2/include/diff.rkt @@ -305,13 +305,15 @@ (_fun _diff_stats -> _size)) (define-libgit2/check git_diff_stats_to_buf - (_fun (_git_buf/bytes-or-null) _diff_stats _git_diff_stats_format_t _size -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _diff_stats _git_diff_stats_format_t _size -> _int + -> out)) (define-libgit2 git_diff_status_char (_fun _git_delta_t -> _uint8)) (define-libgit2/check git_diff_to_buf - (_fun (_git_buf/bytes-or-null) _git_diff _git_diff_format_t -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_diff _git_diff_format_t -> _int + -> out)) (define-libgit2/alloc git_diff_tree_to_index (_fun _git_diff _git_repository _git_tree/null _git_index/null _git_diff_opts-pointer/null -> _int) diff --git a/libgit2/include/filter.rkt b/libgit2/include/filter.rkt index f91f901..c975895 100644 --- a/libgit2/include/filter.rkt +++ b/libgit2/include/filter.rkt @@ -30,10 +30,12 @@ (_fun _filter_list -> _void)) (define-libgit2/check git_filter_list_apply_to_blob - (_fun (_git_buf/bytes-or-null) _filter_list _git_blob -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _filter_list _git_blob -> _int + -> out)) (define-libgit2/check git_filter_list_apply_to_file - (_fun (_git_buf/bytes-or-null) _filter_list _git_repository _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _filter_list _git_repository _string -> _int + -> out)) (define-libgit2 git_filter_list_contains (_fun _filter_list _string -> _bool)) diff --git a/libgit2/include/message.rkt b/libgit2/include/message.rkt index 0d923b2..3c4d660 100644 --- a/libgit2/include/message.rkt +++ b/libgit2/include/message.rkt @@ -7,4 +7,5 @@ (provide git_message_prettify) (define-libgit2/check git_message_prettify - (_fun (_git_buf/bytes-or-null) _string _int _int8 -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _string _int _int8 -> _int + -> out)) diff --git a/libgit2/include/object.rkt b/libgit2/include/object.rkt index 873020b..e808006 100644 --- a/libgit2/include/object.rkt +++ b/libgit2/include/object.rkt @@ -42,7 +42,8 @@ git_object_free) (define-libgit2/check git_object_short_id - (_fun (_git_buf/bytes-or-null) _git_object -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_object -> _int + -> out)) (define-libgit2 git_object_string2type (_fun _string -> _git_object_t)) diff --git a/libgit2/include/pack.rkt b/libgit2/include/pack.rkt index ad4ce0f..d8fdf25 100644 --- a/libgit2/include/pack.rkt +++ b/libgit2/include/pack.rkt @@ -71,4 +71,5 @@ (_fun _git_packbuilder -> _size)) (define-libgit2/check git_packbuilder_write_buf - (_fun (_git_buf/bytes-or-null) _git_packbuilder -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_packbuilder -> _int + -> out)) diff --git a/libgit2/include/patch.rkt b/libgit2/include/patch.rkt index d3beedc..6bd2e8b 100644 --- a/libgit2/include/patch.rkt +++ b/libgit2/include/patch.rkt @@ -65,4 +65,5 @@ (_fun _git_patch _int _int _int -> _size)) (define-libgit2/check git_patch_to_buf - (_fun (_git_buf/bytes-or-null) _git_patch -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_patch -> _int + -> out)) diff --git a/libgit2/include/refspec.rkt b/libgit2/include/refspec.rkt index b9b2d51..57ccffe 100644 --- a/libgit2/include/refspec.rkt +++ b/libgit2/include/refspec.rkt @@ -22,7 +22,8 @@ (_fun _git_refspec -> _bool)) (define-libgit2/check git_refspec_transform - (_fun (_git_buf/bytes-or-null) _git_refspec _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_refspec _string -> _int + -> out)) (define-libgit2 git_refspec_src (_fun _git_refspec -> _string)) @@ -34,5 +35,6 @@ (_fun _git_refspec -> _string)) (define-libgit2/check git_refspec_rtransform - (_fun (_git_buf/bytes-or-null) _git_refspec _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_refspec _string -> _int + -> out)) diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index 0b26cff..3516f02 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -149,7 +149,8 @@ git_remote_free) (define-libgit2/check git_remote_default_branch - (_fun (_git_buf/bytes-or-null) _git_remote -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_remote -> _int + -> out)) (define-libgit2/check git_remote_delete (_fun _git_repository _string -> _int)) diff --git a/libgit2/include/repository.rkt b/libgit2/include/repository.rkt index c4aa25e..1416556 100644 --- a/libgit2/include/repository.rkt +++ b/libgit2/include/repository.rkt @@ -272,8 +272,9 @@ -> (_git_error_code/check))) (define-libgit2 git_repository_message - (_fun (_git_buf/bytes-or-null) _git_repository - -> (_git_error_code/check))) + (_fun [out : (_git_buf/bytes-or-null)] _git_repository + -> (_git_error_code/check) + -> out)) (define-libgit2 git_repository_message_remove (_fun _git_repository diff --git a/libgit2/include/submodule.rkt b/libgit2/include/submodule.rkt index bbaf412..889d5b1 100644 --- a/libgit2/include/submodule.rkt +++ b/libgit2/include/submodule.rkt @@ -110,7 +110,8 @@ (_fun _git_repository _git_submodule _bool -> _int)) (define-libgit2/check git_submodule_resolve_url - (_fun (_git_buf/bytes-or-null) _git_repository _string -> _int)) + (_fun [out : (_git_buf/bytes-or-null)] _git_repository _string -> _int + -> out)) (define-libgit2/check git_submodule_set_branch (_fun _git_repository _string _string -> _int)) diff --git a/libgit2/scribblings/branch.scrbl b/libgit2/scribblings/branch.scrbl index 8faea0e..f103e58 100644 --- a/libgit2/scribblings/branch.scrbl +++ b/libgit2/scribblings/branch.scrbl @@ -124,3 +124,15 @@ reference?]{ Return the reference supporting the remote tracking branch, given a local branch reference. } + +@defproc[(git_branch_upstream_name [repo repository?] [refname string?]) (or/c bytes? #f)]{ + Return the upstream reference name for @racket[refname]. +} + +@defproc[(git_branch_upstream_remote [repo repository?] [refname string?]) (or/c bytes? #f)]{ + Return the configured upstream remote name for @racket[refname]. +} + +@defproc[(git_branch_remote_name [repo repository?] [refname string?]) (or/c bytes? #f)]{ + Return the remote name associated with a remote-tracking reference. +} diff --git a/libgit2/scribblings/commit.scrbl b/libgit2/scribblings/commit.scrbl index f67cc08..d92955f 100644 --- a/libgit2/scribblings/commit.scrbl +++ b/libgit2/scribblings/commit.scrbl @@ -70,7 +70,6 @@ } @defproc[(git_commit_create_buffer - [out buf?] [repo repository?] [author signature?] [committer signature?] @@ -79,7 +78,7 @@ [tree tree?] [parent_count exact-positive-integer?] [parents (or/c (_cpointer _commit) #f)]) - integer?]{ + (or/c bytes? #f)]{ Create a commit and write it into a buffer Create a commit as with git_commit_create() but instead of writing it to the objectdb, write the contents of the object into a buffer. @@ -165,12 +164,10 @@ } @defproc[(git_commit_extract_signature - [signature buf?] - [signed_data buf?] [repo repository?] [commit_id oid?] [field (or/c string? #f)]) - integer?]{ + (values (or/c bytes? #f) (or/c bytes? #f))]{ Extract the signature from a commit If the id is not for a commit, the error class will be GITERR_INVALID. If the commit does not have a signature, the error class will be GITERR_OBJECT. @@ -190,10 +187,9 @@ } @defproc[(git_commit_header_field - [out buf?] [commit commit?] [field string?]) - integer?]{ + (or/c bytes? #f)]{ Get an arbitrary header field } diff --git a/libgit2/scribblings/config.scrbl b/libgit2/scribblings/config.scrbl index 03af346..0437235 100644 --- a/libgit2/scribblings/config.scrbl +++ b/libgit2/scribblings/config.scrbl @@ -75,9 +75,8 @@ } -@defproc[(git_config_find_global - [out buf?]) - integer?]{ +@defproc[(git_config_find_global) + (or/c bytes? #f)]{ Locate the path to the global configuration file The user or global configuration file is usually located in $HOME/.gitconfig. @@ -88,27 +87,24 @@ } -@defproc[(git_config_find_programdata - [out buf?]) - integer?]{ +@defproc[(git_config_find_programdata) + (or/c bytes? #f)]{ Locate the path to the configuration file in ProgramData Look for the file in %PROGRAMDATA% used by portable git. } -@defproc[(git_config_find_system - [out buf?]) - integer?]{ +@defproc[(git_config_find_system) + (or/c bytes? #f)]{ Locate the path to the system configuration file If /etc/gitconfig doesn't exist, it will look for %PROGRAMFILES%. } -@defproc[(git_config_find_xdg - [out buf?]) - integer?]{ +@defproc[(git_config_find_xdg) + (or/c bytes? #f)]{ Locate the path to the global xdg compatible configuration file The xdg compatible configuration file is usually located in $HOME/.config/git/config. @@ -224,10 +220,9 @@ } @defproc[(git_config_get_path - [out buf?] [cfg config?] [name string?]) - integer?]{ + (or/c bytes? #f)]{ Get the value of a path config variable. A leading '~' will be expanded to the global search path (which defaults to the user's home directory but can be overridden via git_libgit2_opts(). @@ -250,10 +245,9 @@ } @defproc[(git_config_get_string_buf - [out buf?] [cfg config?] [name string?]) - integer?]{ + (or/c bytes? #f)]{ Get the value of a string config variable. The value of the config will be copied into the buffer. @@ -405,9 +399,8 @@ } @defproc[(git_config_parse_path - [out buf?] [value string?]) - integer?]{ + (or/c bytes? #f)]{ Parse a string value as a path. A leading '~' will be expanded to the global search path (which defaults to the user's home directory but can be overridden via git_libgit2_opts(). diff --git a/libgit2/scribblings/describe.scrbl b/libgit2/scribblings/describe.scrbl index 46e8d81..d1e5c2b 100644 --- a/libgit2/scribblings/describe.scrbl +++ b/libgit2/scribblings/describe.scrbl @@ -18,10 +18,9 @@ } @defproc[(git_describe_format - [out buf?] [result describe_result?] [opts (or/c git_describe_format_options? #f)]) - integer?]{ + (or/c bytes? #f)]{ Print the describe result to a buffer } diff --git a/libgit2/scribblings/diff.scrbl b/libgit2/scribblings/diff.scrbl index 6df51e1..377d0ef 100644 --- a/libgit2/scribblings/diff.scrbl +++ b/libgit2/scribblings/diff.scrbl @@ -305,11 +305,10 @@ } @defproc[(git_diff_stats_to_buf - [out buf?] [stats diff_stats?] [format git_diff_stats_format_t] [width integer?]) - integer?]{ + (or/c bytes? #f)]{ Print diff statistics to a git_buf. } @@ -324,10 +323,9 @@ } @defproc[(git_diff_to_buf - [out buf?] [diff diff?] [format git_diff_format_t]) - integer?]{ + (or/c bytes? #f)]{ Produce the complete formatted text output from a diff into a buffer. } diff --git a/libgit2/scribblings/filter.scrbl b/libgit2/scribblings/filter.scrbl index 9caf389..21057a3 100644 --- a/libgit2/scribblings/filter.scrbl +++ b/libgit2/scribblings/filter.scrbl @@ -8,10 +8,9 @@ @defproc[(git_filter_list_apply_to_blob - [out buf?] [filters filter_list?] [blob blob?]) - integer?]{ + (or/c bytes? #f)]{ Apply a filter list to the contents of a blob } @@ -30,11 +29,10 @@ } @defproc[(git_filter_list_apply_to_file - [out buf?] [filters filter_list?] [repo repository?] [path string?]) - integer?]{ + (or/c bytes? #f)]{ Apply a filter list to the contents of a file on disk 'path' is relative to wrokdir diff --git a/libgit2/scribblings/message.scrbl b/libgit2/scribblings/message.scrbl index 67c1398..bcc52eb 100644 --- a/libgit2/scribblings/message.scrbl +++ b/libgit2/scribblings/message.scrbl @@ -8,11 +8,10 @@ @defproc[(git_message_prettify - [out buf?] [message string?] [strip_comments boolean?] [comment_char char?]) - integer?]{ + (or/c bytes? #f)]{ Clean up message from excess whitespace and make sure that the last line ends with a ' '. diff --git a/libgit2/scribblings/object.scrbl b/libgit2/scribblings/object.scrbl index 74f80d9..3d5d02f 100644 --- a/libgit2/scribblings/object.scrbl +++ b/libgit2/scribblings/object.scrbl @@ -107,9 +107,8 @@ For all the core types, this would the equivalent of calling sizeof(git_commit) } @defproc[(git_object_short_id - [out buf?] [obj object?]) - integer?]{ + (or/c bytes? #f)]{ Get a short abbreviated OID string for the object This starts at the "core.abbrev" length (default 7 characters) and iteratively extends to a longer string if that length is ambiguous. The result will be unambiguous (at least until new objects are added to the repository). diff --git a/libgit2/scribblings/patch.scrbl b/libgit2/scribblings/patch.scrbl index f8fb477..3fad32e 100644 --- a/libgit2/scribblings/patch.scrbl +++ b/libgit2/scribblings/patch.scrbl @@ -155,8 +155,7 @@ } @defproc[(git_patch_to_buf - [out buf?] [patch patch?]) - integer?]{ + (or/c bytes? #f)]{ Get the content of a patch as a single diff text. } diff --git a/libgit2/scribblings/refspec.scrbl b/libgit2/scribblings/refspec.scrbl index 0648a5e..a58cf55 100644 --- a/libgit2/scribblings/refspec.scrbl +++ b/libgit2/scribblings/refspec.scrbl @@ -37,10 +37,9 @@ } @defproc[(git_refspec_rtransform - [out buf?] [spec refspec?] [name string?]) - integer?]{ + (or/c bytes? #f)]{ Transform a target reference to its source reference following the refspec's rules } @@ -68,9 +67,8 @@ } @defproc[(git_refspec_transform - [out buf?] [spec refspec?] [name string?]) - integer?]{ + (or/c bytes? #f)]{ Transform a reference to its target following the refspec's rules } diff --git a/libgit2/scribblings/remote.scrbl b/libgit2/scribblings/remote.scrbl index 37e712f..61626a4 100644 --- a/libgit2/scribblings/remote.scrbl +++ b/libgit2/scribblings/remote.scrbl @@ -88,9 +88,8 @@ } @defproc[(git_remote_default_branch - [out buf?] [remote remote?]) - integer?]{ + (or/c bytes? #f)]{ Retrieve the name of the remote's default branch The default branch of a repository is the branch which HEAD points to. If the remote does not support reporting this information directly, it performs the guess as git does; that is, if there are multiple branches which point to the same commit, the first one is chosen. If the master branch is a candidate, it wins. diff --git a/libgit2/scribblings/repository.scrbl b/libgit2/scribblings/repository.scrbl index 242278c..6fa139a 100644 --- a/libgit2/scribblings/repository.scrbl +++ b/libgit2/scribblings/repository.scrbl @@ -210,9 +210,8 @@ } @defproc[(git_repository_message - [out buf?] [repo repository?]) - integer?]{ + (or/c bytes? #f)]{ Retrieve git's prepared message Operations such as git revert/cherry-pick/merge with the -n option stop just short of creating a commit with the changes and save their prepared message in .git/MERGE_MSG so the next git-commit execution can present it to the user for them to amend if they wish. @@ -223,7 +222,7 @@ @defproc[(git_repository_message_remove [repo repository?]) - integer?]{ + (or/c bytes? #f)]{ Remove git's prepared message. Remove the message that git_repository_message retrieves. diff --git a/libgit2/scribblings/submodule.scrbl b/libgit2/scribblings/submodule.scrbl index bf57005..a0f26cc 100644 --- a/libgit2/scribblings/submodule.scrbl +++ b/libgit2/scribblings/submodule.scrbl @@ -191,10 +191,9 @@ } @defproc[(git_submodule_resolve_url - [out buf?] [repo repository?] [url string?]) - integer?]{ + (or/c bytes? #f)]{ Resolve a submodule url relative to the given repository. } diff --git a/libgit2/test/test-buffer-results.rkt b/libgit2/test/test-buffer-results.rkt new file mode 100644 index 0000000..fa6c54c --- /dev/null +++ b/libgit2/test/test-buffer-results.rkt @@ -0,0 +1,25 @@ +#lang racket/base + +(require rackunit + libgit2/include/commit + libgit2/include/config + libgit2/include/diff + libgit2/include/message) + +(module+ test + ;; git_buf output parameters are allocated by the binding and returned as + ;; Racket byte strings. They are not caller-visible arguments. + (check-true (procedure-arity-includes? git_message_prettify 3)) + (check-false (procedure-arity-includes? git_message_prettify 4)) + (check-equal? (git_message_prettify "hello" 0 (char->integer #\#)) + #"hello\n") + + (check-true (procedure-arity-includes? git_diff_to_buf 2)) + (check-false (procedure-arity-includes? git_diff_to_buf 3)) + + (check-true (procedure-arity-includes? git_config_parse_path 1)) + (check-false (procedure-arity-includes? git_config_parse_path 2)) + + ;; Two git_buf output parameters become two Racket values. + (check-true (procedure-arity-includes? git_commit_extract_signature 3)) + (check-false (procedure-arity-includes? git_commit_extract_signature 5))) diff --git a/libgit2/test/test-diff.rkt b/libgit2/test/test-diff.rkt index 7157179..d1369d0 100644 --- a/libgit2/test/test-diff.rkt +++ b/libgit2/test/test-diff.rkt @@ -32,6 +32,11 @@ (define delta (git_diff_get_delta diff 0)) (check-equal? (git_diff_delta-status delta) 'GIT_DELTA_MODIFIED) + (define rendered (git_diff_to_buf diff 'GIT_DIFF_FORMAT_PATCH)) + (check-true (bytes? rendered)) + (check-true (regexp-match? #rx#"tracked\\.txt" rendered)) + (check-true (regexp-match? #rx#"after, with a different size" rendered)) + (check-equal? (git_diff_file-path (git_diff_delta-old_file delta)) "tracked.txt") (check-equal? (git_diff_file-path (git_diff_delta-new_file delta)) From 6cc76d0fdabb64869cc7e5b4b2ea910247cd525a Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 12:27:25 +0200 Subject: [PATCH 11/21] Signature buffer input binding to string --- libgit2/include/signature.rkt | 3 +-- libgit2/test/test-signature-from-buffer.rkt | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 libgit2/test/test-signature-from-buffer.rkt diff --git a/libgit2/include/signature.rkt b/libgit2/include/signature.rkt index e83ca68..3e128f2 100644 --- a/libgit2/include/signature.rkt +++ b/libgit2/include/signature.rkt @@ -5,7 +5,6 @@ _git_repository _git_time_t _git_signature-pointer) - "../private/buffer.rkt" "../private/base.rkt") (provide (all-defined-out)) @@ -22,7 +21,7 @@ git_signature_free) (define-libgit2/alloc git_signature_from_buffer - (_fun _git_signature-pointer (_git_buf/bytes-or-null) -> _int) + (_fun _git_signature-pointer _string -> _int) git_signature_free) (define-libgit2/alloc git_signature_new diff --git a/libgit2/test/test-signature-from-buffer.rkt b/libgit2/test/test-signature-from-buffer.rkt new file mode 100644 index 0000000..00c88da --- /dev/null +++ b/libgit2/test/test-signature-from-buffer.rkt @@ -0,0 +1,12 @@ +#lang racket/base + +(require ffi/unsafe + rackunit + libgit2/include/signature) + +(module+ test + (define sig + (git_signature_from_buffer + "A U Thor 1234567890 +0000")) + (check-true (cpointer? sig)) + (check-true (cpointer? (git_signature_dup sig)))) From fb80800d80c399eb44fe0c080846b843fe012c90 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 12:28:29 +0200 Subject: [PATCH 12/21] Some more fixes. --- libgit2/test/test-abi-layout.rkt | 12 ++++++++++-- libgit2/test/test-remote-signatures.rkt | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt index dbb5d62..be34576 100644 --- a/libgit2/test/test-abi-layout.rkt +++ b/libgit2/test/test-abi-layout.rkt @@ -47,6 +47,14 @@ ;; These structs had the right total size before, despite missing a field. ;; Constructor arity protects the actual field set as well as sizeof. (check-equal? (ctype-sizeof config:_git_config_entry) 40) - (check-true (procedure-arity-includes? config:make-git_config_entry 6)) + (define config-ns (module->namespace 'libgit2/include/config)) + (define make-entry + (parameterize ([current-namespace config-ns]) + (eval 'make-git_config_entry))) + (check-true (procedure-arity-includes? make-entry 6)) (check-equal? (ctype-sizeof types:_git_time) 16) - (check-true (procedure-arity-includes? types:make-git_time 3)))) + (define types-ns (module->namespace 'libgit2/include/types)) + (define make-time + (parameterize ([current-namespace types-ns]) + (eval 'make-git_time))) + (check-true (procedure-arity-includes? make-time 3)))) diff --git a/libgit2/test/test-remote-signatures.rkt b/libgit2/test/test-remote-signatures.rkt index 92a6251..6a0737f 100644 --- a/libgit2/test/test-remote-signatures.rkt +++ b/libgit2/test/test-remote-signatures.rkt @@ -9,7 +9,7 @@ (dynamic-wind void (lambda () - (define repo (git_repository_init tmp 0)) + (define repo (git_repository_init tmp)) (git_remote_create repo "origin" "https://example.invalid/repo.git") ;; These functions take a remote NAME, not a git_remote pointer. From fef8fb6e513acb53a39a8e6197fc272855192ea1 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 13:10:13 +0200 Subject: [PATCH 13/21] Remote - function definitions corrected. --- libgit2/include/remote.rkt | 29 +++++++++++++++---------- libgit2/test/test-remote-signatures.rkt | 28 +++++++++++++++++++++++- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index 3516f02..2fbe54d 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -128,9 +128,9 @@ (define-libgit2/check git_remote_connect (_fun _git_remote _git_direction - _git_remote_callbacks - _git_proxy_options-pointer - _git_strarray-pointer + _git_remote_callbacks-pointer/null + _git_proxy_options-pointer/null + (_or-null _git_strarray-pointer) -> _int)) (define-libgit2 git_remote_connected @@ -155,8 +155,8 @@ (define-libgit2/check git_remote_delete (_fun _git_repository _string -> _int)) -(define-libgit2 git_remote_disconnect - (_fun _git_remote -> _void)) +(define-libgit2/check git_remote_disconnect + (_fun _git_remote -> _int)) (define-libgit2/check git_remote_download (_fun _git_remote @@ -172,11 +172,11 @@ (_fun _git_remote (_or-null _git_strarray-pointer) _git_fetch_opts-pointer/null - _string + (_or-null _string) -> _int)) (define-libgit2 git_remote_get_fetch_refspecs - (_fun [lst : _git_strarray-pointer] + (_fun [lst : (_git_strarray-pointer/alloc)] _git_remote -> (_git_error_code/check) -> lst)) @@ -231,7 +231,7 @@ (define-libgit2/check git_remote_push (_fun _git_remote (_or-null _git_strarray-pointer) - _git_push_opts-pointer + _git_push_opts-pointer/null -> _int)) (define-libgit2 git_remote_pushurl @@ -260,16 +260,21 @@ (define-libgit2 git_remote_stats (_fun _git_remote -> _git_transfer_progress-pointer)) -(define-libgit2 git_remote_stop - (_fun _git_remote -> _void)) +(define-libgit2/check git_remote_stop + (_fun _git_remote -> _int)) (define-libgit2/check git_remote_update_tips - (_fun _git_remote _git_remote_callbacks-pointer _int _git_remote_autotag_option_t _string -> _int)) + (_fun _git_remote + _git_remote_callbacks-pointer/null + _int + _git_remote_autotag_option_t + (_or-null _string) + -> _int)) (define-libgit2/check git_remote_upload (_fun _git_remote (_or-null _git_strarray-pointer) - _git_push_opts-pointer + _git_push_opts-pointer/null -> _int)) (define-libgit2 git_remote_url diff --git a/libgit2/test/test-remote-signatures.rkt b/libgit2/test/test-remote-signatures.rkt index 6a0737f..c22c525 100644 --- a/libgit2/test/test-remote-signatures.rkt +++ b/libgit2/test/test-remote-signatures.rkt @@ -24,6 +24,32 @@ (git_remote_set_url repo "origin" "https://example.invalid/other.git"))) (check-not-exn (lambda () - (git_remote_set_pushurl repo "origin" "https://example.invalid/push.git")))) + (git_remote_set_pushurl repo "origin" "https://example.invalid/push.git"))) + ;; NULL is valid for the optional connection arguments. Use an + ;; unsupported protocol so this fails inside libgit2 without network I/O. + (git_remote_set_url repo "origin" "invalid://example/repo.git") + (define remote (git_remote_lookup repo "origin")) + (check-exn + exn:fail? + (lambda () + (git_remote_connect remote 'GIT_DIRECTION_FETCH #f #f #f))) + + ;; These APIs return int status codes in libgit2, not void. A remote + ;; that never connected can still be stopped/disconnected safely. + (check-not-exn (lambda () (git_remote_stop remote))) + (check-not-exn (lambda () (git_remote_disconnect remote))) + + ;; NULL reflog message is explicitly supported by the C API. As above, + ;; the operation fails inside libgit2 because the protocol is unsupported. + (check-exn + exn:fail? + (lambda () + (git_remote_fetch remote #f #f #f))) + + ;; Fetch refspec output is owned by the caller and must be consumed just + ;; like the push-refspec output. + (check-equal? + (git_remote_get_fetch_refspecs remote) + '("+refs/heads/*:refs/remotes/origin/*"))) (lambda () (delete-directory/files tmp)))) From 0cd0902ea09ddfd9d452e1db2ca1154fbfcab783 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 13:11:11 +0200 Subject: [PATCH 14/21] Opaque pointers for callback payloads instead of _bytes. --- libgit2/include/checkout.rkt | 12 +++++----- libgit2/include/clone.rkt | 8 +++---- libgit2/include/config.rkt | 12 +++++----- libgit2/include/diff.rkt | 38 ++++++++++++++++---------------- libgit2/include/index.rkt | 8 +++---- libgit2/include/indexer.rkt | 2 +- libgit2/include/net.rkt | 2 +- libgit2/include/notes.rkt | 4 ++-- libgit2/include/odb.rkt | 6 ++--- libgit2/include/pack.rkt | 10 ++++----- libgit2/include/patch.rkt | 2 +- libgit2/include/proxy.rkt | 2 +- libgit2/include/refs.rkt | 10 ++++----- libgit2/include/remote.rkt | 16 +++++++------- libgit2/include/repository.rkt | 8 +++---- libgit2/include/revwalk.rkt | 4 ++-- libgit2/include/stash.rkt | 8 +++---- libgit2/include/status.rkt | 6 ++--- libgit2/include/submodule.rkt | 4 ++-- libgit2/include/tag.rkt | 4 ++-- libgit2/include/transport.rkt | 14 ++++++------ libgit2/include/tree.rkt | 8 +++---- libgit2/include/types.rkt | 6 ++--- libgit2/test/test-abi-layout.rkt | 21 ++++++++++++++++++ 24 files changed, 118 insertions(+), 97 deletions(-) diff --git a/libgit2/include/checkout.rkt b/libgit2/include/checkout.rkt index c92d8b7..86324ad 100644 --- a/libgit2/include/checkout.rkt +++ b/libgit2/include/checkout.rkt @@ -52,11 +52,11 @@ [chmod_calls _size])) (define _git_checkout_notify_cb - (_fun _git_checkout_notify_t _string _git_diff_file-pointer _git_diff_file-pointer _git_diff_file-pointer _bytes -> _int)) + (_fun _git_checkout_notify_t _string _git_diff_file-pointer _git_diff_file-pointer _git_diff_file-pointer _pointer -> _int)) (define _git_checkout_progress_cb - (_fun _string _size _size _bytes -> _void)) + (_fun _string _size _size _pointer -> _void)) (define _git_checkout_perfdata_cb - (_fun _git_checkout_perfdata-pointer _bytes -> _void)) + (_fun _git_checkout_perfdata-pointer _pointer -> _void)) (define-cstruct _git_checkout_opts ;; FIXME: constructor will need a wrapper to @@ -69,9 +69,9 @@ [file_open_flags _int] [notify_flags _uint] [notify_cb _git_checkout_notify_cb] - [notify_payload _bytes] + [notify_payload _pointer] [progress_cb _git_checkout_progress_cb] - [progress_payload _bytes] + [progress_payload _pointer] [paths _git_strarray] [baseline _git_tree] [baseline_index _git_index] @@ -80,7 +80,7 @@ [our_label _string] [their_label _string] [perfdata_cb _git_checkout_perfdata_cb] - [perfdata_payload _bytes])) + [perfdata_payload _pointer])) ; Functions diff --git a/libgit2/include/clone.rkt b/libgit2/include/clone.rkt index 61e48f8..8fe4f6c 100644 --- a/libgit2/include/clone.rkt +++ b/libgit2/include/clone.rkt @@ -21,9 +21,9 @@ GIT_CLONE_LOCAL_NO_LINKS) (define _git_remote_create_cb - (_fun (_cpointer _git_remote) _git_repository _string _string _bytes -> _int)) + (_fun (_cpointer _git_remote) _git_repository _string _string _pointer -> _int)) (define _git_repository_create_cb - (_fun (_cpointer _git_repository) _string _int _bytes -> _int)) + (_fun (_cpointer _git_repository) _string _int _pointer -> _int)) (define-cstruct _git_clone_opts ([version _uint] @@ -33,9 +33,9 @@ [local _git_clone_local_t] [checkout_branch _string] [repository_cb _git_repository_create_cb] - [repository_cb_payload _bytes] + [repository_cb_payload _pointer] [remote_cb _git_remote_create_cb] - [remote_cb_payload _bytes])) + [remote_cb_payload _pointer])) (define GIT_CLONE_OPTS_VERSION 1) diff --git a/libgit2/include/config.rkt b/libgit2/include/config.rkt index a0d961b..0fd1989 100644 --- a/libgit2/include/config.rkt +++ b/libgit2/include/config.rkt @@ -28,11 +28,11 @@ [include_depth _uint] [level _git_config_level_t] [free (_fun _git_config_entry-pointer -> _void)] - [payload _bytes])) + [payload _pointer])) (define _config_entry _git_config_entry-pointer) (define _git_config_foreach_cb - (_fun _git_config_entry-pointer _bytes -> _int)) + (_fun _git_config_entry-pointer _pointer -> _int)) (define _config_iterator (_cpointer 'git_config_iterator)) @@ -56,7 +56,7 @@ (_fun _git_config _string _git_config_level_t (_or-null _git_repository) _bool -> _int)) (define-libgit2/check git_config_backend_foreach_match - (_fun _git_config_backend _string _git_config_foreach_cb _bytes -> _int)) + (_fun _git_config_backend _string _git_config_foreach_cb _pointer -> _int)) (define-libgit2/check git_config_delete_entry (_fun _git_config _string -> _int)) @@ -84,10 +84,10 @@ -> out)) (define-libgit2/check git_config_foreach - (_fun _git_config _git_config_foreach_cb _bytes -> _int)) + (_fun _git_config _git_config_foreach_cb _pointer -> _int)) (define-libgit2/check git_config_foreach_match - (_fun _git_config _string _git_config_foreach_cb _bytes -> _int)) + (_fun _git_config _string _git_config_foreach_cb _pointer -> _int)) (define-libgit2/dealloc git_config_free (_fun _git_config -> _void)) @@ -109,7 +109,7 @@ (_fun _int _git_config _string _git_cvar_map-pointer _size -> _int)) (define-libgit2/check git_config_get_multivar_foreach - (_fun _git_config _string _string _git_config_foreach_cb _bytes -> _int)) + (_fun _git_config _string _string _git_config_foreach_cb _pointer -> _int)) (define-libgit2/check git_config_get_path (_fun [out : (_git_buf/bytes-or-null)] _git_config _string -> _int diff --git a/libgit2/include/diff.rkt b/libgit2/include/diff.rkt index 32af345..1c010f2 100644 --- a/libgit2/include/diff.rkt +++ b/libgit2/include/diff.rkt @@ -90,9 +90,9 @@ [new_file _git_diff_file])) (define _git_diff_notify_cb - (_fun _git_diff _git_diff_delta-pointer _string _bytes -> _int)) + (_fun _git_diff _git_diff_delta-pointer _string _pointer -> _int)) (define _git_diff_progress_cb - (_fun _git_diff _string _string _bytes -> _int)) + (_fun _git_diff _string _string _pointer -> _int)) (define-cstruct _git_diff_opts ([version _uint] @@ -101,7 +101,7 @@ [pathspec _git_strarray] [notify_cb _git_diff_notify_cb] [progress_cb _git_diff_progress_cb] - [payload _bytes] + [payload _pointer] [context_lines _uint32] [interhunk_lines _uint32] [id_abbrev _uint16] @@ -112,7 +112,7 @@ (define GIT_DIFF_OPTS_VERSION 1) (define _git_diff_file_cb - (_fun _git_diff_delta-pointer _float _bytes -> _int)) + (_fun _git_diff_delta-pointer _float _pointer -> _int)) (define GIT_DIFF_HUNK_HEADER_SIZE 128) @@ -133,7 +133,7 @@ [new_file _git_diff_binary_file])) (define _git_diff_binary_cb - (_fun _git_diff_delta-pointer _git_diff_binary-pointer _bytes -> _int)) + (_fun _git_diff_delta-pointer _git_diff_binary-pointer _pointer -> _int)) (define-cstruct _git_diff_hunk ([old_start _int] @@ -144,7 +144,7 @@ [header (_array _uint8 GIT_DIFF_HUNK_HEADER_SIZE)])) (define _git_diff_hunk_cb - (_fun _git_diff_delta-pointer _git_diff_hunk-pointer _bytes -> _int)) + (_fun _git_diff_delta-pointer _git_diff_hunk-pointer _pointer -> _int)) (define-enum _git_diff_line_t [GIT_DIFF_LINE_CONTEXT = 32] ; = ' ' @@ -168,7 +168,7 @@ [content _string])) (define _git_diff_line_cb - (_fun _git_diff_delta-pointer _git_diff_hunk-pointer _git_diff_line-pointer _bytes -> _int)) + (_fun _git_diff_delta-pointer _git_diff_hunk-pointer _git_diff_line-pointer _pointer -> _int)) (define-bitmask _git_diff_find_t [GIT_DIFF_FIND_BY_CONFIG = 0] @@ -189,20 +189,20 @@ [GIT_DIFF_FIND_REMOVE_UNMODIFIED = #x00010000]) (define-cstruct _git_diff_similarity_metric - ([file_signature (_fun (_cpointer _bytes) + ([file_signature (_fun (_cpointer _pointer) _git_diff_file-pointer _string - _bytes + _pointer -> _int)] - [buffer_signature (_fun (_cpointer _bytes) + [buffer_signature (_fun (_cpointer _pointer) _git_diff_file-pointer _string _size - _bytes + _pointer -> _int)] - [free_signature (_fun _bytes _bytes -> _void)] - [similarity (_fun (_cpointer _int) _bytes _bytes _bytes -> _int)] - [payload _bytes])) + [free_signature (_fun _pointer _pointer -> _void)] + [similarity (_fun (_cpointer _int) _pointer _pointer _pointer -> _int)] + [payload _pointer])) (define-cstruct _git_diff_find_options ([version _int] @@ -241,13 +241,13 @@ (_fun _diff_stats -> _void)) (define-libgit2/check git_diff_blob_to_buffer - (_fun _git_blob/null _string _string _size _string _git_diff_opts-pointer/null _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _bytes -> _int)) + (_fun _git_blob/null _string _string _size _string _git_diff_opts-pointer/null _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _pointer -> _int)) (define-libgit2/check git_diff_blobs - (_fun _git_blob/null _string _git_blob/null _string _git_diff_opts-pointer/null _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _bytes -> _int)) + (_fun _git_blob/null _string _git_blob/null _string _git_diff_opts-pointer/null _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _pointer -> _int)) (define-libgit2/check git_diff_buffers - (_fun _bytes _size _string _bytes _size _string _git_diff_opts-pointer/null _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _bytes -> _int)) + (_fun _bytes _size _string _bytes _size _string _git_diff_opts-pointer/null _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _pointer -> _int)) (define-libgit2/check git_diff_find_options_init (_fun _git_diff_find_options-pointer _uint -> _int)) @@ -256,7 +256,7 @@ (_fun _git_diff _git_diff_find_options-pointer/null -> _int)) (define-libgit2/check git_diff_foreach - (_fun _git_diff _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _bytes -> _int)) + (_fun _git_diff _git_diff_file_cb _git_diff_binary_cb _git_diff_hunk_cb _git_diff_line_cb _pointer -> _int)) (define-libgit2/alloc git_diff_from_buffer (_fun _git_diff _string _size -> _int) @@ -293,7 +293,7 @@ (_fun _git_diff _git_delta_t -> _size)) (define-libgit2/check git_diff_print - (_fun _git_diff _git_diff_format_t _git_diff_line_cb _bytes -> _int)) + (_fun _git_diff _git_diff_format_t _git_diff_line_cb _pointer -> _int)) (define-libgit2 git_diff_stats_deletions (_fun _diff_stats -> _size)) diff --git a/libgit2/include/index.rkt b/libgit2/include/index.rkt index 67c61da..49322f9 100644 --- a/libgit2/include/index.rkt +++ b/libgit2/include/index.rkt @@ -71,7 +71,7 @@ [GIT_INDEX_CAPABILITY_FROM_OWNER -1]) (define _git_index_matched_path_cb - (_fun _string _string _bytes -> _int)) + (_fun _string _string _pointer -> _int)) (define-bitmask _git_index_add_option_t [GIT_INDEX_ADD_DEFAULT 0] @@ -97,7 +97,7 @@ _git_strarray-pointer _git_index_add_option_t _git_index_matched_path_cb - _bytes + _pointer -> _int)) (define-libgit2/check git_index_add_bypath @@ -199,7 +199,7 @@ (_fun _git_index _git_strarray-pointer _git_index_matched_path_cb - _bytes + _pointer -> _int)) (define-libgit2/check git_index_remove_bypath @@ -221,7 +221,7 @@ (_fun _git_index _git_strarray-pointer _git_index_matched_path_cb - _bytes + _pointer -> _int)) (define-libgit2/check git_index_write diff --git a/libgit2/include/indexer.rkt b/libgit2/include/indexer.rkt index 50ac365..e1a802a 100644 --- a/libgit2/include/indexer.rkt +++ b/libgit2/include/indexer.rkt @@ -17,7 +17,7 @@ (define-cstruct _git_indexer_options ([version _uint] [progress_cb _git_transfer_progress_cb] - [progress_cb_payload _bytes] + [progress_cb_payload _pointer] [verify _ubyte])) (define GIT_INDEXER_OPTIONS_VERSION 1) diff --git a/libgit2/include/net.rkt b/libgit2/include/net.rkt index 8d1ec38..aefd102 100644 --- a/libgit2/include/net.rkt +++ b/libgit2/include/net.rkt @@ -20,4 +20,4 @@ [symref_target _string])) (define _git_headlist_cb - (_fun _git_remote_head-pointer _bytes -> _int)) + (_fun _git_remote_head-pointer _pointer -> _int)) diff --git a/libgit2/include/notes.rkt b/libgit2/include/notes.rkt index f052b6c..06302ae 100644 --- a/libgit2/include/notes.rkt +++ b/libgit2/include/notes.rkt @@ -13,7 +13,7 @@ ; Types (define _git_note_foreach_cb - (_fun _git_oid-pointer _git_oid-pointer _bytes -> _int)) + (_fun _git_oid-pointer _git_oid-pointer _pointer -> _int)) (define _note_iterator (_cpointer 'git_iterator)) @@ -29,7 +29,7 @@ (_fun _git_oid-pointer _git_repository _string _git_signature-pointer _git_signature-pointer _git_oid-pointer _git_note _int -> _int)) (define-libgit2/check git_note_foreach - (_fun _git_repository _string _git_note_foreach_cb _bytes -> _int)) + (_fun _git_repository _string _git_note_foreach_cb _pointer -> _int)) (define-libgit2/dealloc git_note_free (_fun _git_note -> _void)) diff --git a/libgit2/include/odb.rkt b/libgit2/include/odb.rkt index 7d69384..4fcc20d 100644 --- a/libgit2/include/odb.rkt +++ b/libgit2/include/odb.rkt @@ -20,7 +20,7 @@ ; Types (define _git_odb_foreach_cb - (_fun _git_oid-pointer _bytes -> _int)) + (_fun _git_oid-pointer _pointer -> _int)) (define-cstruct _git_odb_expand_id ([id _git_oid] @@ -49,7 +49,7 @@ (_fun _git_odb _odb_expand_id _size -> _int)) (define-libgit2/check git_odb_foreach - (_fun _git_odb _git_odb_foreach_cb _bytes -> _int)) + (_fun _git_odb _git_odb_foreach_cb _pointer -> _int)) (define-libgit2/dealloc git_odb_free (_fun _git_odb -> _void)) @@ -136,4 +136,4 @@ (_fun _git_oid-pointer _git_odb _bytes _size _git_object_t -> _int)) (define-libgit2/alloc git_odb_write_pack - (_fun _git_odb_writepack-pointer _git_odb _git_transfer_progress_cb _bytes -> _int)) + (_fun _git_odb_writepack-pointer _git_odb _git_transfer_progress_cb _pointer -> _int)) diff --git a/libgit2/include/pack.rkt b/libgit2/include/pack.rkt index d8fdf25..18bb8a3 100644 --- a/libgit2/include/pack.rkt +++ b/libgit2/include/pack.rkt @@ -20,15 +20,15 @@ GIT_PACKBUILDER_DELTAFICATION))) (define _git_packbuilder_foreach_cb - (_fun _bytes _size _bytes -> _int)) + (_fun _bytes _size _pointer -> _int)) (define _git_packbuilder_progress - (_fun _int _uint32 _uint32 _bytes -> _int)) + (_fun _int _uint32 _uint32 _pointer -> _int)) ; Functions (define-libgit2/check git_packbuilder_foreach - (_fun _git_packbuilder _git_packbuilder_foreach_cb _bytes -> _int)) + (_fun _git_packbuilder _git_packbuilder_foreach_cb _pointer -> _int)) (define-libgit2/dealloc git_packbuilder_free (_fun _git_packbuilder -> _void)) @@ -59,13 +59,13 @@ (_fun _git_packbuilder -> _size)) (define-libgit2/check git_packbuilder_set_callbacks - (_fun _git_packbuilder _git_packbuilder_progress _bytes -> _int)) + (_fun _git_packbuilder _git_packbuilder_progress _pointer -> _int)) (define-libgit2 git_packbuilder_set_threads (_fun _git_packbuilder _uint -> _uint)) (define-libgit2/check git_packbuilder_write - (_fun _git_packbuilder _string _uint _git_transfer_progress_cb _bytes -> _int)) + (_fun _git_packbuilder _string _uint _git_transfer_progress_cb _pointer -> _int)) (define-libgit2 git_packbuilder_written (_fun _git_packbuilder -> _size)) diff --git a/libgit2/include/patch.rkt b/libgit2/include/patch.rkt index 6bd2e8b..e4fbd36 100644 --- a/libgit2/include/patch.rkt +++ b/libgit2/include/patch.rkt @@ -59,7 +59,7 @@ (_fun _git_patch _size -> _int)) (define-libgit2/check git_patch_print - (_fun _git_patch _git_diff_line_cb _bytes -> _int)) + (_fun _git_patch _git_diff_line_cb _pointer -> _int)) (define-libgit2 git_patch_size (_fun _git_patch _int _int _int -> _size)) diff --git a/libgit2/include/proxy.rkt b/libgit2/include/proxy.rkt index 89f74f8..f9dd62f 100644 --- a/libgit2/include/proxy.rkt +++ b/libgit2/include/proxy.rkt @@ -21,7 +21,7 @@ [url _string] [credentials _git_credential_acquire_cb] [certificate_check _git_transport_certificate_check_cb] - [payload _bytes])) + [payload _pointer])) (define GIT_PROXY_OPTIONS_VERSION 1) diff --git a/libgit2/include/refs.rkt b/libgit2/include/refs.rkt index 3acb978..5199280 100644 --- a/libgit2/include/refs.rkt +++ b/libgit2/include/refs.rkt @@ -21,9 +21,9 @@ ; Types (define _git_reference_foreach_cb - (_fun _git_reference _bytes -> _int)) + (_fun _git_reference _pointer -> _int)) (define _git_reference_foreach_name_cb - (_fun _string _bytes -> _int)) + (_fun _string _pointer -> _int)) (define-bitmask _git_reference_normalize_t [GIT_REF_FORMAT_NORMAL = 0] @@ -70,13 +70,13 @@ (_fun _git_repository _string -> _int)) (define-libgit2/check git_reference_foreach - (_fun _git_repository _git_reference_foreach_cb _bytes -> _int)) + (_fun _git_repository _git_reference_foreach_cb _pointer -> _int)) (define-libgit2/check git_reference_foreach_glob - (_fun _git_repository _string _git_reference_foreach_name_cb _bytes -> _int)) + (_fun _git_repository _string _git_reference_foreach_name_cb _pointer -> _int)) (define-libgit2/check git_reference_foreach_name - (_fun _git_repository _git_reference_foreach_name_cb _bytes -> _int)) + (_fun _git_repository _git_reference_foreach_name_cb _pointer -> _int)) (define-libgit2 git_reference_has_log (_fun _git_repository _string -> _bool)) diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index 2fbe54d..26c4721 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -31,7 +31,7 @@ GIT_REMOTE_COMPLETION_ERROR))) (define _git_push_transfer_progress - (_fun _uint _uint _size _bytes -> _int)) + (_fun _uint _uint _size _pointer -> _int)) (define-cstruct _git_push_update ([src_name _string] @@ -41,30 +41,30 @@ (define _push_update _git_push_update-pointer) (define _git_push_negotiation - (_fun (_cpointer _push_update) _size _bytes -> _int)) + (_fun (_cpointer _push_update) _size _pointer -> _int)) (define _git_remote_ready_cb - (_fun _git_remote _git_direction _bytes -> _int)) + (_fun _git_remote _git_direction _pointer -> _int)) (define _git_url_resolve_cb - (_fun _pointer _string _git_direction _bytes -> _int)) + (_fun _pointer _string _git_direction _pointer -> _int)) (define-cstruct _git_remote_callbacks ([version _uint] [sideband_progress _git_transport_message_cb] - [completion (_fun _git_remote_completion_type _bytes -> _int)] + [completion (_fun _git_remote_completion_type _pointer -> _int)] [credentials _git_credential_acquire_cb] [certificate_check _git_transport_certificate_check_cb] [transfer_progress _git_transfer_progress_cb] - [update_tips (_fun _string _git_oid-pointer _git_oid-pointer _bytes -> _int)] + [update_tips (_fun _string _git_oid-pointer _git_oid-pointer _pointer -> _int)] [pack_progress _git_packbuilder_progress] [push_transfer_progress _git_push_transfer_progress] - [push_update_reference (_fun _string _string _bytes -> _int)] + [push_update_reference (_fun _string _string _pointer -> _int)] [push_negotiation _git_push_negotiation] [transport _git_transport_cb] [remote_ready _git_remote_ready_cb] - [payload _bytes] + [payload _pointer] [resolve_url _git_url_resolve_cb])) (define GIT_REMOTE_CB_VERSION 1) diff --git a/libgit2/include/repository.rkt b/libgit2/include/repository.rkt index 1416556..aa97529 100644 --- a/libgit2/include/repository.rkt +++ b/libgit2/include/repository.rkt @@ -180,10 +180,10 @@ (define GIT_REPOSITORY_INIT_OPTS_VERSION 1) (define _git_repository_fetchhead_foreach_cb - (_fun _string _string _git_oid-pointer _uint _bytes -> _int)) + (_fun _string _string _git_oid-pointer _uint _pointer -> _int)) (define _git_repository_mergehead_foreach_cb - (_fun _git_oid-pointer _bytes -> _int)) + (_fun _git_oid-pointer _pointer -> _int)) (define-enum _git_repository_state_t GIT_REPOSITORY_STATE_NONE @@ -213,7 +213,7 @@ (_fun _git_repository -> (_git_error_code/check))) (define-libgit2 git_repository_fetchhead_foreach - (_fun _git_repository _git_repository_fetchhead_foreach_cb _bytes + (_fun _git_repository _git_repository_fetchhead_foreach_cb _pointer -> (_git_error_code/check))) (define-libgit2 git_repository_get_namespace @@ -268,7 +268,7 @@ (_fun _git_repository -> _bool)) (define-libgit2 git_repository_mergehead_foreach - (_fun _git_repository _git_repository_mergehead_foreach_cb _bytes + (_fun _git_repository _git_repository_mergehead_foreach_cb _pointer -> (_git_error_code/check))) (define-libgit2 git_repository_message diff --git a/libgit2/include/revwalk.rkt b/libgit2/include/revwalk.rkt index cb0bf11..e0b2e7c 100644 --- a/libgit2/include/revwalk.rkt +++ b/libgit2/include/revwalk.rkt @@ -20,12 +20,12 @@ [GIT_SORT_REVERSE = 4]) (define _git_revwalk_hide_cb - (_fun _git_oid-pointer _bytes -> _int)) + (_fun _git_oid-pointer _pointer -> _int)) ; Functions (define-libgit2/check git_revwalk_add_hide_cb - (_fun _git_revwalk _git_revwalk_hide_cb _bytes -> _int)) + (_fun _git_revwalk _git_revwalk_hide_cb _pointer -> _int)) (define-libgit2/dealloc git_revwalk_free (_fun _git_revwalk -> _void)) diff --git a/libgit2/include/stash.rkt b/libgit2/include/stash.rkt index 1547747..4e93980 100644 --- a/libgit2/include/stash.rkt +++ b/libgit2/include/stash.rkt @@ -33,19 +33,19 @@ GIT_STASH_APPLY_PROGRESS_DONE) (define _git_stash_apply_progress_cb - (_fun _git_stash_apply_progress_t _bytes -> _int)) + (_fun _git_stash_apply_progress_t _pointer -> _int)) (define-cstruct _git_stash_apply_opts ([version _uint] [flags _git_stash_apply_flags] [checkout_options _git_checkout_opts] [progress_cb _git_stash_apply_progress_cb] - [progress_payload _bytes])) + [progress_payload _pointer])) (define GIT_STASH_APPLY_OPTS_VERSION 1) (define _git_stash_cb - (_fun _size _string _git_oid-pointer _bytes -> _int)) + (_fun _size _string _git_oid-pointer _pointer -> _int)) ; Functions @@ -59,7 +59,7 @@ (_fun _git_repository _size -> _int)) (define-libgit2/check git_stash_foreach - (_fun _git_repository _git_stash_cb _bytes -> _int)) + (_fun _git_repository _git_stash_cb _pointer -> _int)) (define-libgit2/check git_stash_pop (_fun _git_repository _size _git_stash_apply_opts-pointer -> _int)) diff --git a/libgit2/include/status.rkt b/libgit2/include/status.rkt index 7be4abd..0ed200a 100644 --- a/libgit2/include/status.rkt +++ b/libgit2/include/status.rkt @@ -30,7 +30,7 @@ GIT_STATUS_CONFLICTED = #x8000))) (define _git_status_cb - (_fun _string _git_status_t _bytes -> _int)) + (_fun _string _git_status_t _pointer -> _int)) (define _git_status_show_t (_enum '(GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0 @@ -86,10 +86,10 @@ (_fun _uint _git_repository _string -> _int)) (define-libgit2/check git_status_foreach - (_fun _git_repository _git_status_cb _bytes -> _int)) + (_fun _git_repository _git_status_cb _pointer -> _int)) (define-libgit2/check git_status_foreach_ext - (_fun _git_repository _git_status_opts-pointer _git_status_cb _bytes -> _int)) + (_fun _git_repository _git_status_opts-pointer _git_status_cb _pointer -> _int)) (define-libgit2/check git_status_options_init (_fun _git_status_opts-pointer _uint -> _int)) diff --git a/libgit2/include/submodule.rkt b/libgit2/include/submodule.rkt index 889d5b1..e520371 100644 --- a/libgit2/include/submodule.rkt +++ b/libgit2/include/submodule.rkt @@ -37,7 +37,7 @@ [GIT_SUBMODULE_STATUS_WD_UNTRACKED = #x2000]) (define _git_submodule_cb - (_fun _git_submodule _string _bytes -> _int)) + (_fun _git_submodule _string _pointer -> _int)) (define-cstruct _git_submodule_update_opts ([version _uint] @@ -65,7 +65,7 @@ (_fun _git_submodule -> _git_submodule_recurse_t)) (define-libgit2/check git_submodule_foreach - (_fun _git_repository _git_submodule_cb _bytes -> _int)) + (_fun _git_repository _git_submodule_cb _pointer -> _int)) (define-libgit2/dealloc git_submodule_free (_fun _git_submodule -> _void)) diff --git a/libgit2/include/tag.rkt b/libgit2/include/tag.rkt index 0458ca2..ebc3c53 100644 --- a/libgit2/include/tag.rkt +++ b/libgit2/include/tag.rkt @@ -18,7 +18,7 @@ ; Types (define _git_tag_foreach_cb - (_fun _string _git_oid-pointer _bytes -> _int)) + (_fun _string _git_oid-pointer _pointer -> _int)) ; Functions @@ -42,7 +42,7 @@ git_tag_free) (define-libgit2/check git_tag_foreach - (_fun _git_repository _git_tag_foreach_cb _bytes -> _int)) + (_fun _git_repository _git_tag_foreach_cb _pointer -> _int)) (define-libgit2 git_tag_id (_fun _git_tag -> _git_oid-pointer)) diff --git a/libgit2/include/transport.rkt b/libgit2/include/transport.rkt index de3f388..62ef5a8 100644 --- a/libgit2/include/transport.rkt +++ b/libgit2/include/transport.rkt @@ -12,7 +12,7 @@ ; Types (define _git_transport_cb - (_fun (_cpointer _git_transport) _git_remote _bytes -> _int)) + (_fun (_cpointer _git_transport) _git_remote _pointer -> _int)) (define _git_cert_ssh_t (_bitmask '(GIT_CERT_SSH_MD5 = #x0001 @@ -77,7 +77,7 @@ ([parent _git_credential] [username _string] [prompt_callback _git_credential_ssh_interactive_callback] - [payload _bytes])) + [payload _pointer])) (define-cstruct _git_credential_ssh_custom ([parent _git_credential] @@ -85,14 +85,14 @@ [publickey _string] [publickey_len _size] [sign_callback _git_credential_sign_callback] - [payload _bytes])) + [payload _pointer])) (define-cstruct _git_credential_username ([parent _git_credential] [username (_array _int8 1)])) (define _git_credential_acquire_cb - (_fun (_cpointer _git_credential) _string _string _uint _bytes -> _int)) + (_fun (_cpointer _git_credential) _string _string _uint _pointer -> _int)) ; Functions @@ -107,11 +107,11 @@ (_fun _git_credential -> _bool)) (define-libgit2/alloc git_credential_ssh_custom_new - (_fun _git_credential _string _string _size _git_credential_sign_callback _bytes -> _int) + (_fun _git_credential _string _string _size _git_credential_sign_callback _pointer -> _int) git_credential_free) (define-libgit2/alloc git_credential_ssh_interactive_new - (_fun _git_credential _string _git_credential_ssh_interactive_callback _bytes -> _int) + (_fun _git_credential _string _git_credential_ssh_interactive_callback _pointer -> _int) git_credential_free) (define-libgit2/alloc git_credential_ssh_key_from_agent @@ -131,7 +131,7 @@ git_credential_free) (define-libgit2/alloc git_credential_userpass - (_fun _git_credential _string _string _uint _bytes -> _int) + (_fun _git_credential _string _string _uint _pointer -> _int) git_credential_free) (define-libgit2/alloc git_credential_userpass_plaintext_new diff --git a/libgit2/include/tree.rkt b/libgit2/include/tree.rkt index b11d055..adb00b5 100644 --- a/libgit2/include/tree.rkt +++ b/libgit2/include/tree.rkt @@ -19,10 +19,10 @@ ; Types (define _git_treebuilder_filter_cb - (_fun _git_tree_entry _bytes -> _int)) + (_fun _git_tree_entry _pointer -> _int)) (define _git_treewalk_cb - (_fun _string _git_tree_entry _bytes -> _int)) + (_fun _string _git_tree_entry _pointer -> _int)) (define _git_treewalk_mode (_enum '(GIT_TREEWALK_PRE @@ -108,7 +108,7 @@ (_fun _git_tree -> _git_repository)) (define-libgit2/check git_tree_walk - (_fun _git_tree _git_treewalk_mode _git_treewalk_cb _bytes -> _int)) + (_fun _git_tree _git_treewalk_mode _git_treewalk_cb _pointer -> _int)) (define-libgit2 git_treebuilder_clear @@ -118,7 +118,7 @@ (_fun _git_treebuilder -> _uint)) (define-libgit2/check git_treebuilder_filter - (_fun _git_treebuilder _git_treebuilder_filter_cb _bytes -> _int)) + (_fun _git_treebuilder _git_treebuilder_filter_cb _pointer -> _int)) (define-libgit2/dealloc git_treebuilder_free (_fun _git_treebuilder -> _void)) diff --git a/libgit2/include/types.rkt b/libgit2/include/types.rkt index dff167a..cb0817f 100644 --- a/libgit2/include/types.rkt +++ b/libgit2/include/types.rkt @@ -173,10 +173,10 @@ [received_bytes _size])) (define _git_transfer_progress_cb - (_fun _git_transfer_progress-pointer _bytes -> _int)) + (_fun _git_transfer_progress-pointer _pointer -> _int)) (define _git_transport_message_cb - (_fun _string _int _bytes -> _int)) + (_fun _string _int _pointer -> _int)) (define-enum _git_cert_t GIT_CERT_NONE @@ -188,7 +188,7 @@ ([cert_type _git_cert_t])) (define _git_transport_certificate_check_cb - (_fun _git_cert-pointer _int _string _bytes -> _int)) + (_fun _git_cert-pointer _int _string _pointer -> _int)) (define-cpointer-type _git_submodule) diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt index be34576..bc2a7d2 100644 --- a/libgit2/test/test-abi-layout.rkt +++ b/libgit2/test/test-abi-layout.rkt @@ -20,6 +20,7 @@ (require (prefix-in net: "../include/net.rkt") (prefix-in transport: "../include/transport.rkt") + (prefix-in proxy: "../include/proxy.rkt") (prefix-in status: "../include/status.rkt") (prefix-in rebase: "../include/rebase.rkt") (prefix-in merge: "../include/merge.rkt") @@ -39,6 +40,26 @@ (check-equal? (ctype-sizeof clone:_git_clone_opts) 408) (check-equal? (ctype-sizeof submodule:_git_submodule_update_opts) 368))) +(when (= (ctype-sizeof _pointer) 8) + (test-case "remote and credential payloads are opaque pointers" + (define payload (malloc 1 'raw)) + (define callbacks + (make-git_remote_callbacks GIT_REMOTE_CB_VERSION + #f #f #f #f #f #f #f #f #f #f #f #f + payload + #f)) + (check-true (cpointer=? payload (git_remote_callbacks-payload callbacks))) + (define proxy-options + (proxy:make-git_proxy_options proxy:GIT_PROXY_OPTIONS_VERSION + 'GIT_PROXY_NONE + "http://proxy.invalid" + #f + #f + payload)) + (check-true + (cpointer=? payload + (proxy:git_proxy_options-payload proxy-options))))) + (require (prefix-in config: "../include/config.rkt") (prefix-in types: "../include/types.rkt")) From 9597824b2bb9cdc9cd065a9b4e9998f9874429f6 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 13:11:55 +0200 Subject: [PATCH 15/21] Credentials struct layout fixes. --- libgit2/include/status.rkt | 2 +- libgit2/include/transport.rkt | 22 ++++++++++++++++------ libgit2/test/test-abi-layout.rkt | 6 ++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/libgit2/include/status.rkt b/libgit2/include/status.rkt index 0ed200a..6af6804 100644 --- a/libgit2/include/status.rkt +++ b/libgit2/include/status.rkt @@ -58,7 +58,7 @@ (define GIT_STATUS_OPT_DEFAULTS '(GIT_STATUS_OPT_INCLUDE_IGNORED GIT_STATUS_OPT_INCLUDE_UNTRACKED - GIT_STATUS_OPTS_RECURSE_UNTRACKED_DIRS)) + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS)) (define-cstruct _git_status_opts ;; FIXME: constructor will need a wrapper to diff --git a/libgit2/include/transport.rkt b/libgit2/include/transport.rkt index 62ef5a8..5d125d2 100644 --- a/libgit2/include/transport.rkt +++ b/libgit2/include/transport.rkt @@ -54,10 +54,19 @@ GIT_CREDTYPE_USERNAME = #x0020 GIT_CREDTYPE_SSH_MEMORY = #x0040))) +;; Public APIs use git_credential as an opaque pointer, while the concrete +;; credential structs embed the base git_credential structure by value. (define _git_credential (_cpointer 'git_credential)) +(define _git_credential_free_cb + (_fun _pointer -> _void)) + +(define-cstruct _git_credential_base + ([credtype _git_credetial_t] + [free _git_credential_free_cb])) + (define-cstruct _git_credential_userpass_plaintext - ([parent _git_credential] + ([parent _git_credential_base] [username _string] [password _string])) @@ -67,20 +76,20 @@ (_fun _string _int _string _int _int (_cpointer 'LIBSSH_USERAUTH_KBDINT_PROMPT) (_cpointer 'LIBSSH_USERAUTH_KBDINT_RESPONSE) (_cpointer _bytes) -> _void)) (define-cstruct _git_credential_ssh_key - ([parent _git_credential] + ([parent _git_credential_base] [username _string] [publickey _string] [privatekey _string] [passphrase _string])) (define-cstruct _git_credential_ssh_interactive - ([parent _git_credential] + ([parent _git_credential_base] [username _string] [prompt_callback _git_credential_ssh_interactive_callback] [payload _pointer])) (define-cstruct _git_credential_ssh_custom - ([parent _git_credential] + ([parent _git_credential_base] [username _string] [publickey _string] [publickey_len _size] @@ -88,7 +97,7 @@ [payload _pointer])) (define-cstruct _git_credential_username - ([parent _git_credential] + ([parent _git_credential_base] [username (_array _int8 1)])) (define _git_credential_acquire_cb @@ -135,4 +144,5 @@ git_credential_free) (define-libgit2/alloc git_credential_userpass_plaintext_new - (_fun _git_credential _string _string -> _int)) + (_fun _git_credential _string _string -> _int) + git_credential_free) diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt index bc2a7d2..2ad9c95 100644 --- a/libgit2/test/test-abi-layout.rkt +++ b/libgit2/test/test-abi-layout.rkt @@ -31,6 +31,12 @@ (test-case "libgit2 1.4.2 option and network struct fields" (check-equal? (ctype-sizeof net:_git_remote_head) 64) (check-equal? (ctype-sizeof transport:_git_cert_hostkey) 96) + (check-equal? (ctype-sizeof transport:_git_credential_base) 16) + (check-equal? (ctype-sizeof transport:_git_credential_userpass_plaintext) 32) + (check-equal? (ctype-sizeof transport:_git_credential_ssh_key) 48) + (check-equal? (ctype-sizeof transport:_git_credential_ssh_interactive) 40) + (check-equal? (ctype-sizeof transport:_git_credential_ssh_custom) 56) + (check-equal? (ctype-sizeof transport:_git_credential_username) 24) (check-equal? (ctype-sizeof status:_git_status_opts) 48) (check-equal? (ctype-sizeof _git_remote_callbacks) 120) (check-equal? (ctype-sizeof _git_fetch_opts) 208) From 3da804adc109ae9a713e940035d56dd62dce36f7 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 14:55:16 +0200 Subject: [PATCH 16/21] change back to _string. --- libgit2/include/remote.rkt | 4 ++-- libgit2/test/test-abi-layout.rkt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index 26c4721..fb9785b 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -172,7 +172,7 @@ (_fun _git_remote (_or-null _git_strarray-pointer) _git_fetch_opts-pointer/null - (_or-null _string) + _string -> _int)) (define-libgit2 git_remote_get_fetch_refspecs @@ -268,7 +268,7 @@ _git_remote_callbacks-pointer/null _int _git_remote_autotag_option_t - (_or-null _string) + _string -> _int)) (define-libgit2/check git_remote_upload diff --git a/libgit2/test/test-abi-layout.rkt b/libgit2/test/test-abi-layout.rkt index 2ad9c95..837c57d 100644 --- a/libgit2/test/test-abi-layout.rkt +++ b/libgit2/test/test-abi-layout.rkt @@ -54,7 +54,7 @@ #f #f #f #f #f #f #f #f #f #f #f #f payload #f)) - (check-true (cpointer=? payload (git_remote_callbacks-payload callbacks))) + (check-true (ptr-equal? payload (git_remote_callbacks-payload callbacks))) (define proxy-options (proxy:make-git_proxy_options proxy:GIT_PROXY_OPTIONS_VERSION 'GIT_PROXY_NONE @@ -63,7 +63,7 @@ #f payload)) (check-true - (cpointer=? payload + (ptr-equal? payload (proxy:git_proxy_options-payload proxy-options))))) (require (prefix-in config: "../include/config.rkt") From 90c36b8355f13e8f132638c2cb382938d4167300 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 15:29:06 +0200 Subject: [PATCH 17/21] Problem with git_strarray allocation. --- libgit2/include/strarray.rkt | 2 +- libgit2/test/test-remote-signatures.rkt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libgit2/include/strarray.rkt b/libgit2/include/strarray.rkt index a0b31fa..0d9ffbf 100644 --- a/libgit2/include/strarray.rkt +++ b/libgit2/include/strarray.rkt @@ -78,6 +78,6 @@ (syntax-parser [(_) #'(type: - _git_strarray-pointer + _git_strarray:raw-pointer pre: (make-git_strarray:raw #f 0) post: (gsa => (consume-git_strarray gsa)))])) diff --git a/libgit2/test/test-remote-signatures.rkt b/libgit2/test/test-remote-signatures.rkt index c22c525..9335ea0 100644 --- a/libgit2/test/test-remote-signatures.rkt +++ b/libgit2/test/test-remote-signatures.rkt @@ -15,7 +15,7 @@ ;; These functions take a remote NAME, not a git_remote pointer. (check-not-exn (lambda () - (git_remote_add_fetch repo "origin" "+refs/heads/*:refs/remotes/origin/*"))) + (git_remote_add_fetch repo "origin" "+refs/tags/*:refs/tags/*"))) (check-not-exn (lambda () (git_remote_add_push repo "origin" "refs/heads/main:refs/heads/main"))) @@ -50,6 +50,7 @@ ;; like the push-refspec output. (check-equal? (git_remote_get_fetch_refspecs remote) - '("+refs/heads/*:refs/remotes/origin/*"))) + '("+refs/heads/*:refs/remotes/origin/*" + "+refs/tags/*:refs/tags/*"))) (lambda () (delete-directory/files tmp)))) From da828739d833fef00c97dcf99a442b0325cf3914 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 19:17:30 +0200 Subject: [PATCH 18/21] Memory corruption fixes (when in DrRacket). --- libgit2/include/blob.rkt | 3 +- libgit2/include/commit.rkt | 3 +- libgit2/include/submodule.rkt | 3 +- libgit2/scribblings/blob.scrbl | 2 +- libgit2/scribblings/commit.scrbl | 2 +- libgit2/scribblings/submodule.scrbl | 2 +- libgit2/test/test-owner-lifetimes.rkt | 65 +++++++++++++++++++++++++++ 7 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 libgit2/test/test-owner-lifetimes.rkt diff --git a/libgit2/include/blob.rkt b/libgit2/include/blob.rkt index 1404f41..77546c1 100644 --- a/libgit2/include/blob.rkt +++ b/libgit2/include/blob.rkt @@ -52,8 +52,7 @@ git_blob_free) (define-libgit2 git_blob_owner - (_fun _git_blob -> _git_repository) - #:wrap (allocator git_repository_free)) + (_fun _git_blob -> _git_repository)) (define-libgit2 git_blob_rawcontent (_fun _git_blob -> _bytes)) diff --git a/libgit2/include/commit.rkt b/libgit2/include/commit.rkt index 0334d93..bceec62 100644 --- a/libgit2/include/commit.rkt +++ b/libgit2/include/commit.rkt @@ -97,8 +97,7 @@ git_commit_free) (define-libgit2 git_commit_owner - (_fun _git_commit -> _git_repository) - #:wrap (allocator git_repository_free)) + (_fun _git_commit -> _git_repository)) (define-libgit2/alloc git_commit_parent (_fun _git_commit _git_commit _uint -> _int) diff --git a/libgit2/include/submodule.rkt b/libgit2/include/submodule.rkt index e520371..892cc88 100644 --- a/libgit2/include/submodule.rkt +++ b/libgit2/include/submodule.rkt @@ -97,8 +97,7 @@ git_repository_free) (define-libgit2 git_submodule_owner - (_fun _git_submodule -> _git_repository) - #:wrap (allocator git_repository_free)) + (_fun _git_submodule -> _git_repository)) (define-libgit2 git_submodule_path (_fun _git_submodule -> _string)) diff --git a/libgit2/scribblings/blob.scrbl b/libgit2/scribblings/blob.scrbl index 34f63b8..48cda9c 100644 --- a/libgit2/scribblings/blob.scrbl +++ b/libgit2/scribblings/blob.scrbl @@ -129,7 +129,7 @@ Write an in-memory buffer to the ODB as a blob @defproc[(git_blob_owner [blob blob?]) repository?]{ - Get the repository that contains the blob. + Get the repository that contains the blob. The returned repository is borrowed and must not be freed by the caller. } diff --git a/libgit2/scribblings/commit.scrbl b/libgit2/scribblings/commit.scrbl index d92955f..3c11b6b 100644 --- a/libgit2/scribblings/commit.scrbl +++ b/libgit2/scribblings/commit.scrbl @@ -262,7 +262,7 @@ @defproc[(git_commit_owner [commit commit?]) repository?]{ - Get the repository that contains the commit. + Get the repository that contains the commit. The returned repository is borrowed and must not be freed by the caller. } diff --git a/libgit2/scribblings/submodule.scrbl b/libgit2/scribblings/submodule.scrbl index a0f26cc..cbcb754 100644 --- a/libgit2/scribblings/submodule.scrbl +++ b/libgit2/scribblings/submodule.scrbl @@ -155,7 +155,7 @@ @defproc[(git_submodule_owner [submodule submodule?]) repository?]{ - Get the containing repository for a submodule. + Get the containing repository for a submodule. The returned repository is borrowed and must not be freed by the caller. This returns a pointer to the repository that contains the submodule. This is a just a reference to the repository that was passed to the original git_submodule_lookup() call, so if that repository has been freed, then this may be a dangling reference. diff --git a/libgit2/test/test-owner-lifetimes.rkt b/libgit2/test/test-owner-lifetimes.rkt new file mode 100644 index 0000000..b6a1310 --- /dev/null +++ b/libgit2/test/test-owner-lifetimes.rkt @@ -0,0 +1,65 @@ +#lang racket + +(require "../main.rkt" + (submod "../include/repository.rkt" free) + ffi/unsafe + rackunit) + +(define (force-finalizers) + ;; DrRacket is a long-lived process and gives finalizers much more opportunity + ;; to run than a short command-line program. Force several collections here so + ;; a borrowed repository pointer that was accidentally registered as owned is + ;; likely to be finalized while its real owner is still in use. + (for ([i (in-range 4)]) + (collect-garbage))) + +(define (make-initial-commit repo) + (define index (git_repository_index repo)) + (define tree-id (git_oid_fromstr (make-string GIT_OID_HEXSZ #\0))) + (define commit-id (git_oid_fromstr (make-string GIT_OID_HEXSZ #\0))) + (define sig (git_signature_now "libgit2-racket test" "libgit2-racket@example.invalid")) + (dynamic-wind + void + (lambda () + (git_index_write_tree tree-id index) + (define tree (git_tree_lookup repo tree-id)) + (dynamic-wind + void + (lambda () + (check-equal? + (git_commit_create_v commit-id repo "HEAD" sig sig #f + "owner lifetime test" tree 0) + 0) + (git_commit_lookup repo commit-id)) + (lambda () + (git_tree_free tree)))) + (lambda () + (git_signature_free sig) + (git_index_free index)))) + +(test-case + "borrowed owner pointers do not free their repository during GC" + (define temp-dir (make-temporary-file "rkttmp-libgit2-owner~a" 'directory)) + (dynamic-wind + void + (lambda () + (define repo (git_repository_init (path->string temp-dir))) + (define commit (make-initial-commit repo)) + (dynamic-wind + void + (lambda () + ;; git_commit_owner returns a borrowed pointer. Dropping the Racket + ;; wrapper and forcing GC must not schedule git_repository_free. + (let ([owner (git_commit_owner commit)]) + (check-true (ptr-equal? owner repo))) + (force-finalizers) + + ;; The original repository and an object backed by it must both still + ;; be usable after the borrowed owner wrapper has been collected. + (check-true (string? (git_repository_path repo))) + (check-equal? (git_commit_message commit) "owner lifetime test")) + (lambda () + (git_commit_free commit) + (git_repository_free repo)))) + (lambda () + (delete-directory/files temp-dir #:must-exist? #f)))) From 62d4d96c5592be4af4e184a5703c768927765b47 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 10 Aug 2026 20:51:56 +0200 Subject: [PATCH 19/21] Make sure callbacks are not garbage collected. --- libgit2/include/proxy.rkt | 46 ++- libgit2/include/remote.rkt | 309 ++++++++++++++++-- libgit2/private/callback-keepers.rkt | 97 ++++++ .../test/test-remote-callback-lifetimes.rkt | 67 ++++ 4 files changed, 492 insertions(+), 27 deletions(-) create mode 100644 libgit2/private/callback-keepers.rkt create mode 100644 libgit2/test/test-remote-callback-lifetimes.rkt diff --git a/libgit2/include/proxy.rkt b/libgit2/include/proxy.rkt index f9dd62f..017627f 100644 --- a/libgit2/include/proxy.rkt +++ b/libgit2/include/proxy.rkt @@ -2,14 +2,23 @@ (require ffi/unsafe "transport.rkt" - (only-in "types.rkt" - _git_transport_certificate_check_cb) - "../private/base.rkt") + "../private/base.rkt" + "../private/callback-keepers.rkt") (provide (all-defined-out)) ; Types +;; These callback ctypes use an explicit keeper. The generated callback values +;; are registered while a proxy-options struct is constructed or mutated. +(define _git_proxy_credential_acquire_cb + (_fun #:keep keep-current-callback! + (_cpointer _git_credential) _string _string _uint _pointer -> _int)) + +(define _git_proxy_certificate_check_cb + (_fun #:keep keep-current-callback! + _pointer _int _string _pointer -> _int)) + (define-enum _git_proxy_t GIT_PROXY_NONE GIT_PROXY_AUTO @@ -19,12 +28,37 @@ ([version _uint] [type _git_proxy_t] [url _string] - [credentials _git_credential_acquire_cb] - [certificate_check _git_transport_certificate_check_cb] - [payload _pointer])) + [credentials _git_proxy_credential_acquire_cb] + [certificate_check _git_proxy_certificate_check_cb] + [payload _pointer]) + #:malloc-mode 'atomic-interior) (define GIT_PROXY_OPTIONS_VERSION 1) +;; define-cstruct creates the public constructor and mutators. Wrap the ones +;; that convert Racket procedures to C callbacks so the generated callback +;; values are retained by this proxy-options object. +(let ([raw make-git_proxy_options]) + (set! make-git_proxy_options + (lambda args + (call-with-new-callback-owner + (lambda () (apply raw args)))))) + +(let ([raw set-git_proxy_options-credentials!]) + (set! set-git_proxy_options-credentials! + (lambda (opts callback) + (call-with-owner-callback-keeper + opts + (lambda () (raw opts callback)))))) + +(let ([raw set-git_proxy_options-certificate_check!]) + (set! set-git_proxy_options-certificate_check! + (lambda (opts callback) + (call-with-owner-callback-keeper + opts + (lambda () (raw opts callback)))))) + + ; Functions (define-libgit2/check git_proxy_options_init diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index fb9785b..c140031 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -11,15 +11,14 @@ (submod "oid.rkt" private) (only-in "types.rkt" _git_repository - _git_transport_message_cb - _git_transport_certificate_check_cb + _git_transport _git_transfer_progress _git_transfer_progress-pointer - _git_transfer_progress_cb _git_remote _git_refspec) "../private/buffer.rkt" - "../private/base.rkt") + "../private/base.rkt" + "../private/callback-keepers.rkt") (provide (all-defined-out)) @@ -30,8 +29,41 @@ GIT_REMOTE_COMPLETION_INDEXING GIT_REMOTE_COMPLETION_ERROR))) +;; libgit2 can retain all callbacks in this structure for the lifetime of a +;; remote transport. Use an explicit #:keep hook so the generated callback +;; values can be tied to the git_remote lifetime instead of only to the +;; original Racket procedures. +(define _git_remote_sideband_progress_cb + (_fun #:keep keep-current-callback! + _string _int _pointer -> _int)) + +(define _git_remote_completion_cb + (_fun #:keep keep-current-callback! + _git_remote_completion_type _pointer -> _int)) + +(define _git_remote_credential_acquire_cb + (_fun #:keep keep-current-callback! + (_cpointer _git_credential) _string _string _uint _pointer -> _int)) + +(define _git_remote_certificate_check_cb + (_fun #:keep keep-current-callback! + _pointer _int _string _pointer -> _int)) + +(define _git_remote_transfer_progress_cb + (_fun #:keep keep-current-callback! + _git_transfer_progress-pointer _pointer -> _int)) + +(define _git_remote_update_tips_cb + (_fun #:keep keep-current-callback! + _string _git_oid-pointer _git_oid-pointer _pointer -> _int)) + +(define _git_remote_pack_progress_cb + (_fun #:keep keep-current-callback! + _int _uint32 _uint32 _pointer -> _int)) + (define _git_push_transfer_progress - (_fun _uint _uint _size _pointer -> _int)) + (_fun #:keep keep-current-callback! + _uint _uint _size _pointer -> _int)) (define-cstruct _git_push_update ([src_name _string] @@ -40,35 +72,122 @@ [dst _git_oid])) (define _push_update _git_push_update-pointer) +(define _git_push_update_reference_cb + (_fun #:keep keep-current-callback! + _string _string _pointer -> _int)) + (define _git_push_negotiation - (_fun (_cpointer _push_update) _size _pointer -> _int)) + (_fun #:keep keep-current-callback! + (_cpointer _push_update) _size _pointer -> _int)) + +(define _git_remote_transport_cb + (_fun #:keep keep-current-callback! + (_cpointer _git_transport) _git_remote _pointer -> _int)) (define _git_remote_ready_cb - (_fun _git_remote _git_direction _pointer -> _int)) + (_fun #:keep keep-current-callback! + _git_remote _git_direction _pointer -> _int)) (define _git_url_resolve_cb - (_fun _pointer _string _git_direction _pointer -> _int)) - + (_fun #:keep keep-current-callback! + _pointer _string _git_direction _pointer -> _int)) (define-cstruct _git_remote_callbacks ([version _uint] - [sideband_progress _git_transport_message_cb] - [completion (_fun _git_remote_completion_type _pointer -> _int)] - [credentials _git_credential_acquire_cb] - [certificate_check _git_transport_certificate_check_cb] - [transfer_progress _git_transfer_progress_cb] - [update_tips (_fun _string _git_oid-pointer _git_oid-pointer _pointer -> _int)] - [pack_progress _git_packbuilder_progress] + [sideband_progress _git_remote_sideband_progress_cb] + [completion _git_remote_completion_cb] + [credentials _git_remote_credential_acquire_cb] + [certificate_check _git_remote_certificate_check_cb] + [transfer_progress _git_remote_transfer_progress_cb] + [update_tips _git_remote_update_tips_cb] + [pack_progress _git_remote_pack_progress_cb] [push_transfer_progress _git_push_transfer_progress] - [push_update_reference (_fun _string _string _pointer -> _int)] + [push_update_reference _git_push_update_reference_cb] [push_negotiation _git_push_negotiation] - [transport _git_transport_cb] + [transport _git_remote_transport_cb] [remote_ready _git_remote_ready_cb] [payload _pointer] - [resolve_url _git_url_resolve_cb])) + [resolve_url _git_url_resolve_cb]) + #:malloc-mode 'atomic-interior) (define GIT_REMOTE_CB_VERSION 1) + +;; Keep generated callback values with the callback-struct object. When this +;; struct is later copied into fetch/push options, its keeper is copied to the +;; embedded destination as well. +(let ([raw make-git_remote_callbacks]) + (set! make-git_remote_callbacks + (lambda args + (call-with-new-callback-owner + (lambda () (apply raw args)))))) + +(let ([raw set-git_remote_callbacks-sideband_progress!]) + (set! set-git_remote_callbacks-sideband_progress! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-completion!]) + (set! set-git_remote_callbacks-completion! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-credentials!]) + (set! set-git_remote_callbacks-credentials! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-certificate_check!]) + (set! set-git_remote_callbacks-certificate_check! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-transfer_progress!]) + (set! set-git_remote_callbacks-transfer_progress! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-update_tips!]) + (set! set-git_remote_callbacks-update_tips! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-pack_progress!]) + (set! set-git_remote_callbacks-pack_progress! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-push_transfer_progress!]) + (set! set-git_remote_callbacks-push_transfer_progress! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-push_update_reference!]) + (set! set-git_remote_callbacks-push_update_reference! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-push_negotiation!]) + (set! set-git_remote_callbacks-push_negotiation! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-transport!]) + (set! set-git_remote_callbacks-transport! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-remote_ready!]) + (set! set-git_remote_callbacks-remote_ready! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) +(let ([raw set-git_remote_callbacks-resolve_url!]) + (set! set-git_remote_callbacks-resolve_url! + (lambda (callbacks value) + (call-with-owner-callback-keeper callbacks + (lambda () (raw callbacks value)))))) + (define _git_fetch_prune_t (_enum '(GIT_FETCH_PRUNE_UNSPECIFIED GIT_FETCH_PRUNE_PRUNE @@ -95,7 +214,8 @@ [download_tags _git_remote_autotag_option_t] [proxy_opts _git_proxy_options] [follow_redirects _git_remote_redirect_t] - [custom_headers _git_strarray])) + [custom_headers _git_strarray]) + #:malloc-mode 'atomic-interior) (define GIT_FETCH_OPTS_VERSION 1) @@ -107,15 +227,115 @@ [callbacks _git_remote_callbacks] [proxy_opts _git_proxy_options] [follow_redirects _git_remote_redirect_t] - [custom_headers _git_strarray])) + [custom_headers _git_strarray]) + #:malloc-mode 'atomic-interior) (define GIT_PUSH_OPTS_VERSION 1) + +;; Accessors for embedded callback/proxy structs are wrapped so a pointer +;; returned to user code shares the enclosing options object's callback +;; keeper. This also covers the common malloc + *_options_init + mutate-fields +;; construction style. +(let ([raw git_fetch_opts-callbacks]) + (set! git_fetch_opts-callbacks + (lambda (opts) + (define field (raw opts)) + (link-owner-callback-keeper! opts field) + field))) +(let ([raw git_fetch_opts-proxy_opts]) + (set! git_fetch_opts-proxy_opts + (lambda (opts) + (define field (raw opts)) + (link-owner-callback-keeper! opts field) + field))) +(let ([raw git_push_opts-callbacks]) + (set! git_push_opts-callbacks + (lambda (opts) + (define field (raw opts)) + (link-owner-callback-keeper! opts field) + field))) +(let ([raw git_push_opts-proxy_opts]) + (set! git_push_opts-proxy_opts + (lambda (opts) + (define field (raw opts)) + (link-owner-callback-keeper! opts field) + field))) + +;; The C structs embed callbacks/proxy options by value. Copy the generated +;; callback values to the enclosing options keeper after the raw C struct copy. +(let ([raw make-git_fetch_opts]) + (set! make-git_fetch_opts + (lambda (version callbacks prune update-fetchhead download-tags proxy-opts + follow-redirects custom-headers) + (define opts + (raw version callbacks prune update-fetchhead download-tags proxy-opts + follow-redirects custom-headers)) + (copy-owner-callback-keeper! callbacks opts) + (copy-owner-callback-keeper! proxy-opts opts) + ;; Link embedded field pointers to the options keeper. + (git_fetch_opts-callbacks opts) + (git_fetch_opts-proxy_opts opts) + opts))) + +(let ([raw set-git_fetch_opts-callbacks!]) + (set! set-git_fetch_opts-callbacks! + (lambda (opts callbacks) + (raw opts callbacks) + (copy-owner-callback-keeper! callbacks opts) + (git_fetch_opts-callbacks opts) + (void)))) + +(let ([raw set-git_fetch_opts-proxy_opts!]) + (set! set-git_fetch_opts-proxy_opts! + (lambda (opts proxy-opts) + (raw opts proxy-opts) + (copy-owner-callback-keeper! proxy-opts opts) + (git_fetch_opts-proxy_opts opts) + (void)))) + +(let ([raw make-git_push_opts]) + (set! make-git_push_opts + (lambda (version pb-parallelism callbacks proxy-opts follow-redirects + custom-headers) + (define opts + (raw version pb-parallelism callbacks proxy-opts follow-redirects + custom-headers)) + (copy-owner-callback-keeper! callbacks opts) + (copy-owner-callback-keeper! proxy-opts opts) + (git_push_opts-callbacks opts) + (git_push_opts-proxy_opts opts) + opts))) + +(let ([raw set-git_push_opts-callbacks!]) + (set! set-git_push_opts-callbacks! + (lambda (opts callbacks) + (raw opts callbacks) + (copy-owner-callback-keeper! callbacks opts) + (git_push_opts-callbacks opts) + (void)))) + +(let ([raw set-git_push_opts-proxy_opts!]) + (set! set-git_push_opts-proxy_opts! + (lambda (opts proxy-opts) + (raw opts proxy-opts) + (copy-owner-callback-keeper! proxy-opts opts) + (git_push_opts-proxy_opts opts) + (void)))) + ; Functions (define-libgit2/dealloc git_remote_free (_fun _git_remote -> _void)) +;; The allocator wrappers below register git_remote_free as the finalizer. Keep +;; callback keepers reachable until that deallocator actually runs. +(let ([raw git_remote_free]) + (set! git_remote_free + (lambda (remote) + (begin0 (raw remote) + (release-remote-callbacks! remote))))) + (define-libgit2/check git_remote_add_fetch (_fun _git_repository _string _string -> _int)) @@ -285,3 +505,50 @@ (define-libgit2/check git_push_options_init (_fun _git_push_opts-pointer _uint -> _int)) + +;; Any operation that installs connection options may copy callbacks into the +;; remote's transport. Retain the corresponding keepers until git_remote_free; +;; git_remote_disconnect intentionally does not release them because libgit2 +;; keeps the transport object attached to the remote until free. +(let ([raw git_remote_connect]) + (set! git_remote_connect + (lambda (remote direction callbacks proxy-opts custom-headers) + (retain-owner-callbacks-for-remote! remote callbacks) + (retain-owner-callbacks-for-remote! remote proxy-opts) + (raw remote direction callbacks proxy-opts custom-headers)))) + +(let ([raw git_remote_download]) + (set! git_remote_download + (lambda (remote refspecs opts) + (retain-owner-callbacks-for-remote! remote opts) + (raw remote refspecs opts)))) + +(let ([raw git_remote_fetch]) + (set! git_remote_fetch + (lambda (remote refspecs opts reflog-message) + (retain-owner-callbacks-for-remote! remote opts) + (raw remote refspecs opts reflog-message)))) + +(let ([raw git_remote_prune]) + (set! git_remote_prune + (lambda (remote callbacks) + (retain-owner-callbacks-for-remote! remote callbacks) + (raw remote callbacks)))) + +(let ([raw git_remote_push]) + (set! git_remote_push + (lambda (remote refspecs opts) + (retain-owner-callbacks-for-remote! remote opts) + (raw remote refspecs opts)))) + +(let ([raw git_remote_update_tips]) + (set! git_remote_update_tips + (lambda (remote callbacks update-fetchhead download-tags reflog-message) + (retain-owner-callbacks-for-remote! remote callbacks) + (raw remote callbacks update-fetchhead download-tags reflog-message)))) + +(let ([raw git_remote_upload]) + (set! git_remote_upload + (lambda (remote refspecs opts) + (retain-owner-callbacks-for-remote! remote opts) + (raw remote refspecs opts)))) diff --git a/libgit2/private/callback-keepers.rkt b/libgit2/private/callback-keepers.rkt new file mode 100644 index 0000000..6bc90c4 --- /dev/null +++ b/libgit2/private/callback-keepers.rkt @@ -0,0 +1,97 @@ +#lang racket/base + +(require ffi/unsafe) + +(provide call-with-new-callback-owner + call-with-owner-callback-keeper + copy-owner-callback-keeper! + link-owner-callback-keeper! + keep-current-callback! + retain-owner-callbacks-for-remote! + release-remote-callbacks!) + +;; Callback values generated by _fun are ordinary Racket values. If foreign +;; code retains their C function pointers, those callback values must remain +;; reachable until the foreign owner is released. +;; +;; Owner keys are late weak so automatic FFI finalizers can still run. A late +;; weak key remains available until its registered finalizer has been +;; processed, which is exactly the lifetime required by git_remote. +(define (make-empty-late-weak-hasheq) + ;; Racket 9.2 exposes the zero-argument form; newer snapshots also accept an + ;; initial value. Keep the binding usable across both APIs. + (if (procedure-arity-includes? make-late-weak-hasheq 0) + (make-late-weak-hasheq) + (make-late-weak-hasheq '()))) + +(define callback-owner-keepers (make-empty-late-weak-hasheq)) +(define remote-callback-keepers (make-empty-late-weak-hasheq)) + +(define current-callback-keeper (make-parameter #f)) + +(define (make-callback-keeper) + ;; A list-valued box makes _fun #:keep retain every generated callback. + (box '())) + +(define (keep-in-box! keeper callback) + (unless (memq callback (unbox keeper)) + (set-box! keeper (cons callback (unbox keeper))))) + +(define (keep-current-callback! callback) + (define keeper (current-callback-keeper)) + (unless keeper + (error 'keep-current-callback! + "callback converted outside a managed callback owner")) + (keep-in-box! keeper callback)) + +(define (call-with-new-callback-owner make-owner) + (define keeper (make-callback-keeper)) + (define owner + (parameterize ([current-callback-keeper keeper]) + (make-owner))) + (hash-set! callback-owner-keepers owner keeper) + owner) + +(define (ensure-owner-callback-keeper! owner) + (hash-ref callback-owner-keepers + owner + (lambda () + (define keeper (make-callback-keeper)) + (hash-set! callback-owner-keepers owner keeper) + keeper))) + +(define (call-with-owner-callback-keeper owner thunk) + (define keeper (ensure-owner-callback-keeper! owner)) + (parameterize ([current-callback-keeper keeper]) + (thunk))) + +(define (copy-owner-callback-keeper! source destination) + ;; A C struct assignment copies function-pointer bytes, not Racket GC links. + ;; Mirror the currently retained callback values into the destination owner. + (when (and source destination) + (define source-keeper (hash-ref callback-owner-keepers source #f)) + (when source-keeper + (define destination-keeper + (ensure-owner-callback-keeper! destination)) + (for ([callback (in-list (unbox source-keeper))]) + (keep-in-box! destination-keeper callback))))) + +(define (link-owner-callback-keeper! owner field) + ;; Accessors for embedded cstruct fields return pointers into the enclosing + ;; owner. Give that pointer the same keeper, so later field mutations add + ;; callbacks to the enclosing options object's lifetime. + (when (and owner field) + (define keeper (ensure-owner-callback-keeper! owner)) + (hash-set! callback-owner-keepers field keeper))) + +(define (retain-owner-callbacks-for-remote! remote owner) + (when (and remote owner) + (define keeper (hash-ref callback-owner-keepers owner #f)) + (when keeper + (define keepers (hash-ref remote-callback-keepers remote '())) + (unless (memq keeper keepers) + (hash-set! remote-callback-keepers remote (cons keeper keepers)))))) + +(define (release-remote-callbacks! remote) + (when remote + (hash-remove! remote-callback-keepers remote))) diff --git a/libgit2/test/test-remote-callback-lifetimes.rkt b/libgit2/test/test-remote-callback-lifetimes.rkt new file mode 100644 index 0000000..c29d632 --- /dev/null +++ b/libgit2/test/test-remote-callback-lifetimes.rkt @@ -0,0 +1,67 @@ +#lang racket/base + +(require ffi/unsafe + rackunit + racket/file + libgit2) + +(define (make-push-options-with-marker marker) + (define opts + (cast (malloc (ctype-sizeof _git_push_opts) 'atomic-interior) + _pointer + _git_push_opts-pointer)) + (git_push_options_init opts GIT_PUSH_OPTS_VERSION) + (define callbacks (git_push_opts-callbacks opts)) + (set-git_remote_callbacks-sideband_progress! + callbacks + (lambda (message length payload) + (void message length payload) + (if (eq? (vector-ref marker 0) 'callback-still-alive) 0 -1))) + opts) + +(module+ test + (test-case "remote retains callbacks after push options go out of scope" + (define tmp (make-temporary-file "libgit2-remote-callback-test-~a" 'directory)) + (dynamic-wind + void + (lambda () + (define repo (git_repository_init tmp)) + (define remote (git_remote_create repo "origin" "invalid://example/repo.git")) + + (define marker-weak + (let () + (define marker (vector 'callback-still-alive)) + (define opts (make-push-options-with-marker marker)) + (with-handlers ([exn:fail? void]) + (git_remote_push remote #f opts)) + (make-weak-box marker))) + + (collect-garbage) + (collect-garbage) + (check-not-false (weak-box-value marker-weak)) + (check-not-exn (lambda () (git_remote_free remote)))) + (lambda () + (delete-directory/files tmp)))) + + (test-case "automatic remote finalization keeps callbacks alive through cleanup" + (define tmp (make-temporary-file "libgit2-remote-finalizer-test-~a" 'directory)) + (dynamic-wind + void + (lambda () + (let () + (define repo (git_repository_init tmp)) + (define remote (git_remote_create repo "origin" "invalid://example/repo.git")) + (define marker (vector 'callback-still-alive)) + (define opts (make-push-options-with-marker marker)) + (with-handlers ([exn:fail? void]) + (git_remote_push remote #f opts)) + (void)) + ;; No explicit git_remote_free: exercise the allocator finalizer and + ;; the late-weak keeper registry together. + (check-not-exn + (lambda () + (collect-garbage) + (collect-garbage) + (collect-garbage)))) + (lambda () + (delete-directory/files tmp))))) From 4ca7b2f04e9e27befbb3250b4524304a39797e10 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Tue, 11 Aug 2026 08:12:39 +0200 Subject: [PATCH 20/21] fix double frees when using credentials. --- libgit2/include/diff.rkt | 4 +- libgit2/include/transport.rkt | 56 +++++++++---------- libgit2/scribblings/cred.scrbl | 4 ++ libgit2/scribblings/diff.scrbl | 4 +- libgit2/test/test-credential-ownership.rkt | 36 +++++++++++++ libgit2/test/test-diff.rkt | 62 +++++++++++++++++++++- 6 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 libgit2/test/test-credential-ownership.rkt diff --git a/libgit2/include/diff.rkt b/libgit2/include/diff.rkt index 1c010f2..53a954b 100644 --- a/libgit2/include/diff.rkt +++ b/libgit2/include/diff.rkt @@ -42,6 +42,8 @@ [GIT_DIFF_UPDATE_INDEX = #x00008000] [GIT_DIFF_INCLUDE_UNREADABLE = #x00010000] [GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = #x00020000] + [GIT_DIFF_INDENT_HEURISTIC = #x00040000] + [GIT_DIFF_IGNORE_BLANK_LINES = #x00080000] [GIT_DIFF_FORCE_TEXT = #x00100000] [GIT_DIFF_FORCE_BINARY = #x00200000] [GIT_DIFF_IGNORE_WHITESPACE = #x00400000] @@ -205,7 +207,7 @@ [payload _pointer])) (define-cstruct _git_diff_find_options - ([version _int] + ([version _uint] [flags _uint32] [rename_threshold _uint16] [rename_from_rewrite_threshold _uint16] diff --git a/libgit2/include/transport.rkt b/libgit2/include/transport.rkt index 5d125d2..01e9eb7 100644 --- a/libgit2/include/transport.rkt +++ b/libgit2/include/transport.rkt @@ -108,41 +108,43 @@ (define-libgit2/dealloc git_credential_free (_fun _git_credential -> _void)) -(define-libgit2/alloc git_credential_default_new - (_fun _git_credential -> _int) - git_credential_free) +;; Credential objects are special: when returned from an authentication +;; callback, ownership is transferred to libgit2. Do not register these +;; results with Racket's FFI allocator, because libgit2 will free them and a +;; later Racket finalizer would otherwise free them a second time. +(define-syntax-rule (define-credential-constructor name (_fun _git_credential arg ... -> _int)) + (define-libgit2 name + (_fun [out : (_ptr o _git_credential)] + arg ... + -> (_git_error_code/check) + -> out))) + +(define-credential-constructor git_credential_default_new + (_fun _git_credential -> _int)) (define-libgit2 git_credential_has_username (_fun _git_credential -> _bool)) -(define-libgit2/alloc git_credential_ssh_custom_new - (_fun _git_credential _string _string _size _git_credential_sign_callback _pointer -> _int) - git_credential_free) +(define-credential-constructor git_credential_ssh_custom_new + (_fun _git_credential _string _string _size _git_credential_sign_callback _pointer -> _int)) -(define-libgit2/alloc git_credential_ssh_interactive_new - (_fun _git_credential _string _git_credential_ssh_interactive_callback _pointer -> _int) - git_credential_free) +(define-credential-constructor git_credential_ssh_interactive_new + (_fun _git_credential _string _git_credential_ssh_interactive_callback _pointer -> _int)) -(define-libgit2/alloc git_credential_ssh_key_from_agent - (_fun _git_credential _string -> _int) - git_credential_free) +(define-credential-constructor git_credential_ssh_key_from_agent + (_fun _git_credential _string -> _int)) -(define-libgit2/alloc git_credential_ssh_key_memory_new - (_fun _git_credential _string _string _string _string -> _int) - git_credential_free) +(define-credential-constructor git_credential_ssh_key_memory_new + (_fun _git_credential _string _string _string _string -> _int)) -(define-libgit2/alloc git_credential_ssh_key_new - (_fun _git_credential _string _string _string _string -> _int) - git_credential_free) +(define-credential-constructor git_credential_ssh_key_new + (_fun _git_credential _string _string _string _string -> _int)) -(define-libgit2/alloc git_credential_username_new - (_fun _git_credential _string -> _int) - git_credential_free) +(define-credential-constructor git_credential_username_new + (_fun _git_credential _string -> _int)) -(define-libgit2/alloc git_credential_userpass - (_fun _git_credential _string _string _uint _pointer -> _int) - git_credential_free) +(define-credential-constructor git_credential_userpass + (_fun _git_credential _string _string _uint _pointer -> _int)) -(define-libgit2/alloc git_credential_userpass_plaintext_new - (_fun _git_credential _string _string -> _int) - git_credential_free) +(define-credential-constructor git_credential_userpass_plaintext_new + (_fun _git_credential _string _string -> _int)) diff --git a/libgit2/scribblings/cred.scrbl b/libgit2/scribblings/cred.scrbl index a3a1bc3..a5de0b3 100644 --- a/libgit2/scribblings/cred.scrbl +++ b/libgit2/scribblings/cred.scrbl @@ -6,6 +6,10 @@ @defmodule[libgit2/include/cred] +Credential objects have transferable ownership. When a credential is returned +from an authentication callback, libgit2 takes ownership and frees it. If a +credential is created but not transferred to libgit2, the Racket caller must +free it explicitly with @racket[git_cred_free]. @defproc[(git_cred_default_new) cred?]{ diff --git a/libgit2/scribblings/diff.scrbl b/libgit2/scribblings/diff.scrbl index 377d0ef..9d6ecea 100644 --- a/libgit2/scribblings/diff.scrbl +++ b/libgit2/scribblings/diff.scrbl @@ -86,9 +86,9 @@ } -@defproc[(git_diff_find_init_options +@defproc[(git_diff_find_options_init [opts git_diff_find_options?] - [int unsigned]) + [version exact-nonnegative-integer?]) integer?]{ Initializes a git_diff_find_options with default values. Equivalent to creating an instance with GIT_DIFF_FIND_OPTIONS_INIT. diff --git a/libgit2/test/test-credential-ownership.rkt b/libgit2/test/test-credential-ownership.rkt new file mode 100644 index 0000000..b22cadb --- /dev/null +++ b/libgit2/test/test-credential-ownership.rkt @@ -0,0 +1,36 @@ +#lang racket/base + +(require rackunit + ffi/unsafe + "../include/transport.rkt" + "../private/base.rkt") + +;; Simulate what libgit2 does with a credential returned by an authentication +;; callback: it owns and frees the credential without going through Racket's +;; git_credential_free wrapper. +(define-libgit2 raw-git-credential-free + #:c-id git_credential_free + (_fun _git_credential -> _void)) + +(test-case "credential ownership can be transferred to libgit2" + (define cred + (git_credential_userpass_plaintext_new "user" "password")) + + (raw-git-credential-free cred) + (set! cred #f) + + ;; Before the ownership fix, the first GC runs the allocator finalizer and + ;; frees the already-freed credential a second time, crashing Racket. + (collect-garbage) + (collect-garbage) + (collect-garbage) + + (check-true #t)) + +(test-case "credential can still be explicitly freed by its Racket owner" + (define cred + (git_credential_userpass_plaintext_new "user" "password")) + (git_credential_free cred) + (set! cred #f) + (collect-garbage) + (check-true #t)) diff --git a/libgit2/test/test-diff.rkt b/libgit2/test/test-diff.rkt index d1369d0..0e05190 100644 --- a/libgit2/test/test-diff.rkt +++ b/libgit2/test/test-diff.rkt @@ -54,4 +54,64 @@ (git_index_free index) (git_repository_free repo)) (lambda () - (delete-directory/files repo-dir #:must-exist? #f))))) + (delete-directory/files repo-dir #:must-exist? #f))) + + (test-case + "git_diff_find_similar detects a rename" + (define repo-dir (make-temporary-file "rkttmp-libgit2-find-similar~a" 'directory)) + (dynamic-wind + void + (lambda () + (define old-file (build-path repo-dir "old.txt")) + (define new-file (build-path repo-dir "new.txt")) + (define repo (git_repository_init repo-dir)) + (define index (git_repository_index repo)) + + (call-with-output-file old-file + #:exists 'truncate + (lambda (out) (display "same content\n" out))) + (git_index_add_bypath index "old.txt") + (git_index_write index) + (rename-file-or-directory old-file new-file) + + (define diff-opts + (cast (malloc _git_diff_opts 'raw) _pointer _git_diff_opts-pointer)) + (git_diff_options_init diff-opts GIT_DIFF_OPTS_VERSION) + ;; INCLUDE_UNTRACKED | RECURSE_UNTRACKED_DIRS + (set-git_diff_opts-flags! diff-opts #x18) + (define diff (git_diff_index_to_workdir repo index diff-opts)) + (check-equal? (git_diff_num_deltas diff) 2) + + (define find-opts + (cast (malloc _git_diff_find_options 'raw) + _pointer + _git_diff_find_options-pointer)) + (git_diff_find_options_init find-opts GIT_DIFF_FIND_OPTS_VERSION) + (check-equal? (git_diff_find_options-version find-opts) + GIT_DIFF_FIND_OPTS_VERSION) + ;; FIND_RENAMES | FIND_FOR_UNTRACKED + (set-git_diff_find_options-flags! find-opts #x41) + (git_diff_find_similar diff find-opts) + + (check-equal? (git_diff_num_deltas diff) 1) + (define delta (git_diff_get_delta diff 0)) + (check-equal? (git_diff_delta-status delta) 'GIT_DELTA_RENAMED) + (check-equal? (git_diff_delta-similarity delta) 100) + (check-equal? (git_diff_file-path (git_diff_delta-old_file delta)) "old.txt") + (check-equal? (git_diff_file-path (git_diff_delta-new_file delta)) "new.txt") + + (free find-opts) + (git_diff_free diff) + (free diff-opts) + (git_index_free index) + (git_repository_free repo)) + (lambda () + (delete-directory/files repo-dir #:must-exist? #f)))) + + (test-case + "libgit2 1.4 diff flags are available" + (check-equal? (cast 'GIT_DIFF_INDENT_HEURISTIC _git_diff_option_t _uint32) + #x00040000) + (check-equal? (cast 'GIT_DIFF_IGNORE_BLANK_LINES _git_diff_option_t _uint32) + #x00080000)) +)) From 02e3577155c629bf686eae8b109dd93b284a3458 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Tue, 11 Aug 2026 11:06:12 +0200 Subject: [PATCH 21/21] Safe callback mechanism to give dependent packages a normal racket environment. --- Makefile.rkt | 21 ++++++++++ libgit2/include/remote.rkt | 56 +++++++++++++++++++++++++ libgit2/test/test-remote-signatures.rkt | 12 ++++++ 3 files changed, 89 insertions(+) create mode 100644 Makefile.rkt diff --git a/Makefile.rkt b/Makefile.rkt new file mode 100644 index 0000000..283b514 --- /dev/null +++ b/Makefile.rkt @@ -0,0 +1,21 @@ +#lang racket + +(require racket-makefile + package-zipper + ) + + +(target all + (displayln "use (make clean) or (make package)") + ) + +(target clean + (for-each (λ (f) (displayln f) (rm-f f)) (list-files "libgit2" #px"([.]bak|~)$" #:recursive #t)) + (for-each (λ (d) (displayln d) (rm-rf d)) (list-dirs "libgit2" #px"(compiled|doc)$" #:recursive #t)) + (for-each (λ (f) (displayln f) (rm-f f)) (list-files "libgit2/scribblings" #px"[.](css|js|html)$")) + ) + +(target package + (deps clean) + (zip-package)) + \ No newline at end of file diff --git a/libgit2/include/remote.rkt b/libgit2/include/remote.rkt index c140031..f3955a0 100644 --- a/libgit2/include/remote.rkt +++ b/libgit2/include/remote.rkt @@ -1,6 +1,7 @@ #lang racket (require ffi/unsafe + ffi/unsafe/os-async-channel (only-in "net.rkt" _git_direction _git_remote_head) @@ -29,6 +30,24 @@ GIT_REMOTE_COMPLETION_INDEXING GIT_REMOTE_COMPLETION_ERROR))) +;; Blocking remote callouts allow other Racket threads to run while libgit2 is +;; doing network/pack work. Racket requires callbacks entered from such a +;; callout to have a non-#f #:async-apply. Queue callback thunks to an ordinary +;; Racket thread so callback bodies may safely perform normal Racket work. +(define remote-callback-channel (make-os-async-channel)) + +(define remote-callback-dispatch-thread + (thread + (lambda () + (let loop () + (define thunk (sync remote-callback-channel)) + (thunk) + (loop))))) + +(define (remote-callback-async-apply thunk) + ;; os-async-channel-put is valid in atomic mode and from an OS thread. + (os-async-channel-put remote-callback-channel thunk)) + ;; libgit2 can retain all callbacks in this structure for the lifetime of a ;; remote transport. Use an explicit #:keep hook so the generated callback ;; values can be tied to the git_remote lifetime instead of only to the @@ -43,6 +62,7 @@ (define _git_remote_credential_acquire_cb (_fun #:keep keep-current-callback! + #:async-apply remote-callback-async-apply (_cpointer _git_credential) _string _string _uint _pointer -> _int)) (define _git_remote_certificate_check_cb @@ -51,6 +71,7 @@ (define _git_remote_transfer_progress_cb (_fun #:keep keep-current-callback! + #:async-apply remote-callback-async-apply _git_transfer_progress-pointer _pointer -> _int)) (define _git_remote_update_tips_cb @@ -59,10 +80,12 @@ (define _git_remote_pack_progress_cb (_fun #:keep keep-current-callback! + #:async-apply remote-callback-async-apply _int _uint32 _uint32 _pointer -> _int)) (define _git_push_transfer_progress (_fun #:keep keep-current-callback! + #:async-apply remote-callback-async-apply _uint _uint _size _pointer -> _int)) (define-cstruct _git_push_update @@ -395,6 +418,18 @@ _string -> _int)) +;; Blocking variant for callers that need other Racket threads to remain +;; schedulable while libgit2 fetches. Callbacks used with this variant must +;; have a non-#f #:async-apply. +(define-libgit2 git_remote_fetch/blocking + (_fun #:blocking? #t + _git_remote + (_or-null _git_strarray-pointer) + _git_fetch_opts-pointer/null + _string + -> (_git_error_code/check)) + #:c-id git_remote_fetch) + (define-libgit2 git_remote_get_fetch_refspecs (_fun [lst : (_git_strarray-pointer/alloc)] _git_remote @@ -454,6 +489,15 @@ _git_push_opts-pointer/null -> _int)) +;; Blocking variant; see git_remote_fetch/blocking. +(define-libgit2 git_remote_push/blocking + (_fun #:blocking? #t + _git_remote + (_or-null _git_strarray-pointer) + _git_push_opts-pointer/null + -> (_git_error_code/check)) + #:c-id git_remote_push) + (define-libgit2 git_remote_pushurl (_fun _git_remote -> _string)) @@ -529,6 +573,12 @@ (retain-owner-callbacks-for-remote! remote opts) (raw remote refspecs opts reflog-message)))) +(let ([raw git_remote_fetch/blocking]) + (set! git_remote_fetch/blocking + (lambda (remote refspecs opts reflog-message) + (retain-owner-callbacks-for-remote! remote opts) + (raw remote refspecs opts reflog-message)))) + (let ([raw git_remote_prune]) (set! git_remote_prune (lambda (remote callbacks) @@ -541,6 +591,12 @@ (retain-owner-callbacks-for-remote! remote opts) (raw remote refspecs opts)))) +(let ([raw git_remote_push/blocking]) + (set! git_remote_push/blocking + (lambda (remote refspecs opts) + (retain-owner-callbacks-for-remote! remote opts) + (raw remote refspecs opts)))) + (let ([raw git_remote_update_tips]) (set! git_remote_update_tips (lambda (remote callbacks update-fetchhead download-tags reflog-message) diff --git a/libgit2/test/test-remote-signatures.rkt b/libgit2/test/test-remote-signatures.rkt index 9335ea0..bb9049a 100644 --- a/libgit2/test/test-remote-signatures.rkt +++ b/libgit2/test/test-remote-signatures.rkt @@ -46,6 +46,18 @@ (lambda () (git_remote_fetch remote #f #f #f))) + ;; Blocking variants use the same C entry points but allow a caller to + ;; execute them in a parallel Racket thread while callbacks are safely + ;; dispatched through #:async-apply. + (check-exn + exn:fail? + (lambda () + (git_remote_fetch/blocking remote #f #f #f))) + (check-exn + exn:fail? + (lambda () + (git_remote_push/blocking remote #f #f))) + ;; Fetch refspec output is owned by the caller and must be consumed just ;; like the push-refspec output. (check-equal?