#include "_cgo_export.h" #include #include #include #include // There are two ways in which to declare a callback: // // * If there is a guarantee that the callback will always be called within the // same stack (e.g. by passing the callback directly into a function / into a // struct that goes into a function), the following pattern is preferred, // which preserves the error object as-is: // // // myfile.go // type FooCallback func(...) (..., error) // type FooCallbackData struct { // callback FooCallback // errorTarget *error // } // // //export fooCallback // func fooCallback(..., handle unsafe.Pointer) C.int { // payload := pointerHandles.Get(handle) // data := payload.(*fooCallbackData) // ... // err := data.callback(...) // if err != nil { // *data.errorTarget = err // return C.int(ErrorCodeUser) // } // return C.int(ErrorCodeOK) // } // // func MyFunction(... callback FooCallback) error { // var err error // data := fooCallbackData{ // callback: callback, // errorTarget: &err, // } // handle := pointerHandles.Track(&data) // defer pointerHandles.Untrack(handle) // // runtime.LockOSThread() // defer runtime.UnlockOSThread() // // ret := C._go_git_my_function(..., handle) // if ret == C.int(ErrorCodeUser) && err != nil { // return err // } // if ret < 0 { // return MakeGitError(ret) // } // return nil // } // // // wrapper.c // int _go_git_my_function(..., void *payload) // { // return git_my_function(..., (git_foo_cb)&fooCallback, payload); // } // // * Otherwise, if the same callback can be invoked from multiple functions or // from different stacks (e.g. when passing the callback to an object), a // different pattern should be used instead, which has the downside of losing // the original error object and converting it to a GitError: // // // myfile.go // type FooCallback func(...) (..., error) // // //export fooCallback // func fooCallback(errorMessage **C.char, ..., handle unsafe.Pointer) C.int { // callback := pointerHandles.Get(data).(*FooCallback) // ... // err := callback(...) // if err != nil { // return setCallbackError(errorMessage, err) // } // return C.int(ErrorCodeOK) // } // // // wrapper.c // static int foo_callback(...) // { // char *error_message = NULL; // const int ret = fooCallback(&error_message, ...); // return set_callback_error(error_message, ret); // } /** * Sets the thread-local error to the provided string. This needs to happen in * C because Go might change Goroutines _just_ before returning, which would * lose the contents of the error message. */ static int set_callback_error(char *error_message, int ret) { if (error_message != NULL) { if (ret < 0) git_error_set_str(GIT_ERROR_CALLBACK, error_message); free(error_message); } return ret; } void _go_git_populate_apply_callbacks(git_apply_options *options) { options->delta_cb = (git_apply_delta_cb)&deltaApplyCallback; options->hunk_cb = (git_apply_hunk_cb)&hunkApplyCallback; } static int commit_signing_callback( git_buf *signature, git_buf *signature_field, const char *commit_contents, void *payload) { char *error_message = NULL; const int ret = commitSigningCallback( &error_message, signature, signature_field, (char *)commit_contents, payload ); return set_callback_error(error_message, ret); } void _go_git_populate_rebase_callbacks(git_rebase_options *opts) { opts->signing_cb = commit_signing_callback; } void _go_git_populate_clone_callbacks(git_clone_options *opts) { opts->remote_cb = (git_remote_create_cb)&remoteCreateCallback; } void _go_git_populate_checkout_callbacks(git_checkout_options *opts) { opts->notify_cb = (git_checkout_notify_cb)&checkoutNotifyCallback; opts->progress_cb = (git_checkout_progress_cb)&checkoutProgressCallback; } int _go_git_visit_submodule(git_repository *repo, void *fct) { return git_submodule_foreach(repo, (git_submodule_cb)&submoduleCallback, fct); } int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr) { return git_tree_walk(tree, mode, (git_treewalk_cb)&treeWalkCallback, ptr); } int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload) { return git_packbuilder_foreach(pb, (git_packbuilder_foreach_cb)&packbuilderForEachCallback, payload); } int _go_git_odb_foreach(git_odb *db, void *payload) { return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCallback, payload); } void _go_git_odb_backend_free(git_odb_backend *backend) { if (!backend->free) return; backend->free(backend); } void _go_git_refdb_backend_free(git_refdb_backend *backend) { if (!backend->free) return; backend->free(backend); } int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload) { git_diff_file_cb fcb = NULL; git_diff_hunk_cb hcb = NULL; git_diff_line_cb lcb = NULL; if (eachFile) fcb = (git_diff_file_cb)&diffForEachFileCallback; if (eachHunk) hcb = (git_diff_hunk_cb)&diffForEachHunkCallback; if (eachLine) lcb = (git_diff_line_cb)&diffForEachLineCallback; return git_diff_foreach(diff, fcb, NULL, hcb, lcb, payload); } int _go_git_diff_blobs( git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload) { git_diff_file_cb fcb = NULL; git_diff_hunk_cb hcb = NULL; git_diff_line_cb lcb = NULL; if (eachFile) fcb = (git_diff_file_cb)&diffForEachFileCallback; if (eachHunk) hcb = (git_diff_hunk_cb)&diffForEachHunkCallback; if (eachLine) lcb = (git_diff_line_cb)&diffForEachLineCallback; return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload); } void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { opts->notify_cb = (git_diff_notify_cb)&diffNotifyCallback; } static int sideband_progress_callback(const char *str, int len, void *payload) { char *error_message = NULL; const int ret = sidebandProgressCallback(&error_message, (char *)str, len, payload); return set_callback_error(error_message, ret); } static int completion_callback(git_remote_completion_type completion_type, void *data) { char *error_message = NULL; const int ret = completionCallback(&error_message, completion_type, data); return set_callback_error(error_message, ret); } static int credentials_callback( git_credential **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) { char *error_message = NULL; const int ret = credentialsCallback( &error_message, cred, (char *)url, (char *)username_from_url, allowed_types, data ); return set_callback_error(error_message, ret); } static int transfer_progress_callback(const git_transfer_progress *stats, void *data) { char *error_message = NULL; const int ret = transferProgressCallback( &error_message, (git_transfer_progress *)stats, data ); return set_callback_error(error_message, ret); } static int update_tips_callback(const char *refname, const git_oid *a, const git_oid *b, void *data) { char *error_message = NULL; const int ret = updateTipsCallback( &error_message, (char *)refname, (git_oid *)a, (git_oid *)b, data ); return set_callback_error(error_message, ret); } static int certificate_check_callback(git_cert *cert, int valid, const char *host, void *data) { char *error_message = NULL; const int ret = certificateCheckCallback( &error_message, cert, valid, (char *)host, data ); return set_callback_error(error_message, ret); } static int pack_progress_callback(int stage, unsigned int current, unsigned int total, void *data) { char *error_message = NULL; const int ret = packProgressCallback( &error_message, stage, current, total, data ); return set_callback_error(error_message, ret); } static int push_transfer_progress_callback( unsigned int current, unsigned int total, size_t bytes, void *data) { char *error_message = NULL; const int ret = pushTransferProgressCallback( &error_message, current, total, bytes, data ); return set_callback_error(error_message, ret); } static int push_update_reference_callback(const char *refname, const char *status, void *data) { char *error_message = NULL; const int ret = pushUpdateReferenceCallback( &error_message, (char *)refname, (char *)status, data ); return set_callback_error(error_message, ret); } void _go_git_populate_remote_callbacks(git_remote_callbacks *callbacks) { callbacks->sideband_progress = sideband_progress_callback; callbacks->completion = completion_callback; callbacks->credentials = credentials_callback; callbacks->transfer_progress = transfer_progress_callback; callbacks->update_tips = update_tips_callback; callbacks->certificate_check = certificate_check_callback; callbacks->pack_progress = pack_progress_callback; callbacks->push_transfer_progress = push_transfer_progress_callback; callbacks->push_update_reference = push_update_reference_callback; } int _go_git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; return git_index_add_all(index, pathspec, flags, cb, callback); } int _go_git_index_update_all(git_index *index, const git_strarray *pathspec, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; return git_index_update_all(index, pathspec, cb, callback); } int _go_git_index_remove_all(git_index *index, const git_strarray *pathspec, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb)&indexMatchedPathCallback : NULL; return git_index_remove_all(index, pathspec, cb, callback); } int _go_git_tag_foreach(git_repository *repo, void *payload) { return git_tag_foreach(repo, (git_tag_foreach_cb)&tagForeachCallback, payload); } int _go_git_merge_file( git_merge_file_result* out, char* ancestorContents, size_t ancestorLen, char* ancestorPath, unsigned int ancestorMode, char* oursContents, size_t oursLen, char* oursPath, unsigned int oursMode, char* theirsContents, size_t theirsLen, char* theirsPath, unsigned int theirsMode, git_merge_file_options* copts) { git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT; git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT; ancestor.ptr = ancestorContents; ancestor.size = ancestorLen; ancestor.path = ancestorPath; ancestor.mode = ancestorMode; ours.ptr = oursContents; ours.size = oursLen; ours.path = oursPath; ours.mode = oursMode; theirs.ptr = theirsContents; theirs.size = theirsLen; theirs.path = theirsPath; theirs.mode = theirsMode; return git_merge_file(out, &ancestor, &ours, &theirs, copts); } void _go_git_populate_stash_apply_callbacks(git_stash_apply_options *opts) { opts->progress_cb = (git_stash_apply_progress_cb)&stashApplyProgressCallback; } int _go_git_stash_foreach(git_repository *repo, void *payload) { return git_stash_foreach(repo, (git_stash_cb)&stashForeachCallback, payload); } int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len) { return stream->write(stream, buffer, len); } int _go_git_writestream_close(git_writestream *stream) { return stream->close(stream); } void _go_git_writestream_free(git_writestream *stream) { stream->free(stream); } git_credential_t _go_git_credential_credtype(git_credential *cred) { return cred->credtype; } static int credential_ssh_sign_callback( LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract) { char *error_message = NULL; const int ret = credentialSSHSignCallback( &error_message, sig, sig_len, (unsigned char *)data, data_len, (void *)*(uintptr_t *)abstract); return set_callback_error(error_message, ret); } void _go_git_populate_credential_ssh_custom(git_credential_ssh_custom *cred) { cred->parent.free = (void (*)(git_credential *))credentialSSHCustomFree; cred->sign_callback = credential_ssh_sign_callback; } int _go_git_odb_write_pack(git_odb_writepack **out, git_odb *db, void *progress_payload) { return git_odb_write_pack(out, db, transfer_progress_callback, progress_payload); } int _go_git_odb_writepack_append( git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats) { return writepack->append(writepack, data, size, stats); } int _go_git_odb_writepack_commit(git_odb_writepack *writepack, git_transfer_progress *stats) { return writepack->commit(writepack, stats); } void _go_git_odb_writepack_free(git_odb_writepack *writepack) { writepack->free(writepack); } int _go_git_indexer_new( git_indexer **out, const char *path, unsigned int mode, git_odb *odb, void *progress_cb_payload) { git_indexer_options indexer_options = GIT_INDEXER_OPTIONS_INIT; indexer_options.progress_cb = transfer_progress_callback; indexer_options.progress_cb_payload = progress_cb_payload; return git_indexer_new(out, path, mode, odb, &indexer_options); }