mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-10-31 19:49:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			563 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			563 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "clar_libgit2.h"
 | |
| #include "posix.h"
 | |
| #include "path.h"
 | |
| #include "git2/sys/repository.h"
 | |
| 
 | |
| void cl_git_report_failure(
 | |
| 	int error, const char *file, int line, const char *fncall)
 | |
| {
 | |
| 	char msg[4096];
 | |
| 	const git_error *last = giterr_last();
 | |
| 	p_snprintf(msg, 4096, "error %d - %s",
 | |
| 		error, last ? last->message : "<no message>");
 | |
| 	clar__assert(0, file, line, fncall, msg, 1);
 | |
| }
 | |
| 
 | |
| void cl_git_mkfile(const char *filename, const char *content)
 | |
| {
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = p_creat(filename, 0666);
 | |
| 	cl_assert(fd != -1);
 | |
| 
 | |
| 	if (content) {
 | |
| 		cl_must_pass(p_write(fd, content, strlen(content)));
 | |
| 	} else {
 | |
| 		cl_must_pass(p_write(fd, filename, strlen(filename)));
 | |
| 		cl_must_pass(p_write(fd, "\n", 1));
 | |
| 	}
 | |
| 
 | |
| 	cl_must_pass(p_close(fd));
 | |
| }
 | |
| 
 | |
| void cl_git_write2file(
 | |
| 	const char *path, const char *content, size_t content_len,
 | |
| 	int flags, unsigned int mode)
 | |
| {
 | |
| 	int fd;
 | |
| 	cl_assert(path && content);
 | |
| 	cl_assert((fd = p_open(path, flags, mode)) >= 0);
 | |
| 	if (!content_len)
 | |
| 		content_len = strlen(content);
 | |
| 	cl_must_pass(p_write(fd, content, content_len));
 | |
| 	cl_must_pass(p_close(fd));
 | |
| }
 | |
| 
 | |
| void cl_git_append2file(const char *path, const char *content)
 | |
| {
 | |
| 	cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
 | |
| }
 | |
| 
 | |
| void cl_git_rewritefile(const char *path, const char *content)
 | |
| {
 | |
| 	cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
 | |
| }
 | |
| 
 | |
| void cl_git_rmfile(const char *filename)
 | |
| {
 | |
| 	cl_must_pass(p_unlink(filename));
 | |
| }
 | |
| 
 | |
| #ifdef GIT_WIN32
 | |
| 
 | |
| #include "win32/utf-conv.h"
 | |
| 
 | |
| char *cl_getenv(const char *name)
 | |
| {
 | |
| 	wchar_t *wide_name, *wide_value;
 | |
| 	char *utf8_value = NULL;
 | |
| 	DWORD value_len;
 | |
| 
 | |
| 	cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0);
 | |
| 
 | |
| 	value_len = GetEnvironmentVariableW(wide_name, NULL, 0);
 | |
| 
 | |
| 	if (value_len) {
 | |
| 		cl_assert(wide_value = git__malloc(value_len * sizeof(wchar_t)));
 | |
| 		cl_assert(GetEnvironmentVariableW(wide_name, wide_value, value_len));
 | |
| 		cl_assert(git__utf16_to_8_alloc(&utf8_value, wide_value) >= 0);
 | |
| 		git__free(wide_value);
 | |
| 	}
 | |
| 
 | |
| 	git__free(wide_name);
 | |
| 	return utf8_value;
 | |
| }
 | |
| 
 | |
| int cl_setenv(const char *name, const char *value)
 | |
| {
 | |
| 	wchar_t *wide_name, *wide_value = NULL;
 | |
| 
 | |
| 	cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0);
 | |
| 
 | |
| 	if (value) {
 | |
| 		cl_assert(git__utf8_to_16_alloc(&wide_value, value) >= 0);
 | |
| 		cl_assert(SetEnvironmentVariableW(wide_name, wide_value));
 | |
| 	} else {
 | |
| 		/* Windows XP returns 0 (failed) when passing NULL for lpValue when
 | |
| 		* lpName does not exist in the environment block. This behavior
 | |
| 		* seems to have changed in later versions. Don't check the return value
 | |
| 		* of SetEnvironmentVariable when passing NULL for lpValue. */
 | |
| 		SetEnvironmentVariableW(wide_name, NULL);
 | |
| 	}
 | |
| 
 | |
| 	git__free(wide_name);
 | |
| 	git__free(wide_value);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* This function performs retries on calls to MoveFile in order
 | |
|  * to provide enhanced reliability in the face of antivirus
 | |
|  * agents that may be scanning the source (or in the case that
 | |
|  * the source is a directory, a child of the source). */
 | |
