static void assert_sm_valid(git_repository *parent, git_repository *child, const char *sm_name) { git_buf expected = GIT_BUF_INIT, actual = GIT_BUF_INIT; /* assert working directory */ cl_git_pass(git_buf_joinpath(&expected, git_repository_workdir(parent), sm_name)); cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL)); cl_git_pass(git_buf_sets(&actual, git_repository_workdir(child))); cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL)); cl_assert_equal_s(expected.ptr, actual.ptr); git_buf_clear(&expected); git_buf_clear(&actual); /* assert common directory */ cl_git_pass(git_buf_joinpath(&expected, git_repository_commondir(parent), "modules")); cl_git_pass(git_buf_joinpath(&expected, expected.ptr, sm_name)); cl_git_pass(git_path_prettify_dir(&expected, expected.ptr, NULL)); cl_git_pass(git_buf_sets(&actual, git_repository_commondir(child))); cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL)); cl_assert_equal_s(expected.ptr, actual.ptr); /* assert git directory */ cl_git_pass(git_buf_sets(&actual, git_repository_path(child))); cl_git_pass(git_path_prettify_dir(&actual, actual.ptr, NULL)); cl_assert_equal_s(expected.ptr, actual.ptr); git_buf_dispose(&expected); git_buf_dispose(&actual); }
void test_repo_init__bare_repo_escaping_current_workdir(void) { git_buf path_repository = GIT_BUF_INIT; git_buf path_current_workdir = GIT_BUF_INIT; cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL)); cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c")); cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), GIT_DIR_MODE)); /* Change the current working directory */ cl_git_pass(chdir(git_buf_cstr(&path_repository))); /* Initialize a bare repo with a relative path escaping out of the current working directory */ cl_git_pass(git_repository_init(&_repo, "../d/e.git", 1)); cl_git_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/")); git_repository_free(_repo); /* Open a bare repo with a relative path escaping out of the current working directory */ cl_git_pass(git_repository_open(&_repo, "../d/e.git")); cl_git_pass(chdir(git_buf_cstr(&path_current_workdir))); git_buf_free(&path_current_workdir); git_buf_free(&path_repository); cleanup_repository("a"); }
int git_repository_open_bare( git_repository **repo_ptr, const char *bare_path) { int error; git_buf path = GIT_BUF_INIT; git_repository *repo = NULL; if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0) return error; if (!valid_repository_path(&path)) { git_buf_free(&path); giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path); return GIT_ENOTFOUND; } repo = repository_alloc(); GITERR_CHECK_ALLOC(repo); repo->path_repository = git_buf_detach(&path); GITERR_CHECK_ALLOC(repo->path_repository); /* of course we're bare! */ repo->is_bare = 1; repo->workdir = NULL; *repo_ptr = repo; return 0; }
/* * Read the contents of `file_path` and set `path_out` to the repo dir that * it points to. Before calling, set `path_out` to the base directory that * should be used if the contents of `file_path` are a relative path. */ static int read_gitfile(git_buf *path_out, const char *file_path) { int error = 0; git_buf file = GIT_BUF_INIT; size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX); assert(path_out && file_path); if (git_futils_readbuffer(&file, file_path) < 0) return -1; git_buf_rtrim(&file); /* apparently on Windows, some people use backslashes in paths */ git_path_mkposix(file.ptr); if (git_buf_len(&file) <= prefix_len || memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = git_buf_cstr(&file) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; error = git_path_prettify_dir( path_out, gitlink, git_buf_cstr(path_out)); } git_buf_free(&file); return error; }
static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { int error; const git_config_entry *ce; git_buf worktree = GIT_BUF_INIT; if (repo->is_bare) return 0; if ((error = git_config__lookup_entry( &ce, config, "core.worktree", false)) < 0) return error; if (ce && ce->value) { if ((error = git_path_prettify_dir( &worktree, ce->value, repo->path_repository)) < 0) return error; repo->workdir = git_buf_detach(&worktree); } else if (parent_path && git_path_isdir(parent_path->ptr)) repo->workdir = git_buf_detach(parent_path); else { if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || git_path_to_dir(&worktree) < 0) return -1; repo->workdir = git_buf_detach(&worktree); } GITERR_CHECK_ALLOC(repo->workdir); return 0; }
/* * Read the contents of `file_path` and set `path_out` to the repo dir that * it points to. Before calling, set `path_out` to the base directory that * should be used if the contents of `file_path` are a relative path. */ static int read_gitfile(git_buf *path_out, const char *file_path) { int error = 0; git_buf file = GIT_BUF_INIT; size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX); assert(path_out && file_path); if (git_futils_readbuffer(&file, file_path) < 0) return -1; git_buf_rtrim(&file); if (file.size <= prefix_len || memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = ((const char *)file.ptr) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; error = git_path_prettify_dir(path_out, gitlink, path_out->ptr); } git_buf_free(&file); return error; }
static void env_cd_( const char *path, void (*passfail_)(const char *, const char *, int), const char *file, int line) { git_buf cwd_buf = GIT_BUF_INIT; cl_git_pass(git_path_prettify_dir(&cwd_buf, ".", NULL)); cl_must_pass(p_chdir(path)); passfail_(NULL, file, line); cl_must_pass(p_chdir(git_buf_cstr(&cwd_buf))); git_buf_free(&cwd_buf); }
//no check is performed on ceiling_dirs length, so be sure it's long enough static void append_ceiling_dir(git_buf *ceiling_dirs, const char *path) { git_buf pretty_path = GIT_BUF_INIT; char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' }; cl_git_pass(git_path_prettify_dir(&pretty_path, path, NULL)); if (ceiling_dirs->size > 0) git_buf_puts(ceiling_dirs, ceiling_separator); git_buf_puts(ceiling_dirs, pretty_path.ptr); git_buf_free(&pretty_path); cl_assert(git_buf_oom(ceiling_dirs) == 0); }
void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void) { git_buf full_path = GIT_BUF_INIT; repo = cl_git_sandbox_init(BARE_REPO); cl_must_pass(p_mkdir(ELSEWHERE, 0777)); cl_must_pass(git_path_prettify_dir(&full_path, ELSEWHERE, NULL)); cl_must_pass(git_buf_puts(&full_path, "test.txt")); assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk); git_buf_free(&full_path); cl_must_pass(git_futils_rmdir_r(ELSEWHERE, GIT_DIRREMOVAL_FILES_AND_DIRS)); }
void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolute_filepath_pointing_outside_of_the_working_directory(void) { git_buf full_path = GIT_BUF_INIT; repo = cl_git_sandbox_init(WORKDIR); cl_must_pass(p_mkdir(ELSEWHERE, 0777)); cl_must_pass(git_path_prettify_dir(&full_path, ELSEWHERE, NULL)); cl_must_pass(git_buf_puts(&full_path, "test.txt")); assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk); git_buf_free(&full_path); cl_must_pass(git_futils_rmdir_r(ELSEWHERE, GIT_DIRREMOVAL_FILES_AND_DIRS)); }
int git_repository_set_workdir(git_repository *repo, const char *workdir) { git_buf path = GIT_BUF_INIT; assert(repo && workdir); if (git_path_prettify_dir(&path, workdir, NULL) < 0) return -1; git__free(repo->workdir); repo->workdir = git_buf_detach(&path); repo->is_bare = 0; return 0; }
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++; } cl_assert(url_buf.size < 4096); strncpy(url, git_buf_cstr(&url_buf), 4096); git_buf_free(&url_buf); git_buf_free(&path_buf); return url; }
static void build_local_file_url(git_buf *out, const char *fixture) { const char *in_buf; git_buf path_buf = GIT_BUF_INIT; cl_git_pass(git_path_prettify_dir(&path_buf, fixture, NULL)); cl_git_pass(git_buf_puts(out, "file://")); #ifdef _MSC_VER /* * 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(out, '/')); #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(out, "%20")); else cl_git_pass(git_buf_putc(out, *in_buf)); in_buf++; } git_buf_free(&path_buf); }
static void build_local_file_url(git_buf *out, const char *fixture) { git_buf path_buf = GIT_BUF_INIT; cl_git_pass(git_path_prettify_dir(&path_buf, cl_fixture(fixture), NULL)); cl_git_pass(git_buf_puts(out, "file://")); #ifdef _MSC_VER /* * 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(out, '/')); #endif cl_git_pass(git_buf_puts(out, git_buf_cstr(&path_buf))); git_buf_free(&path_buf); }
void test_repo_env__open(void) { git_repository *repo = NULL; git_buf repo_dir_buf = GIT_BUF_INIT; const char *repo_dir = NULL; git_index *index = NULL; const char *t_obj = "testrepo.git/objects"; const char *p_obj = "peeled.git/objects"; clear_git_env(); cl_fixture_sandbox("attr"); cl_fixture_sandbox("testrepo.git"); cl_fixture_sandbox("peeled.git"); cl_git_pass(p_rename("attr/.gitted", "attr/.git")); cl_git_pass(git_path_prettify_dir(&repo_dir_buf, "attr", NULL)); repo_dir = git_buf_cstr(&repo_dir_buf); /* GIT_DIR that doesn't exist */ cl_setenv("GIT_DIR", "does-not-exist"); env_fail(NULL); /* Explicit start_path overrides GIT_DIR */ env_pass("attr"); env_pass("attr/.git"); env_pass("attr/sub"); env_pass("attr/sub/sub"); /* GIT_DIR with relative paths */ cl_setenv("GIT_DIR", "attr/.git"); env_pass(NULL); cl_setenv("GIT_DIR", "attr"); env_fail(NULL); cl_setenv("GIT_DIR", "attr/sub"); env_fail(NULL); cl_setenv("GIT_DIR", "attr/sub/sub"); env_fail(NULL); /* GIT_DIR with absolute paths */ cl_setenv_printf("GIT_DIR", "%s/.git", repo_dir); env_pass(NULL); cl_setenv("GIT_DIR", repo_dir); env_fail(NULL); cl_setenv_printf("GIT_DIR", "%s/sub", repo_dir); env_fail(NULL); cl_setenv_printf("GIT_DIR", "%s/sub/sub", repo_dir); env_fail(NULL); cl_setenv("GIT_DIR", NULL); /* Searching from the current directory */ env_cd_pass("attr"); env_cd_pass("attr/.git"); env_cd_pass("attr/sub"); env_cd_pass("attr/sub/sub"); /* A ceiling directory blocks searches from ascending into that * directory, but doesn't block the start_path itself. */ cl_setenv("GIT_CEILING_DIRECTORIES", repo_dir); env_cd_pass("attr"); env_cd_fail("attr/sub"); env_cd_fail("attr/sub/sub"); cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s/sub", repo_dir); env_cd_pass("attr"); env_cd_pass("attr/sub"); env_cd_fail("attr/sub/sub"); /* Multiple ceiling directories */ cl_setenv_printf("GIT_CEILING_DIRECTORIES", "123%c%s/sub%cabc", GIT_PATH_LIST_SEPARATOR, repo_dir, GIT_PATH_LIST_SEPARATOR); env_cd_pass("attr"); env_cd_pass("attr/sub"); env_cd_fail("attr/sub/sub"); cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s%c%s/sub", repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir); env_cd_pass("attr"); env_cd_fail("attr/sub"); env_cd_fail("attr/sub/sub"); cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s/sub%c%s", repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir); env_cd_pass("attr"); env_cd_fail("attr/sub"); env_cd_fail("attr/sub/sub"); cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s%c%s/sub/sub", repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir); env_cd_pass("attr"); env_cd_fail("attr/sub"); env_cd_fail("attr/sub/sub"); cl_setenv("GIT_CEILING_DIRECTORIES", NULL); /* Index files */ cl_setenv("GIT_INDEX_FILE", cl_fixture("gitgit.index")); cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL)); cl_git_pass(git_repository_index(&index, repo)); cl_assert_equal_s(git_index_path(index), cl_fixture("gitgit.index")); cl_assert_equal_i(git_index_entrycount(index), 1437); git_index_free(index); git_repository_free(repo); cl_setenv("GIT_INDEX_FILE", NULL); /* Namespaces */ cl_setenv("GIT_NAMESPACE", "some-namespace"); cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL)); cl_assert_equal_s(git_repository_get_namespace(repo), "some-namespace"); git_repository_free(repo); cl_setenv("GIT_NAMESPACE", NULL); /* Object directories and alternates */ env_check_objects(true, false, false); cl_setenv("GIT_OBJECT_DIRECTORY", t_obj); env_check_objects(false, true, false); cl_setenv("GIT_OBJECT_DIRECTORY", NULL); cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", t_obj); env_check_objects(true, true, false); cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); cl_setenv("GIT_OBJECT_DIRECTORY", p_obj); env_check_objects(false, false, true); cl_setenv("GIT_OBJECT_DIRECTORY", NULL); cl_setenv("GIT_OBJECT_DIRECTORY", t_obj); cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", p_obj); env_check_objects(false, true, true); cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); cl_setenv("GIT_OBJECT_DIRECTORY", NULL); cl_setenv_printf("GIT_ALTERNATE_OBJECT_DIRECTORIES", "%s%c%s", t_obj, GIT_PATH_LIST_SEPARATOR, p_obj); env_check_objects(true, true, true); cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); cl_setenv_printf("GIT_ALTERNATE_OBJECT_DIRECTORIES", "%s%c%s", p_obj, GIT_PATH_LIST_SEPARATOR, t_obj); env_check_objects(true, true, true); cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL); cl_fixture_cleanup("peeled.git"); cl_fixture_cleanup("testrepo.git"); cl_fixture_cleanup("attr"); git_buf_free(&repo_dir_buf); clear_git_env(); }
static int find_repo( git_buf *repo_path, git_buf *parent_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) { int error; git_buf path = GIT_BUF_INIT; struct stat st; dev_t initial_device = 0; bool try_with_dot_git = false; int ceiling_offset; git_buf_free(repo_path); if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0) return error; ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) return error; while (!error && !git_buf_len(repo_path)) { if (p_stat(path.ptr, &st) == 0) { /* check that we have not crossed device boundaries */ if (initial_device == 0) initial_device = st.st_dev; else if (st.st_dev != initial_device && (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0) break; if (S_ISDIR(st.st_mode)) { if (valid_repository_path(&path)) { git_path_to_dir(&path); git_buf_set(repo_path, path.ptr, path.size); break; } } else if (S_ISREG(st.st_mode)) { git_buf repo_link = GIT_BUF_INIT; if (!(error = read_gitfile(&repo_link, path.ptr))) { if (valid_repository_path(&repo_link)) git_buf_swap(repo_path, &repo_link); git_buf_free(&repo_link); break; } git_buf_free(&repo_link); } } /* move up one directory level */ if (git_path_dirname_r(&path, path.ptr) < 0) { error = -1; break; } if (try_with_dot_git) { /* if we tried original dir with and without .git AND either hit * directory ceiling or NO_SEARCH was requested, then be done. */ if (path.ptr[ceiling_offset] == '\0' || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0) break; /* otherwise look first for .git item */ error = git_buf_joinpath(&path, path.ptr, DOT_GIT); } try_with_dot_git = !try_with_dot_git; } if (!error && parent_path != NULL) { if (!git_buf_len(repo_path)) git_buf_clear(parent_path); else { git_path_dirname_r(parent_path, path.ptr); git_path_to_dir(parent_path); } if (git_buf_oom(parent_path)) return -1; } git_buf_free(&path); if (!git_buf_len(repo_path) && !error) { giterr_set(GITERR_REPOSITORY, "Could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; } return error; }