mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-01 08:49:57 +00:00
285 lines
11 KiB
Markdown
285 lines
11 KiB
Markdown
Error reporting in libgit2
|
|
==========================
|
|
|
|
Libgit2 tries to follow the POSIX style: functions return an `int` value
|
|
with 0 (zero) indicating success and negative values indicating an error.
|
|
There are specific negative error codes for each "expected failure"
|
|
(e.g. `GIT_ENOTFOUND` for files that take a path which might be missing)
|
|
and a generic error code (-1) for all critical or non-specific failures
|
|
(e.g. running out of memory or system corruption).
|
|
|
|
When a negative value is returned, an error message is also set. The
|
|
message can be accessed via the `git_error_last` function which will return a
|
|
pointer to a `git_error` structure containing the error message text and
|
|
the class of error (i.e. what part of the library generated the error).
|
|
|
|
For instance: An object lookup by SHA prefix (`git_object_lookup_prefix`)
|
|
has two expected failure cases: the SHA is not found at all which returns
|
|
`GIT_ENOTFOUND` or the SHA prefix is ambiguous (i.e. two or more objects
|
|
share the prefix) which returns `GIT_EAMBIGUOUS`. There are any number of
|
|
critical failures (such as a packfile being corrupted, a loose object
|
|
having the wrong access permissions, etc.) all of which will return -1.
|
|
When the object lookup is successful, it will return 0.
|
|
|
|
If libgit2 was compiled with threads enabled (`-DTHREADSAFE=ON` when using
|
|
CMake), then the error message will be kept in thread-local storage, so it
|
|
will not be modified by other threads. If threads are not enabled, then
|
|
the error message is in global data.
|
|
|
|
All of the error return codes, the `git_error` type, the error access
|
|
functions, and the error classes are defined in `include/git2/errors.h`.
|
|
See the documentation there for details on the APIs for accessing,
|
|
clearing, and even setting error codes.
|
|
|
|
When writing libgit2 code, please be smart and conservative when returning
|
|
error codes. Functions usually have a maximum of two or three "expected
|
|
errors" and in most cases only one. If you feel there are more possible
|
|
expected error scenarios, then the API you are writing may be at too high
|
|
a level for core libgit2.
|
|
|
|
Example usage
|
|
-------------
|
|
|
|
When using libgit2, you will typically capture the return value from
|
|
functions using an `int` variable and check to see if it is negative.
|
|
When that happens, you can, if you wish, look at the specific value or
|
|
look at the error message that was generated.
|
|
|
|
~~~c
|
|
{
|
|
git_repository *repo;
|
|
int error = git_repository_open(&repo, "path/to/repo");
|
|
|
|
if (error < 0) {
|
|
fprintf(stderr, "Could not open repository: %s\n", git_error_last()->message);
|
|
exit(1);
|
|
}
|
|
|
|
... use `repo` here ...
|
|
|
|
git_repository_free(repo); /* void function - no error return code */
|
|
}
|
|
~~~
|
|
|
|
Some of the error return values do have meaning. Optionally, you can look
|
|
at the specific error values to decide what to do.
|
|
|
|
~~~c
|
|
{
|
|
git_repository *repo;
|
|
const char *path = "path/to/repo";
|
|
int error = git_repository_open(&repo, path);
|
|
|
|
if (error < 0) {
|
|
if (error == GIT_ENOTFOUND)
|
|
fprintf(stderr, "Could not find repository at path '%s'\n", path);
|
|
else
|
|
fprintf(stderr, "Unable to open repository: %s\n",
|
|
git_error_last()->message);
|
|
exit(1);
|
|
}
|
|
|
|
... happy ...
|
|
}
|
|
~~~
|
|
|
|
Some of the higher-level language bindings may use a range of information
|
|
from libgit2 to convert error return codes into exceptions, including the
|
|
specific error return codes and even the class of error and the error
|
|
message returned by `git_error_last`, but the full range of that logic is
|
|
beyond the scope of this document.
|
|
|
|
Example internal implementation
|
|
-------------------------------
|
|
|
|
Internally, libgit2 detects error scenarios, records error messages, and
|
|
returns error values. Errors from low-level functions are generally
|
|
passed upwards (unless the higher level can either handle the error or
|
|
wants to translate the error into something more meaningful).
|
|
|
|
~~~c
|
|
int git_repository_open(git_repository **repository, const char *path)
|
|
{
|
|
/* perform some logic to open the repository */
|
|
if (p_exists(path) < 0) {
|
|
git_error_set(GIT_ERROR_REPOSITORY, "The path '%s' doesn't exist", path);
|
|
return GIT_ENOTFOUND;
|
|
}
|
|
|
|
...
|
|
}
|
|
~~~
|
|
|
|
Note that some error codes have been defined with a specific meaning in the
|
|
context of callbacks:
|
|
- `GIT_EUSER` provides a way to bubble up a non libgit2-related failure, which
|
|
allows it to be preserved all the way up to the initial function call (a `git_cred`
|
|
setup trying to access an unavailable LDAP server for instance).
|
|
- `GIT_EPASSTHROUGH` provides a way to tell libgit2 that it should behave as if
|
|
no callback was provided. This is of special interest to bindings, which would
|
|
always provide a C function as a "trampoline", and decide at runtime what to do.
|
|
|
|
The public error API
|
|
--------------------
|
|
|
|
- `const git_error *git_error_last(void)`: The main function used to look up
|
|
the last error. This may return NULL if no error has occurred.
|
|
Otherwise this should return a `git_error` object indicating the class
|
|
of error and the error message that was generated by the library.
|
|
Do not use this function unless the prior call to a libgit2 API
|
|
returned an error, as it can otherwise give misleading results.
|
|
libgit2's error strings are not cleared aggressively,
|
|
and this function may return an error string that reflects a prior error,
|
|
possibly even reflecting internal state.
|
|
|
|
The last error is stored in thread-local storage when libgit2 is
|
|
compiled with thread support, so you do not have to worry about another
|
|
thread overwriting the value. When thread support is off, the last
|
|
error is a global value.
|
|
|
|
_Note_ There are some known bugs in the library where this may return
|
|
NULL even when an error code was generated. Please report these as
|
|
bugs, but in the meantime, please code defensively and check for NULL
|
|
when calling this function.
|
|
|
|
- `void git_error_clear(void)`: This function clears the last error. The
|
|
library will call this when an error is generated by low level function
|
|
and the higher level function handles the error.
|
|
|
|
_Note_ There are some known bugs in the library where a low level
|
|
function's error message is not cleared by higher level code that
|
|
handles the error and returns zero. Please report these as bugs, but in
|
|
the meantime, a zero return value from a libgit2 API does not guarantee
|
|
that `git_error_last()` will return NULL.
|
|
|
|
- `void git_error_set(int error_class, const char *message)`: This
|
|
function can be used when writing a custom backend module to set the
|
|
libgit2 error message. See the documentation on this function for its
|
|
use. Normal usage of libgit2 will probably never need to call this API.
|
|
|
|
- `void git_error_set_oom(void)`: This is a standard function for reporting
|
|
an out-of-memory error. It is written in a manner that it doesn't have
|
|
to allocate any extra memory in order to record the error, so this is
|
|
the best way to report that scenario.
|
|
|
|
Deviations from the standard
|
|
----------------------------
|
|
|
|
There are some public functions that do not return `int` values. There
|
|
are two primary cases:
|
|
|
|
* `void` return values: If a function has a `void` return, then it will
|
|
never fail. This primary will be used for object destructors.
|
|
|
|
* `git_xyz *` return values: These are simple accessor functions where the
|
|
only meaningful error would typically be looking something up by index
|
|
and having the index be out of bounds. In those cases, the function
|
|
will typically return NULL.
|
|
|
|
* Boolean return values: There are some cases where a function cannot fail
|
|
and wants to return a boolean value. In those cases, we try to return 1
|
|
for true and 0 for false. These cases are rare and the return value for
|
|
the function should probably be an `unsigned int` to denote these cases.
|
|
If you find an exception, please open an issue and let's fix it.
|
|
|
|
There are a few other exceptions to these rules here and there in the
|
|
library, but those are extremely rare and should probably be converted
|
|
over to other to more standard patterns for usage. Feel free to open
|
|
issues pointing these out.
|
|
|
|
There are some known bugs in the library where some functions may return a
|
|
negative value but not set an error message and some other functions may
|
|
return zero (no error) and yet leave an error message set. Please report
|
|
these cases as issues and they will be fixed. In the meanwhile, please
|
|
code defensively, checking that the return value of `git_error_last` is not
|
|
NULL before using it, and not relying on `git_error_last` to return NULL when
|
|
a function returns 0 for success.
|
|
|
|
The internal error API
|
|
----------------------
|
|
|
|
- `void git_error_set(int error_class, const char *fmt, ...)`: This is the
|
|
main internal function for setting an error. It works like `printf` to
|
|
format the error message. See the notes of `git_error_set_str` for a
|
|
general description of how error messages are stored (and also about
|
|
special handling for `error_class` of `GIT_ERROR_OS`).
|
|
|
|
Writing error messages
|
|
----------------------
|
|
|
|
Here are some guidelines when writing error messages:
|
|
|
|
- Use proper English, and an impersonal or past tenses: *The given path
|
|
does not exist*, *Failed to lookup object in ODB*
|
|
|
|
- Use short, direct and objective messages. **One line, max**. libgit2 is
|
|
a low level library: think that all the messages reported will be thrown
|
|
as Ruby or Python exceptions. Think how long are common exception
|
|
messages in those languages.
|
|
|
|
- **Do not add redundant information to the error message**, specially
|
|
information that can be inferred from the context.
|
|
|
|
E.g. in `git_repository_open`, do not report a message like "Failed to
|
|
open repository: path not found". Somebody is calling that
|
|
function. If it fails, they already know that the repository failed to
|
|
open!
|
|
|
|
General guidelines for error reporting
|
|
--------------------------------------
|
|
|
|
- Libgit2 does not handle programming errors with these
|
|
functions. Programming errors are `assert`ed, and when their source is
|
|
internal, fixed as soon as possible. This is C, people.
|
|
|
|
Example of programming errors that would **not** be handled: passing
|
|
NULL to a function that expects a valid pointer; passing a `git_tree`
|
|
to a function that expects a `git_commit`. All these cases need to be
|
|
identified with `assert` and fixed asap.
|
|
|
|
Example of a runtime error: failing to parse a `git_tree` because it
|
|
contains invalid data. Failing to open a file because it doesn't exist
|
|
on disk. These errors are handled, a meaningful error message is set,
|
|
and an error code is returned.
|
|
|
|
- In general, *do not* try to overwrite errors internally and *do*
|
|
propagate error codes from lower level functions to the higher level.
|
|
There are some cases where propagating an error code will be more
|
|
confusing rather than less, so there are some exceptions to this rule,
|
|
but the default behavior should be to simply clean up and pass the error
|
|
on up to the caller.
|
|
|
|
**WRONG**
|
|
|
|
~~~c
|
|
int git_commit_parent(...)
|
|
{
|
|
...
|
|
|
|
if (git_commit_lookup(parent, repo, parent_id) < 0) {
|
|
git_error_set(GIT_ERROR_COMMIT, "Overwrite lookup error message");
|
|
return -1; /* mask error code */
|
|
}
|
|
|
|
...
|
|
}
|
|
~~~
|
|
|
|
**RIGHT**
|
|
|
|
~~~c
|
|
int git_commit_parent(...)
|
|
{
|
|
...
|
|
|
|
error = git_commit_lookup(parent, repo, parent_id);
|
|
if (error < 0) {
|
|
/* cleanup intermediate objects if necessary */
|
|
/* leave error message and propagate error code */
|
|
return error;
|
|
}
|
|
|
|
...
|
|
}
|
|
~~~
|