Merge branch 'development' into error-handling

The code in this branch has been modified so it works with the global
state introduced in development.
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 5ac0d5b..db71fc2 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -8,6 +8,7 @@
 #define INCLUDE_git_errors_h__
 
 #include "common.h"
+#include "types.h"
 
 /**
  * @file git2/errors.h
@@ -113,7 +114,7 @@
 
 	/** The buffer is too short to satisfy the request */
 	GIT_ESHORTBUFFER = -32,
-} git_error;
+} git_error_code;
 
 /**
  * Return a detailed error string with the latest error
@@ -139,6 +140,16 @@
  */
 GIT_EXTERN(void) git_clearerror(void);
 
+GIT_EXTERN(void) git_error_free(git_error *err);
+
+/**
+ * Print a stack trace to stderr
+ *
+ * A bog standard stack trace. You can use it if you don't want to do
+ * anything more complex in your UI.
+ */
+GIT_EXTERN(void) git_error_print_stack(git_error *error_in);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/include/git2/types.h b/include/git2/types.h
index 1df1897..03b5fcf 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -131,6 +131,15 @@
 /** Representation of a reference log */
 typedef struct git_reflog git_reflog;
 
+/** Represensation of a git_error */
+typedef struct git_error {
+	int code;
+	char *msg;
+	struct git_error *child;
+	const char *file;
+	unsigned int line;
+} git_error;
+
 /** Time in a signature */
 typedef struct git_time {
 	git_time_t time; /** time in seconds from epoch */
diff --git a/src/common.h b/src/common.h
index 727a08e..f4dcc1c 100644
--- a/src/common.h
+++ b/src/common.h
@@ -50,14 +50,7 @@
 #include "thread-utils.h"
 #include "bswap.h"
 
-extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
-#define git__throw(error, ...) \
-	(git___throw(__VA_ARGS__), error)
-
-extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
-#define git__rethrow(error, ...) \
-	(git___rethrow(__VA_ARGS__), error)
-
+#include "errors.h"
 #include "util.h"
 
 #endif /* INCLUDE_common_h__ */
diff --git a/src/errors.c b/src/errors.c
index 81770e7..22b8ae7 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -4,7 +4,10 @@
  * 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 "errors.h"
+#include "posix.h"
 #include "global.h"
 #include <stdarg.h>
 
@@ -55,50 +58,109 @@
 	return "Unknown error";
 }
 
-#define ERROR_MAX_LEN 1024
+static git_error git_error_OOM = {
+	GIT_ENOMEM,
+	"out of memory",
+	NULL,
+	NULL,
+	-1
+};
 
-void git___rethrow(const char *msg, ...)
+git_error * git_error_oom(void)
 {
-	char new_error[ERROR_MAX_LEN];
-	char *last_error;
-	char *old_error = NULL;
-
-	va_list va;
-
-	last_error = GIT_GLOBAL->error.last;
-
-	va_start(va, msg);
-	vsnprintf(new_error, ERROR_MAX_LEN, msg, va);
-	va_end(va);
-
-	old_error = git__strdup(last_error);
-
-	snprintf(last_error, ERROR_MAX_LEN, "%s \n	- %s", new_error, old_error);
-
-	git__free(old_error);
+	 /*
+	  * Throw an out-of-memory error:
+	  * what we return is actually a static pointer, because on
+	  * oom situations we cannot afford to allocate a new error
+	  * object.
+	  *
+	  * The `git_error_free` function will take care of not
+	  * freeing this special type of error.
+	  *
+	  */
+	return &git_error_OOM;
 }
 
