Skip to content

Commit

Permalink
Fix error: "fatal: '$GIT_DIR' too big" when checking out in a long pa…
Browse files Browse the repository at this point in the history
…th on

Windows with core.longpaths = true.

This is a fix for issue: #3372

Use case:
Allow git to checkout a repository or submodule in a directory with a long
path when core.longpaths = true.

Example:
> ./git.exe config --global core.longpaths true
> ./git.exe clone https://github.com/git/git.git --recurse-submodules \
/c/eval/git_test/loooooooooooooooooooooooooooooooooooooooooooooooooooooooo\
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\
oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\
oooooooong

Context:
$ sh --version
	GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)
$ ./git.exe --version --build-options
	git version 2.36.1.windows.1
	cpu: x86_64
	built from commit: e2ff68a
	sizeof-long: 4
	sizeof-size_t: 8
	shell-path: /bin/sh
	feature: fsmonitor--daemon

Error:
fatal: '$GIT_DIR' too big.

Problem analysis:
setup_explicit_git_dir in setup.c uses PATH_MAX to check if the git dir is
to long. On Windows PATH_MAX is set by limit.h to 260 and
setup_explicit_git_dir ignores core.longpaths.

Solution:
The implementation is based on the solution proposed by Johannes
Schindelin, see:
#3372 (comment)

* Refactor the part of trace2_initialize() that reads the config.
* Make tr2_sysenv_cb() a public function.
* No longer calling it from trace2_initialize(), but from a static callback
  function in common-main.c.
* Calling read_very_early_config() explicitly in main(), with that static
  callback function that calls into tr2_sysenv_cb().
* Extend the static callback function for Windows, and parse core.longPaths
  in that function.
* Extend startup_info struct in cache.h with 'int max_long_path' so we can
  use it in setup_explicit_git_dir instead of PATH_MAX.

Signed-off-by: Kevin Worm <kevin.worm_1@signify.com>
  • Loading branch information
kevin-worm committed May 30, 2022
1 parent 590ade5 commit 40ff106
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 14 deletions.
1 change: 1 addition & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,7 @@ void overlay_tree_on_index(struct index_state *istate,

/* setup.c */
struct startup_info {
int max_long_path;
int have_repository;
const char *prefix;
const char *original_cwd;
Expand Down
20 changes: 20 additions & 0 deletions common-main.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "cache.h"
#include "config.h"
#include "exec-cmd.h"
#include "attr.h"
#include "trace2/tr2_sysenv.h"

/*
* Many parts of Git have subprograms communicate via pipe, expect the
Expand All @@ -23,6 +25,21 @@ static void restore_sigpipe_to_default(void)
signal(SIGPIPE, SIG_DFL);
}

static int read_very_early_config_cb(const char *key, const char *value, void *d)
{
tr2_sysenv_cb(key, value, d);

#if defined(__MINGW32__)
if (!strcmp(key, "core.longpaths") && git_config_bool(key, value)) {
startup_info->max_long_path = MAX_LONG_PATH;
}
#else
startup_info->max_long_path = PATH_MAX;
#endif

return 0;
}

int main(int argc, const char **argv)
{
int result;
Expand All @@ -47,6 +64,9 @@ int main(int argc, const char **argv)
attr_start();

trace2_initialize();

read_very_early_config(read_very_early_config_cb, NULL);

trace2_cmd_start(argv);
trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);

Expand Down
2 changes: 1 addition & 1 deletion setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv,
char *gitfile;
int offset;

if (PATH_MAX - 40 < strlen(gitdirenv))
if (startup_info->max_long_path - 40 < strlen(gitdirenv))
die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);

gitfile = (char*)read_gitfile(gitdirenv);
Expand Down
2 changes: 1 addition & 1 deletion trace2.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ void trace2_initialize_fl(const char *file, int line)
if (trace2_enabled)
return;

tr2_sysenv_load();
tr2_sysenv_check_size();

if (!tr2_tgt_want_builtins())
return;
Expand Down
20 changes: 9 additions & 11 deletions trace2/tr2_sysenv.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ static struct tr2_sysenv_entry tr2_sysenv_settings[] = {
};
/* clang-format on */

static int tr2_sysenv_cb(const char *key, const char *value, void *d)
/*
* Load Trace2 settings from the system config (usually "/etc/gitconfig"
* unless we were built with a runtime-prefix). These are intended to
* define the default values for Trace2 as requested by the administrator.
*
* Then override with the Trace2 settings from the global config.
*/
int tr2_sysenv_cb(const char *key, const char *value, void *d)
{
int k;

Expand All @@ -75,19 +82,10 @@ static int tr2_sysenv_cb(const char *key, const char *value, void *d)
return 0;
}

/*
* Load Trace2 settings from the system config (usually "/etc/gitconfig"
* unless we were built with a runtime-prefix). These are intended to
* define the default values for Trace2 as requested by the administrator.
*
* Then override with the Trace2 settings from the global config.
*/
void tr2_sysenv_load(void)
void tr2_sysenv_check_size(void)
{
if (ARRAY_SIZE(tr2_sysenv_settings) != TR2_SYSENV_MUST_BE_LAST)
BUG("tr2_sysenv_settings size is wrong");

read_very_early_config(tr2_sysenv_cb, NULL);
}

/*
Expand Down
3 changes: 2 additions & 1 deletion trace2/tr2_sysenv.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ enum tr2_sysenv_variable {
TR2_SYSENV_MUST_BE_LAST
};

void tr2_sysenv_load(void);
int tr2_sysenv_cb(const char *key, const char *value, void *d);
void tr2_sysenv_check_size(void);

const char *tr2_sysenv_get(enum tr2_sysenv_variable);
const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var);
Expand Down

0 comments on commit 40ff106

Please sign in to comment.