| int cl_rename(const char *source, const char *dest)
 | |
| {
 | |
| 	git_win32_path source_utf16;
 | |
| 	git_win32_path dest_utf16;
 | |
| 	unsigned retries = 1;
 | |
| 
 | |
| 	cl_assert(git_win32_path_from_utf8(source_utf16, source) >= 0);
 | |
| 	cl_assert(git_win32_path_from_utf8(dest_utf16, dest) >= 0);
 | |
| 
 | |
| 	while (!MoveFileW(source_utf16, dest_utf16)) {
 | |
| 		/* Only retry if the error is ERROR_ACCESS_DENIED;
 | |
| 		 * this may indicate that an antivirus agent is
 | |
| 		 * preventing the rename from source to target */
 | |
| 		if (retries > 5 ||
 | |
| 			ERROR_ACCESS_DENIED != GetLastError())
 | |
| 			return -1;
 | |
| 
 | |
| 		/* With 5 retries and a coefficient of 10ms, the maximum
 | |
| 		 * delay here is 550 ms */
 | |
| 		Sleep(10 * retries * retries);
 | |
| 		retries++;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| #include <stdlib.h>
 | |
| char *cl_getenv(const char *name)
 | |
| {
 | |
| 	return getenv(name);
 | |
| }
 | |
| 
 | |
| int cl_setenv(const char *name, const char *value)
 | |
| {
 | |
| 	return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
 | |
| }
 | |
| 
 | |
| int cl_rename(const char *source, const char *dest)
 | |
| {
 | |
| 	return p_rename(source, dest);
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static const char *_cl_sandbox = NULL;
 | |
| static git_repository *_cl_repo = NULL;
 | |
| 
 | |
| git_repository *cl_git_sandbox_init(const char *sandbox)
 | |
| {
 | |
| 	/* Copy the whole sandbox folder from our fixtures to our test sandbox
 | |
| 	 * area.  After this it can be accessed with `./sandbox`
 | |
| 	 */
 | |
| 	cl_fixture_sandbox(sandbox);
 | |
| 	_cl_sandbox = sandbox;
 | |
| 
 | |
| 	cl_git_pass(p_chdir(sandbox));
 | |
| 
 | |
| 	/* If this is not a bare repo, then rename `sandbox/.gitted` to
 | |
| 	 * `sandbox/.git` which must be done since we cannot store a folder
 | |
| 	 * named `.git` inside the fixtures folder of our libgit2 repo.
 | |
| 	 */
 | |
| 	if (p_access(".gitted", F_OK) == 0)
 | |
| 		cl_git_pass(cl_rename(".gitted", ".git"));
 | |
| 
 | |
| 	/* If we have `gitattributes`, rename to `.gitattributes`.  This may
 | |
| 	 * be necessary if we don't want the attributes to be applied in the
 | |
| 	 * libgit2 repo, but just during testing.
 | |
| 	 */
 | |
| 	if (p_access("gitattributes", F_OK) == 0)
 | |
| 		cl_git_pass(cl_rename("gitattributes", ".gitattributes"));
 | |
| 
 | |
| 	/* As with `gitattributes`, we may need `gitignore` just for testing. */
 | |
| 	if (p_access("gitignore", F_OK) == 0)
 | |
| 		cl_git_pass(cl_rename("gitignore", ".gitignore"));
 | |
| 
 | |
| 	cl_git_pass(p_chdir(".."));
 | |
| 
 | |
| 	/* Now open the sandbox repository and make it available for tests */
 | |
| 	cl_git_pass(git_repository_open(&_cl_repo, sandbox));
 | |
| 
 | |
| 	/* Adjust configs after copying to new filesystem */
 | |
| 	cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
 | |
| 
 | |
| 	return _cl_repo;
 | |
| }
 | |
| 
 | |
| git_repository *cl_git_sandbox_reopen(void)
 | |
| {
 | |
| 	if (_cl_repo) {
 | |
| 		git_repository_free(_cl_repo);
 | |
| 		_cl_repo = NULL;
 | |
| 
 | |
| 		cl_git_pass(git_repository_open(&_cl_repo, _cl_sandbox));
 | |
| 	}
 | |
| 
 | |
| 	return _cl_repo;
 | |
| }
 | |
| 
 | |
| void cl_git_sandbox_cleanup(void)
 | |
| {
 | |
| 	if (_cl_repo) {
 | |
| 		git_repository_free(_cl_repo);
 | |
| 		_cl_repo = NULL;
 | |
| 	}
 | |
| 	if (_cl_sandbox) {
 | |
| 		cl_fixture_cleanup(_cl_sandbox);
 | |
| 		_cl_sandbox = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool cl_toggle_filemode(const char *filename)
 | |
| {
 | |
| 	struct stat st1, st2;
 | |
| 
 | |
| 	cl_must_pass(p_stat(filename, &st1));
 | |
| 	cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
 | |
| 	cl_must_pass(p_stat(filename, &st2));
 | |
| 
 | |
| 	return (st1.st_mode != st2.st_mode);
 | |
| }
 | |
| 
 | |
| bool cl_is_chmod_supported(void)
 | |
| {
 | |
| 	static int _is_supported = -1;
 | |
| 
 | |
| 	if (_is_supported < 0) {
 | |
| 		cl_git_mkfile("filemode.t", "Test if filemode can be modified");
 | |
| 		_is_supported = cl_toggle_filemode("filemode.t");
 | |
| 		cl_must_pass(p_unlink("filemode.t"));
 | |
| 	}
 | |
| 
 | |
| 	return _is_supported;
 | |
| }
 | |
| 
 | |
| const char* cl_git_fixture_url(const char *fixturename)
 | |
| {
 | |
| 	return cl_git_path_url(cl_fixture(fixturename));
 | |
| }
 | |
| 
 | |
| const char* cl_git_path_url(const char *path)
 | |
| {
 | |
| 	static char url[4096];
 | |
| 
 | |
| 	const char *in_buf;
 | |
| 	git_buf path_buf = GIT_BUF_INIT;
 | |
| 	git_buf url_buf = GIT_BUF_INIT;
 | |
| 
 | |
| 	cl_git_pass(git_path_prettify_dir(&path_buf, path, NULL));
 | |
| 	cl_git_pass(git_buf_puts(&url_buf, "file://"));
 | |
| 
 | |
| #ifdef GIT_WIN32
 | |
| 	/*
 | |
| 	 * A FILE uri matches the following format: file://[host]/path
 | |
| 	 * where "host" can be empty and "path" is an absolute path to the resource.
 | |
| 	 *
 | |
| 	 * In this test, no hostname is used, but we have to ensure the leading triple slashes:
 | |
| 	 *
 | |
| 	 * *nix: file:///usr/home/...
 | |
| 	 * Windows: file:///C:/Users/...
 | |
| 	 */
 | |
| 	cl_git_pass(git_buf_putc(&url_buf, '/'));
 | |
| #endif
 | |
| 
 | |
| 	in_buf = git_buf_cstr(&path_buf);
 | |
| 
 | |
| 	/*
 | |
| 	 * A very hacky Url encoding that only takes care of escaping the spaces
 | |
| 	 */
 | |
| 	while (*in_buf) {
 | |
| 		if (*in_buf == ' ')
 | |
| 			cl_git_pass(git_buf_puts(&url_buf, "%20"));
 | |
| 		else
 | |
| 			cl_git_pass(git_buf_putc(&url_buf, *in_buf));
 | |
| 
 | |
| 		in_buf++;
 | |
| 	}
 | |
| 
 | |
| 	strncpy(url, git_buf_cstr(&url_buf), 4096);
 | |
| 	git_buf_free(&url_buf);
 | |
| 	git_buf_free(&path_buf);
 | |
| 	return url;
 | |
| }
 | |
| 
 | |
| typedef struct {
 | |
| 	const char *filename;
 | |
| 	size_t filename_len;
 | |
| } remove_data;
 | |
| 
 | |
| static int remove_placeholders_recurs(void *_data, git_buf *path)
 | |
| {
 | |
| 	remove_data *data = (remove_data *)_data;
 | |
| 	size_t pathlen;
 | |
| 
 | |
| 	if (git_path_isdir(path->ptr) == true)
 | |
| 		return git_path_direach(path, 0, remove_placeholders_recurs, data);
 | |
| 
 | |
| 	pathlen = path->size;
 | |
| 
 | |
| 	if (pathlen < data->filename_len)
 | |
| 		return 0;
 | |
| 
 | |
| 	/* if path ends in '/'+filename (or equals filename) */
 | |
| 	if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
 | |
| 		(pathlen == data->filename_len ||
 | |
| 		 path->ptr[pathlen - data->filename_len - 1] == '/'))
 | |
| 		return p_unlink(path->ptr);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int cl_git_remove_placeholders(const char *directory_path, const char *filename)
 | |
| {
 | |
| 	int error;
 | |
| 	remove_data data;
 | |
| 	git_buf buffer = GIT_BUF_INIT;
 | |
| 
 | |
| 	if (git_path_isdir(directory_path) == false)
 | |
| 		return -1;
 | |
| 
 | |
| 	if (git_buf_sets(&buffer, directory_path) < 0)
 | |
| 		return -1;
 | |
| 
 | |
| 	data.filename = filename;
 | |
| 	data.filename_len = strlen(filename);
 | |
| 
 | |
| 	error = remove_placeholders_recurs(&data, &buffer);
 | |
| 
 | |
| 	git_buf_free(&buffer);
 | |
| 
 | |
| 	return error;
 | |
| }
 | |
| 
 | |
| #define CL_COMMIT_NAME "Libgit2 Tester"
 | |
| #define CL_COMMIT_EMAIL "libgit2-test@github.com"
 | |
| #define CL_COMMIT_MSG "Test commit of tree "
 | |
| 
 | |
| void cl_repo_commit_from_index(
 | |
| 	git_oid *out,
 | |
| 	git_repository *repo,
 | |
| 	git_signature *sig,
 | |
| 	git_time_t time,
 | |
| 	const char *msg)
 | |
| {
 | |
| 	git_index *index;
 | |
| 	git_oid commit_id, tree_id;
 | |
| 	git_object *parent = NULL;
 | |
| 	git_reference *ref = NULL;
 | |
| 	git_tree *tree = NULL;
 | |
| 	char buf[128];
 | |
| 	int free_sig = (sig == NULL);
 | |
| 
 | |
| 	/* it is fine if looking up HEAD fails - we make this the first commit */
 | |
| 	git_revparse_ext(&parent, &ref, repo, "HEAD");
 | |
| 
 | |
| 	/* write the index content as a tree */
 | |
| 	cl_git_pass(git_repository_index(&index, repo));
 | |
| 	cl_git_pass(git_index_write_tree(&tree_id, index));
 | |
| 	cl_git_pass(git_index_write(index));
 | |
| 	git_index_free(index);
 | |
| 
 | |
| 	cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
 | |
| 
 | |
| 	if (sig)
 | |
| 		cl_assert(sig->name && sig->email);
 | |
| 	else if (!time)
 | |
| 		cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
 | |
| 	else
 | |
| 		cl_git_pass(git_signature_new(
 | |
| 			&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
 | |
| 
 | |
| 	if (!msg) {
 | |
| 		strcpy(buf, CL_COMMIT_MSG);
 | |
| 		git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
 | |
| 			sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
 | |
| 		msg = buf;
 | |
| 	}
 | |
| 
 | |
| 	cl_git_pass(git_commit_create_v(
 | |
| 		&commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
 | |
| 		sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
 | |
| 
 | |
| 	if (out)
 | |
| 		git_oid_cpy(out, &commit_id);
 | |
| 
 | |
| 	git_object_free(parent);
 | |
| 	git_reference_free(ref);
 | |
| 	if (free_sig)
 | |
| 		git_signature_free(sig);
 | |
| 	git_tree_free(tree);
 | |
| }
 | |
| 
 | |
| void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
 | |
| {
 | |
| 	git_config *config;
 | |
| 	cl_git_pass(git_repository_config(&config, repo));
 | |
| 	cl_git_pass(git_config_set_bool(config, cfg, value != 0));
 | |
| 	git_config_free(config);
 | |
| }
 | |
| 
 | |
| int cl_repo_get_bool(git_repository *repo, const char *cfg)
 | |
| {
 | |
| 	int val = 0;
 | |
| 	git_config *config;
 | |
| 	cl_git_pass(git_repository_config(&config, repo));
 | |
| 	if (git_config_get_bool(&val, config, cfg) < 0)
 | |
| 		giterr_clear();
 | |
| 	git_config_free(config);
 | |
| 	return val;
 | |
| }
 | |
| 
 | |
| void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value)
 | |
| {
 | |
| 	git_config *config;
 | |
| 	cl_git_pass(git_repository_config(&config, repo));
 | |
| 	cl_git_pass(git_config_set_string(config, cfg, value));
 | |
| 	git_config_free(config);
 | |
| }
 | |
| 
 | |
| /* this is essentially the code from git__unescape modified slightly */
 | |
| static size_t strip_cr_from_buf(char *start, size_t len)
 | |
| {
 | |
| 	char *scan, *trail, *end = start + len;
 | |
| 
 | |
| 	for (scan = trail = start; scan < end; trail++, scan++) {
 | |
| 		while (*scan == '\r')
 | |
| 			scan++; /* skip '\r' */
 | |
| 
 | |
| 		if (trail != scan)
 | |
| 			*trail = *scan;
 | |
| 	}
 | |
| 
 | |
| 	*trail = '\0';
 | |
| 
 | |
| 	return (trail - start);
 | |
| }
 | |
| 
 | |
| void clar__assert_equal_file(
 | |
| 	const char *expected_data,
 | |
| 	size_t expected_bytes,
 | |
| 	int ignore_cr,
 | |
| 	const char *path,
 | |
| 	const char *file,
 | |
| 	int line)
 | |
| {
 | |
| 	char buf[4000];
 | |
| 	ssize_t bytes, total_bytes = 0;
 | |
| 	int fd = p_open(path, O_RDONLY | O_BINARY);
 | |
| 	cl_assert(fd >= 0);
 | |
| 
 | |
| 	if (expected_data && !expected_bytes)
 | |
| 		expected_bytes = strlen(expected_data);
 | |
| 
 | |
| 	while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
 | |
| 		clar__assert(
 | |
| 			bytes > 0, file, line, "error reading from file", path, 1);
 | |
| 
 | |
| 		if (ignore_cr)
 | |
| 			bytes = strip_cr_from_buf(buf, bytes);
 | |
| 
 | |
| 		if (memcmp(expected_data, buf, bytes) != 0) {
 | |
| 			int pos;
 | |
| 			for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
 | |
| 				/* find differing byte offset */;
 | |
| 			p_snprintf(
 | |
| 				buf, sizeof(buf), "file content mismatch at byte %d",
 | |
| 				(int)(total_bytes + pos));
 | |
| 			p_close(fd);
 | |
| 			clar__fail(file, line, path, buf, 1);
 | |
| 		}
 | |
| 
 | |
| 		expected_data += bytes;
 | |
| 		total_bytes   += bytes;
 | |
| 	}
 | |
| 
 | |
| 	p_close(fd);
 | |
| 
 | |
| 	clar__assert(!bytes, file, line, "error reading from file", path, 1);
 | |
| 	clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ,
 | |
| 		(size_t)expected_bytes, (size_t)total_bytes);
 | |
| }
 | |
| 
 | |
| static char *_cl_restore_home = NULL;
 | |
| 
 | |
| void cl_fake_home_cleanup(void *payload)
 | |
| {
 | |
| 	char *restore = _cl_restore_home;
 | |
| 	_cl_restore_home = NULL;
 | |
| 
 | |
| 	GIT_UNUSED(payload);
 | |
| 
 | |
| 	if (restore) {
 | |
| 		cl_git_pass(git_libgit2_opts(
 | |
| 			GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore));
 | |
| 		git__free(restore);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void cl_fake_home(void)
 | |
| {
 | |
| 	git_buf path = GIT_BUF_INIT;
 | |
| 
 | |
| 	cl_git_pass(git_libgit2_opts(
 | |
| 		GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &path));
 | |
| 
 | |
| 	_cl_restore_home = git_buf_detach(&path);
 | |
| 	cl_set_cleanup(cl_fake_home_cleanup, NULL);
 | |
| 
 | |
| 	if (!git_path_exists("home"))
 | |
| 		cl_must_pass(p_mkdir("home", 0777));
 | |
| 	cl_git_pass(git_path_prettify(&path, "home", NULL));
 | |
| 	cl_git_pass(git_libgit2_opts(
 | |
| 		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
 | |
| 	git_buf_free(&path);
 | |
| }
 | |
| 
 | |
| void cl_sandbox_set_search_path_defaults(void)
 | |
| {
 | |
| 	const char *sandbox_path = clar_sandbox_path();
 | |
| 
 | |
| 	git_libgit2_opts(
 | |
| 		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path);
 | |
| 	git_libgit2_opts(
 | |
| 		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path);
 | |
| 	git_libgit2_opts(
 | |
| 		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, sandbox_path);
 | |
| }
 | |
| 
 | |
| #ifdef GIT_WIN32
 | |
| bool cl_sandbox_supports_8dot3(void)
 | |
| {
 | |
| 	git_buf longpath = GIT_BUF_INIT;
 | |
| 	char *shortname;
 | |
| 	bool supported;
 | |
| 
 | |
| 	cl_git_pass(
 | |
| 		git_buf_joinpath(&longpath, clar_sandbox_path(), "longer_than_8dot3"));
 | |
| 
 | |
| 	cl_git_write2file(longpath.ptr, "", 0, O_RDWR|O_CREAT, 0666);
 | |
| 	shortname = git_win32_path_8dot3_name(longpath.ptr);
 | |
| 
 | |
| 	supported = (shortname != NULL);
 | |
| 
 | |
| 	git__free(shortname);
 | |
| 	git_buf_free(&longpath);
 | |
| 
 | |
| 	return supported;
 | |
| }
 | |
| #endif
 | |
| 
 | 