-void git___throw(const char *msg, ...)
+git_error * git_error_createf(const char *file, unsigned int line, int code,
+			      const char *fmt, ...)
 {
-	va_list va;
+	git_error *err;
+	va_list ap;
+	size_t size;
 
-	va_start(va, msg);
-	vsnprintf(GIT_GLOBAL->error.last, ERROR_MAX_LEN, msg, va);
-	va_end(va);
+	err = git__malloc(sizeof(git_error));
+	if (err == NULL)
+		return git_error_oom();
+
+	memset(err, 0x0, sizeof(git_error));
+
+	va_start(ap, fmt);
+	size = p_vsnprintf(err->msg, 0, fmt, ap);
+	va_end(ap);
+
+	size++;
+
+	err->msg = git__malloc(size);
+	if (err->msg == NULL) {
+		free(err);
+		return git_error_oom();
+	}
+
+	va_start(ap, fmt);
+	size = p_vsnprintf(err->msg, size, fmt, ap);
+	va_end(ap);
+
+	err->code  = code;
+	err->child = GIT_GLOBAL->git_errno;
+	err->file  = file;
+	err->line  = line;
+
+	GIT_GLOBAL->git_errno = err;
+
+	return err;
 }
 
-const char *git_lasterror(void)
+git_error * git_error__quick_wrap(const char *file, int line,
+								 git_error_code error, const char *msg)
 {
-	char *last_error = GIT_GLOBAL->error.last;
+	if (error == GIT_SUCCESS)
+		return GIT_SUCCESS;
 
-	if (!last_error[0])
-		return NULL;
+	return git_error_createf(file, line, error, "%s", msg);
+}
 
-	return last_error;
+void git_error_free(git_error *err)
+{
+	if (err == NULL)
+		return;
+
+	if (err->child)
+		git_error_free(err->child);
+
+	if (err->msg)
+		free(err->msg);
+
+	free(err);
 }
 
 void git_clearerror(void)
 {
-	char *last_error = GIT_GLOBAL->error.last;
-	last_error[0] = '\0';
+	git_error_free(GIT_GLOBAL->git_errno);
+	GIT_GLOBAL->git_errno = NULL;
+}
+
+const char *git_lasterror(void)
+{
+	return GIT_GLOBAL->git_errno == NULL ? NULL : GIT_GLOBAL->git_errno->msg;
+}
+
+void git_error_print_stack(git_error *error_in)
+{
+	git_error *error;
+
+	if (error_in == NULL)
+		error_in = GIT_GLOBAL->git_errno;
+
+	for (error = error_in; error; error = error->child)
+		fprintf(stderr, "%s:%u %s\n", error->file, error->line, error->msg);
 }
diff --git a/src/errors.h b/src/errors.h
new file mode 100644
index 0000000..525cb0f
--- /dev/null
+++ b/src/errors.h
@@ -0,0 +1,48 @@
+#ifndef INCLUDE_errors_h__
+#define INCLUDE_errors_h__
+
+#include "git2/common.h"
+
+/* Deprecated - please use the more advanced functions below. */
+#define git__throw(error, ...) \
+	(git_error_createf(__FILE__, __LINE__, error, __VA_ARGS__), error)
+
+#define git__rethrow(error, ...) \
+	(git_error_createf(__FILE__, __LINE__, error, __VA_ARGS__), error)
+
+/*
+ * This implementation is loosely based on subversion's error
+ * handling.
+ */
+
+git_error * git_error_createf(const char *file, unsigned int line, int code,
+			      const char *msg, ...) GIT_FORMAT_PRINTF(4, 5);
+
+git_error * git_error__quick_wrap(const char *file, int line,
+				 git_error_code error, const char *msg);
+
+/*
+ * Wrap an error with a message. All git_error values are assigned with
+ * child's fields.
+ */
+#define git_error_quick_wrap(error, message)				\
+	git_error__quick_wrap(__FILE__, __LINE__, error, message)
+
+/*
+ * Use this function to wrap functions like
+ *
+ *	git_error * foo(void)
+ *	{
+ *		return git_error_trace(bar());
+ *	}
+ *
+ * Otherwise the call of foo() wouldn't be visible in the trace.
+ *
+ */
+#define git_error_trace(error) \
+	git_error_quick_wrap(error, "traced error");
+
+/* Throw an out-of-memory error */
+extern git_error * git_error_oom(void);
+
+#endif /* INCLUDE_errors_h__ */
diff --git a/src/global.h b/src/global.h
index 641f47c..6a15a8d 100644
--- a/src/global.h
+++ b/src/global.h
@@ -8,12 +8,10 @@
 #define INCLUDE_global_h__
 
 #include "mwindow.h"
