Skip to content

Fix error: "fatal: '$GIT_DIR' too big" when checking out in a long path with core.longpaths = true. #3877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 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
@@ -23,6 +25,14 @@ 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)
{
if (starts_with(key, "core."))
return platform_core_config(key, value, d);
else
return tr2_sysenv_cb(key, value, d);
}

int main(int argc, const char **argv)
{
int result;
@@ -46,7 +56,10 @@ int main(int argc, const char **argv)

attr_start();

read_very_early_config(read_very_early_config_cb, NULL);

trace2_initialize();

trace2_cmd_start(argv);
trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);

33 changes: 18 additions & 15 deletions compat/mingw.c
Original file line number Diff line number Diff line change
@@ -933,7 +933,7 @@ static int current_directory_len = 0;

int mingw_chdir(const char *dirname)
{
int result;
int result = 0;
wchar_t wdirname[MAX_LONG_PATH];
if (xutftowcs_long_path(wdirname, dirname) < 0)
return -1;
@@ -954,8 +954,11 @@ int mingw_chdir(const char *dirname)
CloseHandle(hnd);
}

result = _wchdir(normalize_ntpath(wdirname));
if(!SetCurrentDirectoryW(wdirname)) {
result = -1;
}
current_directory_len = GetCurrentDirectoryW(0, NULL);

return result;
}

@@ -1254,15 +1257,11 @@ unsigned int sleep (unsigned int seconds)

char *mingw_mktemp(char *template)
{
wchar_t wtemplate[MAX_PATH];
wchar_t wtemplate[MAX_LONG_PATH];
int offset = 0;

/* we need to return the path, thus no long paths here! */
if (xutftowcsn(wtemplate, template, MAX_PATH, -1) < 0) {
if (errno == ERANGE)
errno = ENAMETOOLONG;
if (xutftowcs_long_path(wtemplate, template) < 0)
return NULL;
}

if (is_dir_sep(template[0]) && !is_dir_sep(template[1]) &&
iswalpha(wtemplate[0]) && wtemplate[1] == L':') {
@@ -1278,10 +1277,14 @@ char *mingw_mktemp(char *template)

int mkstemp(char *template)
{
char *filename = mktemp(template);
if (filename == NULL)
wchar_t wtemplate[MAX_LONG_PATH];

if (xutftowcs_long_path(wtemplate, template) < 0)
return -1;
return open(filename, O_RDWR | O_CREAT, 0600);
if (!_wmktemp(wtemplate))
return -1;

return _wopen(wtemplate, O_RDWR | O_CREAT, 0600);
}

int gettimeofday(struct timeval *tv, void *tz)
@@ -1401,7 +1404,7 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path)

char *mingw_getcwd(char *pointer, int len)
{
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
wchar_t cwd[MAX_LONG_PATH], wpointer[MAX_LONG_PATH];
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
HANDLE hnd;

@@ -3488,16 +3491,16 @@ static PSID get_current_user_sid(void)

int is_path_owned_by_current_sid(const char *path)
{
WCHAR wpath[MAX_PATH];
WCHAR wpath[MAX_LONG_PATH];
PSID sid = NULL;
PSECURITY_DESCRIPTOR descriptor = NULL;
DWORD err;

static wchar_t home[MAX_PATH];
static wchar_t home[MAX_LONG_PATH];

int result = 0;

if (xutftowcs_path(wpath, path) < 0)
if (xutftowcs_long_path(wpath, path) < 0)
return 0;

/*
6 changes: 6 additions & 0 deletions compat/mingw.h
Original file line number Diff line number Diff line change
@@ -552,6 +552,12 @@ int is_valid_win32_path(const char *path, int allow_literal_nul);
*/
int handle_long_path(wchar_t *path, int len, int max_path, int expand);

static inline int mingw_get_max_path_size(void)
{
return core_long_paths ? MAX_LONG_PATH : MAX_PATH;
}
#define get_max_path_size mingw_get_max_path_size

/**
* Converts UTF-8 encoded string to UTF-16LE.
*
9 changes: 7 additions & 2 deletions compat/win32/git.manifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity type="win32" name="Git" version="0.0.0.1" />
<asmv3:application>
<asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</asmv3:windowsSettings>
</asmv3:application>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
@@ -18,7 +23,7 @@
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 -->
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
4 changes: 4 additions & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
@@ -372,6 +372,10 @@ char *gitdirname(char *);
#define PATH_MAX 4096
#endif

#ifndef get_max_path_size
#define get_max_path_size() PATH_MAX
#endif

typedef uintmax_t timestamp_t;
#define PRItime PRIuMAX
#define parse_timestamp strtoumax
2 changes: 1 addition & 1 deletion git.rc
Original file line number Diff line number Diff line change
@@ -21,4 +21,4 @@ BEGIN
END
END

1 RT_MANIFEST "compat/win32/git.manifest"
1 24 "compat/win32/git.manifest"
2 changes: 1 addition & 1 deletion setup.c
Original file line number Diff line number Diff line change
@@ -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 (get_max_path_size() - 40 < strlen(gitdirenv))
die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT);

gitfile = (char*)read_gitfile(gitdirenv);
7 changes: 7 additions & 0 deletions t/t2031-checkout-long-paths.sh
Original file line number Diff line number Diff line change
@@ -99,4 +99,11 @@ test_expect_success SHORTABSPATH 'clean up path close to MAX_PATH' '
test ! -d "$subdir1"
'

test_expect_success 'init with long path' '
p=/123456789abcdef/123456789abcdef/123456789abcdef/123456789abc/ef &&
p=y$p$p$p$p &&
test_config_global core.longpaths true &&
git init $p
'

test_done
2 changes: 1 addition & 1 deletion trace2.c
Original file line number Diff line number Diff line change
@@ -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;
20 changes: 9 additions & 11 deletions trace2/tr2_sysenv.c
Original file line number Diff line number Diff line change
@@ -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;

@@ -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);
}

/*
3 changes: 2 additions & 1 deletion trace2/tr2_sysenv.h
Original file line number Diff line number Diff line change
@@ -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);