Introduce the basic warnings interface

This provides the base for how we will report warnings to the
caller. The warnins are global and we pass in a pointer to a
library-allocated struct which the caller can copy if they wish.
diff --git a/include/git2.h b/include/git2.h
index ac4a631..919ac84 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -61,5 +61,6 @@
 #include "git2/tree.h"
 #include "git2/types.h"
 #include "git2/version.h"
+#include "git2/warning.h"
 
 #endif
diff --git a/include/git2/warning.h b/include/git2/warning.h
new file mode 100644
index 0000000..3c25f62
--- /dev/null
+++ b/include/git2/warning.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_warning_h__
+#define INCLUDE_git_warning_h__
+
+#include "common.h"
+#include "types.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * The kind of warning that was generated.
+ */
+typedef enum {
+	/**
+	 * Sentinel value. Should never be used.
+	 */
+	GIT_GENERIC_NONE = 0,
+
+	/**
+	 * Generic warning. There is no extended information
+	 * available.
+	 */
+	GIT_WARNING_GENERIC,
+} git_warning_t;
+
+/**
+ * Base struct for warnings.
+
+ * These fields are always available for warnings.
+ */
+typedef struct {
+	/**
+	 * The kind of warning.
+	 */
+	git_warning_t type;
+
+	/**
+	 * The text for this warning
+	 */
+	const char *str;
+} git_warning;
+
+/**
+ * User-specified callback for warnings
+ *
+ * The warning object is owned by the library and may be deallocated
+ * on function return. Make a copy if you want to store the data for
+ * later processing. Do not attempt to free it.
+ *
+ * Note that this may be called concurrently from multiple threads.
+ *
+ * @param warning the current warning
+ * @param payload user-provided payload when registering
+ * @return 0 to continue, a negative number to stop processing
+ */
+typedef int (*git_warning_cb)(git_warning *warning, void *payload);
+
+/**
+ * Set the warning callback
+ *
+ * This sets the global warning callback which be called in places
+ * where issues were found which might be of interest to a user but
+ * would not cause an error to be returned.
+ *
+ * This function does not perform locking. Do not call it
+ * concurrently.
+ *
+ * @param callback the function to call; pass `NULL` to unregister
+ * @param payload user-specified data to be passed to the callback
+ */
+GIT_EXTERN(int) git_warning_set_callback(git_warning_cb callback, void *payload);
+
+GIT_END_DECL
+
+#endif
diff --git a/src/warning.c b/src/warning.c
new file mode 100644
index 0000000..3b658b0
--- /dev/null
+++ b/src/warning.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "warning.h"
+
+static void *warning_payload;
+static git_warning_cb warning_callback;
+
+int git_warning_set_callback(git_warning_cb callback, void *payload)
+{
+	warning_callback = callback;
+	warning_payload = callback ? payload : NULL;
+
+	return 0;
+}
+
+int git_warning__raise(git_warning *warning)
+{
+	if (!warning_callback)
+		return 0;
+
+	return warning_callback(warning, warning_payload);
+}
diff --git a/src/warning.h b/src/warning.h
new file mode 100644
index 0000000..e9e53d0
--- /dev/null
+++ b/src/warning.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_warning_h__
+#define INCLUDE_warning_h__
+
+#include "common.h"
+#include "git2/warning.h"
+
+/**
+ * Raise a warning.
+ *
+ * It returns the return value from the user-specified callback. If no
+ * callback is set, it returns 0.
+ */
+extern int git_warning__raise(git_warning *warning);
+
+#endif
diff --git a/tests/core/warning.c b/tests/core/warning.c
new file mode 100644
index 0000000..a62dcb4
--- /dev/null
+++ b/tests/core/warning.c
@@ -0,0 +1,49 @@
+#include "clar_libgit2.h"
+#include "warning.h"
+
+static git_warning g_warning = { GIT_WARNING_GENERIC, "Not really an error" };
+static int g_dummy_payload;
+
+void test_core_warning__cleanup(void)
+{
+	git_warning_set_callback(NULL, NULL);
+}
+
+void test_core_warning__zero_on_unset(void)
+{
+	cl_git_pass(git_warning__raise(&g_warning));
+}
+
+static int values_callback(git_warning *warning, void *payload)
+{
+	cl_assert_equal_p(&g_warning, warning);
+	cl_assert_equal_p(&g_dummy_payload, payload);
+
+	return 0;
+}
+
+void test_core_warning__raises_values(void)
+{
+	cl_git_pass(git_warning_set_callback(values_callback, &g_dummy_payload));
+	cl_git_pass(git_warning__raise(&g_warning));
+}
+
+static int should_be_called;
+static int can_unset_callback(git_warning *warning, void *payload)
+{
+	GIT_UNUSED(warning); GIT_UNUSED(payload);
+
+	cl_assert(should_be_called);
+
+	return 0;
+}
+
+void test_core_warning__can_unset(void)
+{
+	should_be_called = 1;
+	cl_git_pass(git_warning_set_callback(can_unset_callback, &g_dummy_payload));
+	cl_git_pass(git_warning__raise(&g_warning));
+	should_be_called = 0;
+	cl_git_pass(git_warning_set_callback(NULL, NULL));
+	cl_git_pass(git_warning__raise(&g_warning));
+}