+#include "git2/types.h"
 
 typedef struct {
-	struct {
-		char last[1024];
-	} error;
-
+	git_error *git_errno;
 	git_mwindow_ctl mem_ctl;
 } git_global_st;
 
diff --git a/src/remote.c b/src/remote.c
index 3ff08a2..51e77e5 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -14,6 +14,7 @@
 #include "remote.h"
 #include "fetch.h"
 #include "refs.h"
+#include "error.h"
 
 static int refspec_parse(git_refspec *refspec, const char *str)
 {
diff --git a/tests-clay/object/tree/frompath.c b/tests-clay/object/tree/frompath.c
index 1effcb1..651a86d 100644
--- a/tests-clay/object/tree/frompath.c
+++ b/tests-clay/object/tree/frompath.c
@@ -23,9 +23,10 @@
 {
 	git_tree_close(tree);
 	git_repository_free(repo);
+	git_clearerror();
 }
 
-static void assert_tree_from_path(git_tree *root, const char *path, git_error expected_result, const char *expected_raw_oid)
+static void assert_tree_from_path(git_tree *root, const char *path, int expected_result, const char *expected_raw_oid)
 {
 	git_tree *containing_tree = NULL;
 
diff --git a/tests-clay/status/worktree.c b/tests-clay/status/worktree.c
index 1e8a5dd..7d120ec 100644
--- a/tests-clay/status/worktree.c
+++ b/tests-clay/status/worktree.c
@@ -92,6 +92,7 @@
 	_repository = NULL;
 
 	cl_fixture_cleanup("status");
+	git_clearerror();
 }
 
 /**
diff --git a/tests/t18-status.c b/tests/t18-status.c
index 73e328c..3b75472 100644
--- a/tests/t18-status.c
+++ b/tests/t18-status.c
@@ -430,6 +430,8 @@
 	git_repository_free(repo);
 
 	git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
+
+	git_clearerror();
 END_TEST
 
 BEGIN_SUITE(status)
diff --git a/tests/test_lib.c b/tests/test_lib.c
index a4c39df..9d3cba1 100755
--- a/tests/test_lib.c
+++ b/tests/test_lib.c
@@ -10,12 +10,15 @@
 #define DO_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE)))
 #define GIT_MAX_TEST_CASES 64
 
+extern git_error *git_errno;
+
 struct git_test {
 	char *name;
 	char *message;
 	char *failed_pos;
 	char *description;
 	char *error_message;
+	git_error *error_stack;
 
 	git_testfunc function;
 	unsigned failed:1, ran:1;
@@ -36,6 +39,7 @@
 		free(t->failed_pos);
 		free(t->message);
 		free(t->error_message);
+		git_error_free(t->error_stack);
 		free(t);
 	}
 }
@@ -84,6 +88,8 @@
 	tc->failed = 1;
 	tc->message = strdup(message);
 	tc->failed_pos = strdup(buf);
+	tc->error_stack = GIT_GLOBAL->git_errno;
+	GIT_GLOBAL->git_errno = NULL;
 
 	if (last_error)
 		tc->error_message = strdup(last_error);
@@ -146,6 +152,14 @@
 	ts->list[ts->count++] = create_test(test);
 }
 
+static void print_trace(git_error *error)
+{
+	git_error *err;
+
+	for (err = error; err; err = err->child)
+		printf("\t%s:%u %s\n", err->file, err->line, err->msg);
+}
+
 static void print_details(git_testsuite *ts)
 {
 	int i;
@@ -165,6 +179,8 @@
 					failCount, tc->description, tc->name, tc->failed_pos, tc->message);
 				if (tc->error_message)
 					printf("\tError: %s\n", tc->error_message);
+				fprintf(stderr, "\tError stack trace:\n");
+				print_trace(tc->error_stack);
 			}
 		}
 	}
diff --git a/tests/test_lib.h b/tests/test_lib.h
index 9d90e48..7552ebf 100755
--- a/tests/test_lib.h
+++ b/tests/test_lib.h
@@ -7,6 +7,7 @@
 #include <string.h>
 
 #include "common.h"
+#include "global.h"
 #include <git2.h>
 
 #define DECLARE_SUITE(SNAME) extern git_testsuite *libgit2_suite_##SNAME(void)