mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-27 00:58:30 -05:00
The Git project is not exactly the easiest project to get started in: it's written in C and POSIX shell, with bits of Perl, Rust and other languages sprinkled into it. On top of that, the project has grown somewhat organically over time, making the codebase hard to navigate. These are problems that we're aware of, and there have been and still are efforts to clean up some of the technical debt that is natural to exist an a project that is more than 20 years old. Furthermore, we provide resources to newcomers that help them out like our coding guidelines, code of conduct or "MyFirstContribution.adoc". But there is a rather practical problem: finding your way around in our project's tree is not easy. Doing a directory listing in the top-level directory will present you with more than 550 files, which makes it extremely hard for a newcomer to figure out what files they are even supposed to look at. This makes the onboarding experience somewhat harder than it really needs to be. This isn't only a problem for newcomers though, as I myself struggle to find the files I am looking for because of the sheer number of files. Besides the problem of discoverability it also creates a problem of structure. It is not obvious at all which files are part of "libgit.a" and which files are only linked into our final executables. So while we have this split in our build systems, that split is not evident at all in our tree. Introduce a new "lib/" directory and move all of our sources for "libgit.a" into it to fix these issues. It makes the split we have evident and reduces the number of files in our top-level tree from 550 files to ~80 files. This is still a lot of files, but it's significantly easier to navigate already. Furthermore, we can further iterate after this step and think about introducing a better structure for remaining files, as well. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
409 lines
8.6 KiB
C
409 lines
8.6 KiB
C
/*
|
|
* GIT - The information manager from hell
|
|
*
|
|
* Copyright (C) Linus Torvalds, 2005
|
|
*/
|
|
|
|
#include "git-compat-util.h"
|
|
#include "gettext.h"
|
|
#include "trace2.h"
|
|
#include "strbuf.h"
|
|
|
|
static void vfreportf(FILE *f, const char *prefix, const char *err, va_list params)
|
|
{
|
|
char msg[4096];
|
|
char *p, *pend = msg + sizeof(msg);
|
|
size_t prefix_len = strlen(prefix);
|
|
|
|
if (sizeof(msg) <= prefix_len) {
|
|
fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix);
|
|
abort();
|
|
}
|
|
memcpy(msg, prefix, prefix_len);
|
|
p = msg + prefix_len;
|
|
if (vsnprintf(p, pend - p, err, params) < 0) {
|
|
fprintf(stderr, _("error: unable to format message: %s\n"),
|
|
err);
|
|
*p = '\0'; /* vsnprintf() failed, clip at prefix */
|
|
}
|
|
|
|
for (; p != pend - 1 && *p; p++) {
|
|
if (iscntrl(*p) && *p != '\t' && *p != '\n')
|
|
*p = '?';
|
|
}
|
|
|
|
*(p++) = '\n'; /* we no longer need a NUL */
|
|
fflush(f);
|
|
write_in_full(fileno(f), msg, p - msg);
|
|
}
|
|
|
|
static void vreportf(const char *prefix, const char *err, va_list params)
|
|
{
|
|
vfreportf(stderr, prefix, err, params);
|
|
}
|
|
|
|
static NORETURN void usage_builtin(const char *err, va_list params)
|
|
{
|
|
vreportf(_("usage: "), err, params);
|
|
|
|
/*
|
|
* When we detect a usage error *before* the command dispatch in
|
|
* cmd_main(), we don't know what verb to report. Force it to this
|
|
* to facilitate post-processing.
|
|
*/
|
|
trace2_cmd_name("_usage_");
|
|
|
|
/*
|
|
* Currently, the (err, params) are usually just the static usage
|
|
* string which isn't very useful here. Usually, the call site
|
|
* manually calls fprintf(stderr,...) with the actual detailed
|
|
* syntax error before calling usage().
|
|
*
|
|
* TODO It would be nice to update the call sites to pass both
|
|
* the static usage string and the detailed error message.
|
|
*/
|
|
|
|
exit(129);
|
|
}
|
|
|
|
static void die_message_builtin(const char *err, va_list params)
|
|
{
|
|
if (!err)
|
|
return;
|
|
trace2_cmd_error_va(err, params);
|
|
vreportf(_("fatal: "), err, params);
|
|
}
|
|
|
|
/*
|
|
* We call trace2_cmd_error_va() in the below functions first and
|
|
* expect it to va_copy 'params' before using it (because an 'ap' can
|
|
* only be walked once).
|
|
*/
|
|
static NORETURN void die_builtin(const char *err, va_list params)
|
|
{
|
|
report_fn die_message_fn = get_die_message_routine();
|
|
|
|
die_message_fn(err, params);
|
|
exit(128);
|
|
}
|
|
|
|
static void error_builtin(const char *err, va_list params)
|
|
{
|
|
trace2_cmd_error_va(err, params);
|
|
|
|
vreportf(_("error: "), err, params);
|
|
}
|
|
|
|
static void warn_builtin(const char *warn, va_list params)
|
|
{
|
|
trace2_cmd_error_va(warn, params);
|
|
|
|
vreportf(_("warning: "), warn, params);
|
|
}
|
|
|
|
static int die_is_recursing_builtin(void)
|
|
{
|
|
static int dying;
|
|
/*
|
|
* Just an arbitrary number X where "a < x < b" where "a" is
|
|
* "maximum number of pthreads we'll ever plausibly spawn" and
|
|
* "b" is "something less than Inf", since the point is to
|
|
* prevent infinite recursion.
|
|
*/
|
|
static const int recursion_limit = 1024;
|
|
|
|
dying++;
|
|
if (dying > recursion_limit) {
|
|
return 1;
|
|
} else if (dying == 2) {
|
|
warning("die() called many times. Recursion error or racy threaded death!");
|
|
return 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* If we are in a dlopen()ed .so write to a global variable would segfault
|
|
* (ugh), so keep things static. */
|
|
static NORETURN_PTR report_fn usage_routine = usage_builtin;
|
|
static NORETURN_PTR report_fn die_routine = die_builtin;
|
|
static report_fn die_message_routine = die_message_builtin;
|
|
static report_fn error_routine = error_builtin;
|
|
static report_fn warn_routine = warn_builtin;
|
|
static int (*die_is_recursing)(void) = die_is_recursing_builtin;
|
|
|
|
void set_die_routine(NORETURN_PTR report_fn routine)
|
|
{
|
|
die_routine = routine;
|
|
}
|
|
|
|
report_fn get_die_message_routine(void)
|
|
{
|
|
return die_message_routine;
|
|
}
|
|
|
|
void set_error_routine(report_fn routine)
|
|
{
|
|
error_routine = routine;
|
|
}
|
|
|
|
report_fn get_error_routine(void)
|
|
{
|
|
return error_routine;
|
|
}
|
|
|
|
void set_warn_routine(report_fn routine)
|
|
{
|
|
warn_routine = routine;
|
|
}
|
|
|
|
report_fn get_warn_routine(void)
|
|
{
|
|
return warn_routine;
|
|
}
|
|
|
|
void set_die_is_recursing_routine(int (*routine)(void))
|
|
{
|
|
die_is_recursing = routine;
|
|
}
|
|
|
|
void NORETURN usagef(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
|
usage_routine(err, params);
|
|
va_end(params);
|
|
}
|
|
|
|
void NORETURN usage(const char *err)
|
|
{
|
|
usagef("%s", err);
|
|
}
|
|
|
|
static void show_usage_if_asked_helper(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
|
vfreportf(stdout, _("usage: "), err, params);
|
|
va_end(params);
|
|
exit(129);
|
|
}
|
|
|
|
void show_usage_if_asked(int ac, const char **av, const char *err)
|
|
{
|
|
if (ac == 2 && (!strcmp(av[1], "-h") ||
|
|
!strcmp(av[1], "--help-all")))
|
|
show_usage_if_asked_helper(err);
|
|
}
|
|
|
|
void NORETURN die(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
if (die_is_recursing()) {
|
|
fputs("fatal: recursion detected in die handler\n", stderr);
|
|
exit(128);
|
|
}
|
|
|
|
va_start(params, err);
|
|
die_routine(err, params);
|
|
va_end(params);
|
|
}
|
|
|
|
static const char *fmt_with_err(char *buf, int n, const char *fmt)
|
|
{
|
|
char str_error[256], *err;
|
|
size_t i, j;
|
|
|
|
err = strerror(errno);
|
|
for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
|
|
if ((str_error[j++] = err[i++]) != '%')
|
|
continue;
|
|
if (j < sizeof(str_error) - 1) {
|
|
str_error[j++] = '%';
|
|
} else {
|
|
/* No room to double the '%', so we overwrite it with
|
|
* '\0' below */
|
|
j--;
|
|
break;
|
|
}
|
|
}
|
|
str_error[j] = 0;
|
|
/* Truncation is acceptable here */
|
|
snprintf(buf, n, "%s: %s", fmt, str_error);
|
|
return buf;
|
|
}
|
|
|
|
void NORETURN die_errno(const char *fmt, ...)
|
|
{
|
|
char buf[1024];
|
|
va_list params;
|
|
|
|
if (die_is_recursing()) {
|
|
fputs("fatal: recursion detected in die_errno handler\n",
|
|
stderr);
|
|
exit(128);
|
|
}
|
|
|
|
va_start(params, fmt);
|
|
die_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
|
|
va_end(params);
|
|
}
|
|
|
|
#undef die_message
|
|
int die_message(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
|
die_message_routine(err, params);
|
|
va_end(params);
|
|
return 128;
|
|
}
|
|
|
|
#undef die_message_errno
|
|
int die_message_errno(const char *fmt, ...)
|
|
{
|
|
char buf[1024];
|
|
va_list params;
|
|
|
|
va_start(params, fmt);
|
|
die_message_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
|
|
va_end(params);
|
|
return 128;
|
|
}
|
|
|
|
#undef error_errno
|
|
int error_errno(const char *fmt, ...)
|
|
{
|
|
char buf[1024];
|
|
va_list params;
|
|
|
|
va_start(params, fmt);
|
|
error_routine(fmt_with_err(buf, sizeof(buf), fmt), params);
|
|
va_end(params);
|
|
return -1;
|
|
}
|
|
|
|
#undef error
|
|
int error(const char *err, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, err);
|
|
error_routine(err, params);
|
|
va_end(params);
|
|
return -1;
|
|
}
|
|
|
|
void warning_errno(const char *warn, ...)
|
|
{
|
|
char buf[1024];
|
|
va_list params;
|
|
|
|
va_start(params, warn);
|
|
warn_routine(fmt_with_err(buf, sizeof(buf), warn), params);
|
|
va_end(params);
|
|
}
|
|
|
|
void warning(const char *warn, ...)
|
|
{
|
|
va_list params;
|
|
|
|
va_start(params, warn);
|
|
warn_routine(warn, params);
|
|
va_end(params);
|
|
}
|
|
|
|
/* Only set this, ever, from t/helper/, when verifying that bugs are caught. */
|
|
int BUG_exit_code;
|
|
|
|
static void BUG_vfl_common(const char *file, int line, const char *fmt,
|
|
va_list params)
|
|
{
|
|
char prefix[256];
|
|
|
|
/* truncation via snprintf is OK here */
|
|
snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
|
|
|
|
vreportf(prefix, fmt, params);
|
|
}
|
|
|
|
static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
|
|
{
|
|
va_list params_copy;
|
|
static int in_bug;
|
|
|
|
va_copy(params_copy, params);
|
|
BUG_vfl_common(file, line, fmt, params);
|
|
|
|
if (in_bug)
|
|
abort();
|
|
in_bug = 1;
|
|
|
|
trace2_cmd_error_va(fmt, params_copy);
|
|
|
|
if (BUG_exit_code)
|
|
exit(BUG_exit_code);
|
|
abort();
|
|
}
|
|
|
|
NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
bug_called_must_BUG = 0;
|
|
|
|
va_start(ap, fmt);
|
|
BUG_vfl(file, line, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
int bug_called_must_BUG;
|
|
void bug_fl(const char *file, int line, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
bug_called_must_BUG = 1;
|
|
|
|
va_start(ap, fmt);
|
|
BUG_vfl_common(file, line, fmt, ap);
|
|
va_end(ap);
|
|
|
|
va_start(ap, fmt);
|
|
trace2_cmd_error_va(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
NORETURN void you_still_use_that(const char *command_name, const char *hint)
|
|
{
|
|
struct strbuf percent_encoded = STRBUF_INIT;
|
|
strbuf_add_percentencode(&percent_encoded,
|
|
command_name,
|
|
STRBUF_ENCODE_SLASH);
|
|
|
|
fprintf(stderr,
|
|
_("'%s' is nominated for removal.\n"), command_name);
|
|
|
|
if (hint)
|
|
fputs(hint, stderr);
|
|
|
|
fprintf(stderr,
|
|
_("If you still use this command, here's what you can do:\n"
|
|
"\n"
|
|
"- read https://git-scm.com/docs/BreakingChanges.html\n"
|
|
"- check if anyone has discussed this on the mailing\n"
|
|
" list and if they came up with something that can\n"
|
|
" help you: https://lore.kernel.org/git/?q=%s\n"
|
|
"- send an email to <git@vger.kernel.org> to let us\n"
|
|
" know that you still use this command and were unable\n"
|
|
" to determine a suitable replacement\n"
|
|
"\n"),
|
|
percent_encoded.buf);
|
|
strbuf_release(&percent_encoded);
|
|
die(_("refusing to run without --i-still-use-this"));
|
|
}
|