diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2017-06-04 19:03:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-04 19:03:07 +0200 |
commit | 04de614b1f421d99240d049763546f2d06421248 (patch) | |
tree | f11ccf650db4d5593b71edd76e5de8d69d250a2f | |
parent | a1023a43027207ac7a5df7233bddfe7347bee256 (diff) | |
parent | 2696c5c3ece1a28c545d6e59b0d121480e9f977d (diff) | |
download | libgit2-04de614b1f421d99240d049763546f2d06421248.tar.gz |
Merge pull request #4243 from pks-t/pks/submodule-workdir
Submodule working directory
-rw-r--r-- | src/repository.c | 97 | ||||
-rw-r--r-- | tests/submodule/open.c | 90 |
2 files changed, 149 insertions, 38 deletions
diff --git a/src/repository.c b/src/repository.c index 48e27069d..d0a38cc6a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -422,10 +422,10 @@ static int read_gitfile(git_buf *path_out, const char *file_path) } static int find_repo( - git_buf *repo_path, - git_buf *parent_path, - git_buf *link_path, - git_buf *common_path, + git_buf *gitdir_path, + git_buf *workdir_path, + git_buf *gitlink_path, + git_buf *commondir_path, const char *start_path, uint32_t flags, const char *ceiling_dirs) @@ -440,7 +440,7 @@ static int find_repo( bool in_dot_git; size_t ceiling_offset = 0; - git_buf_free(repo_path); + git_buf_clear(gitdir_path); error = git_path_prettify(&path, start_path, NULL); if (error < 0) @@ -482,13 +482,13 @@ static int find_repo( if (S_ISDIR(st.st_mode)) { if (valid_repository_path(&path, &common_link)) { git_path_to_dir(&path); - git_buf_set(repo_path, path.ptr, path.size); + git_buf_set(gitdir_path, path.ptr, path.size); - if (link_path) - git_buf_attach(link_path, + if (gitlink_path) + git_buf_attach(gitlink_path, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0); - if (common_path) - git_buf_swap(&common_link, common_path); + if (commondir_path) + git_buf_swap(&common_link, commondir_path); break; } @@ -498,12 +498,12 @@ static int find_repo( if (error < 0) break; if (valid_repository_path(&repo_link, &common_link)) { - git_buf_swap(repo_path, &repo_link); + git_buf_swap(gitdir_path, &repo_link); - if (link_path) - error = git_buf_put(link_path, path.ptr, path.size); - if (common_path) - git_buf_swap(&common_link, common_path); + if (gitlink_path) + error = git_buf_put(gitlink_path, path.ptr, path.size); + if (commondir_path) + git_buf_swap(&common_link, commondir_path); } break; } @@ -529,20 +529,20 @@ static int find_repo( break; } - if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { - if (!git_buf_len(repo_path)) - git_buf_clear(parent_path); + if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { + if (!git_buf_len(gitdir_path)) + git_buf_clear(workdir_path); else { - git_path_dirname_r(parent_path, path.ptr); - git_path_to_dir(parent_path); + git_path_dirname_r(workdir_path, path.ptr); + git_path_to_dir(workdir_path); } - if (git_buf_oom(parent_path)) + if (git_buf_oom(workdir_path)) return -1; } /* If we didn't find the repository, and we don't have any other error * to report, report that. */ - if (!git_buf_len(repo_path) && !error) { + if (!git_buf_len(gitdir_path) && !error) { giterr_set(GITERR_REPOSITORY, "could not find repository from '%s'", start_path); error = GIT_ENOTFOUND; @@ -758,6 +758,29 @@ success: return error; } +static int repo_is_worktree(unsigned *out, const git_repository *repo) +{ + git_buf gitdir_link = GIT_BUF_INIT; + int error; + + /* Worktrees cannot have the same commondir and gitdir */ + if (repo->commondir && repo->gitdir + && !strcmp(repo->commondir, repo->gitdir)) { + *out = 0; + return 0; + } + + if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0) + return -1; + + /* A 'gitdir' file inside a git directory is currently + * only used when the repository is a working tree. */ + *out = !!git_path_exists(gitdir_link.ptr); + + git_buf_free(&gitdir_link); + return error; +} + int git_repository_open_ext( git_repository **repo_ptr, const char *start_path, @@ -765,8 +788,9 @@ int git_repository_open_ext( const char *ceiling_dirs) { int error; - git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT, - link_path = GIT_BUF_INIT, common_path = GIT_BUF_INIT; + unsigned is_worktree; + git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT, + gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT; git_repository *repo; git_config *config = NULL; @@ -777,7 +801,7 @@ int git_repository_open_ext( *repo_ptr = NULL; error = find_repo( - &path, &parent, &link_path, &common_path, start_path, flags, ceiling_dirs); + &gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs); if (error < 0 || !repo_ptr) return error; @@ -785,24 +809,21 @@ int git_repository_open_ext( repo = repository_alloc(); GITERR_CHECK_ALLOC(repo); - repo->gitdir = git_buf_detach(&path); + repo->gitdir = git_buf_detach(&gitdir); GITERR_CHECK_ALLOC(repo->gitdir); - if (link_path.size) { - repo->gitlink = git_buf_detach(&link_path); + if (gitlink.size) { + repo->gitlink = git_buf_detach(&gitlink); GITERR_CHECK_ALLOC(repo->gitlink); } - if (common_path.size) { - repo->commondir = git_buf_detach(&common_path); + if (commondir.size) { + repo->commondir = git_buf_detach(&commondir); GITERR_CHECK_ALLOC(repo->commondir); } - if ((error = git_buf_joinpath(&path, repo->gitdir, "gitdir")) < 0) + if ((error = repo_is_worktree(&is_worktree, repo)) < 0) goto cleanup; - /* A 'gitdir' file inside a git directory is currently - * only used when the repository is a working tree. */ - if (git_path_exists(path.ptr)) - repo->is_worktree = 1; + repo->is_worktree = is_worktree; /* * We'd like to have the config, but git doesn't particularly @@ -822,13 +843,13 @@ int git_repository_open_ext( if (config && ((error = load_config_data(repo, config)) < 0 || - (error = load_workdir(repo, config, &parent)) < 0)) + (error = load_workdir(repo, config, &workdir)) < 0)) goto cleanup; } cleanup: - git_buf_free(&path); - git_buf_free(&parent); + git_buf_free(&gitdir); + git_buf_free(&workdir); git_config_free(config); if (error < 0) diff --git a/tests/submodule/open.c b/tests/submodule/open.c new file mode 100644 index 000000000..0ef01ec24 --- /dev/null +++ b/tests/submodule/open.c @@ -0,0 +1,90 @@ +#include "clar_libgit2.h" +#include "submodule_helpers.h" +#include "path.h" + +static git_repository *g_parent; +static git_repository *g_child; +static git_submodule *g_module; + +void test_submodule_open__initialize(void) +{ + g_parent = setup_fixture_submod2(); +} + +void test_submodule_open__cleanup(void) +{ + git_submodule_free(g_module); + git_repository_free(g_child); + cl_git_sandbox_cleanup(); + g_parent = NULL; + g_child = NULL; + g_module = NULL; +} + +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_free(&expected); + git_buf_free(&actual); +} + +void test_submodule_open__opening_via_lookup_succeeds(void) +{ + cl_git_pass(git_submodule_lookup(&g_module, g_parent, "sm_unchanged")); + cl_git_pass(git_submodule_open(&g_child, g_module)); + assert_sm_valid(g_parent, g_child, "sm_unchanged"); +} + +void test_submodule_open__direct_open_succeeds(void) +{ + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged")); + cl_git_pass(git_repository_open(&g_child, path.ptr)); + assert_sm_valid(g_parent, g_child, "sm_unchanged"); + + git_buf_free(&path); +} + +void test_submodule_open__direct_open_succeeds_for_broken_sm_with_gitdir(void) +{ + git_buf path = GIT_BUF_INIT; + + /* + * This is actually not a valid submodule, but we + * encountered at least one occasion where the gitdir + * file existed inside of a submodule's gitdir. As we are + * now able to open these submodules correctly, we still + * add a test for this. + */ + cl_git_mkfile("submod2/.git/modules/sm_unchanged/gitdir", ".git"); + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged")); + cl_git_pass(git_repository_open(&g_child, path.ptr)); + assert_sm_valid(g_parent, g_child, "sm_unchanged"); + + git_buf_free(&path); +} |