git2go/wrapper.c

476 lines
13 KiB
C

#include "_cgo_export.h"
#include <git2.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/refdb_backend.h>
#include <git2/sys/cred.h>
// 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;
}
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);
}