diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc index ed7d80c690..fb5aceae8f 100644 --- a/Documentation/git-repo.adoc +++ b/Documentation/git-repo.adoc @@ -113,12 +113,69 @@ values that they return: The path to the Git repository's common directory relative to the current working directory. +`path.git-prefix`:: + The path from the root of the working tree to the current working + directory. Returns the empty string when the current working directory + is the root of the working tree. + `path.gitdir.absolute`:: The canonical absolute path to the Git repository directory (the `.git` directory). `path.gitdir.relative`:: The path to the Git repository directory relative to the current working directory. +`path.grafts.absolute`:: + The canonical absolute path to the repository's graft file. + Respects the `GIT_GRAFT_FILE` environment override. The path is + returned regardless of whether the file currently exists on disk. + +`path.grafts.relative`:: + The path to the repository's graft file relative to the current + working directory. Respects the `GIT_GRAFT_FILE` environment + override. The path is returned regardless of whether the file + currently exists on disk. + +`path.hooks.absolute`:: + The canonical absolute path to the repository's hooks directory. + Respects the `core.hooksPath` configuration. If `core.hooksPath` is + set to `/dev/null`, that value is returned unchanged. + +`path.hooks.relative`:: + The path to the repository's hooks directory relative to the current + working directory. Respects the `core.hooksPath` configuration. + +`path.index.absolute`:: + The canonical absolute path to the repository's current index file. + Respects the `GIT_INDEX_FILE` environment override. Returns the + configured index path even if the repository is bare or the file does + not exist. + +`path.index.relative`:: + The path to the repository's current index file relative to the current + working directory. Respects the `GIT_INDEX_FILE` environment override. + Returns the configured index path even if the repository is bare or the + file does not exist. + +`path.superproject-root.absolute`:: + The canonical absolute path to the working tree root of the superproject + if the current repository is an initialized submodule. Outputs an empty + string if not in a submodule. + +`path.superproject-root.relative`:: + The path to the working tree root of the superproject relative to the + current working directory if the current repository is an initialized + submodule. Outputs an empty string if not in a submodule. + +`path.toplevel.absolute`:: + The canonical absolute path to the top-level directory of the + repository's working tree. Outputs an empty string if the repository + is bare. + +`path.toplevel.relative`:: + The path to the top-level directory of the repository's working + tree relative to the current working directory. Outputs an empty + string if the repository is bare. + `references.format`:: The reference storage format. The valid values are: + diff --git a/builtin/repo.c b/builtin/repo.c index 84e012f83f..8e9f6296b8 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -14,10 +14,10 @@ #include "ref-filter.h" #include "refs.h" #include "revision.h" -#include "setup.h" #include "strbuf.h" #include "string-list.h" #include "shallow.h" +#include "submodule.h" #include "tree.h" #include "tree-walk.h" #include "utf8.h" @@ -99,6 +99,16 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b return 0; } +static int get_path_git_prefix(struct repository *repo, struct strbuf *buf) +{ + /* + * repo->prefix is NULL when the current working directory is + * the worktree root. + */ + strbuf_addstr(buf, repo->prefix ? repo->prefix : ""); + return 0; +} + static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf) { const char *git_dir = repo_get_git_dir(repo); @@ -121,6 +131,120 @@ static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf) return 0; } +static int get_path_grafts_absolute(struct repository *repo, struct strbuf *buf) +{ + const char *graft_file = repo_get_graft_file(repo); + + if (!graft_file) + return error(_("unable to get graft file")); + + format_path(buf, graft_file, "", PATH_FORMAT_CANONICAL); + return 0; +} + +static int get_path_grafts_relative(struct repository *repo, struct strbuf *buf) +{ + const char *graft_file = repo_get_graft_file(repo); + + if (!graft_file) + return error(_("unable to get graft file")); + + format_path(buf, graft_file, repo->prefix, PATH_FORMAT_RELATIVE); + return 0; +} + +static int get_path_hooks_absolute(struct repository *repo, struct strbuf *buf) +{ + struct strbuf hooks_path = STRBUF_INIT; + + repo_git_path_replace(repo, &hooks_path, "hooks"); + format_path(buf, hooks_path.buf, "", PATH_FORMAT_CANONICAL); + strbuf_release(&hooks_path); + return 0; +} + +static int get_path_hooks_relative(struct repository *repo, struct strbuf *buf) +{ + struct strbuf hooks_path = STRBUF_INIT; + + repo_git_path_replace(repo, &hooks_path, "hooks"); + format_path(buf, hooks_path.buf, repo->prefix, PATH_FORMAT_RELATIVE); + strbuf_release(&hooks_path); + return 0; +} + +static int get_path_index_absolute(struct repository *repo, struct strbuf *buf) +{ + const char *index_file = repo_get_index_file(repo); + + if (!index_file) + return error(_("unable to get index file")); + + format_path(buf, index_file, "", PATH_FORMAT_CANONICAL); + return 0; +} + +static int get_path_index_relative(struct repository *repo, struct strbuf *buf) +{ + const char *index_file = repo_get_index_file(repo); + + if (!index_file) + return error(_("unable to get index file")); + + format_path(buf, index_file, repo->prefix, PATH_FORMAT_RELATIVE); + return 0; +} + +static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf) +{ + struct strbuf superproject = STRBUF_INIT; + + if (!get_superproject_working_tree(&superproject)) { + strbuf_release(&superproject); + return 0; + } + + format_path(buf, superproject.buf, "", PATH_FORMAT_CANONICAL); + strbuf_release(&superproject); + return 0; +} + +static int get_path_superproject_relative(struct repository *repo, struct strbuf *buf) +{ + struct strbuf superproject = STRBUF_INIT; + + if (!get_superproject_working_tree(&superproject)) { + strbuf_release(&superproject); + return 0; + } + + format_path(buf, superproject.buf, repo->prefix, PATH_FORMAT_RELATIVE); + strbuf_release(&superproject); + return 0; +} + +static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf) +{ + const char *work_tree = repo_get_work_tree(repo); + + if (!work_tree) + return 0; + + format_path(buf, work_tree, "", PATH_FORMAT_CANONICAL); + return 0; +} + +static int get_path_toplevel_relative(struct repository *repo, struct strbuf *buf) +{ + const char *work_tree = repo_get_work_tree(repo); + + if (!work_tree) + return 0; + + format_path(buf, work_tree, repo->prefix, PATH_FORMAT_RELATIVE); + return 0; +} + static int get_references_format(struct repository *repo, struct strbuf *buf) { strbuf_addstr(buf, @@ -135,8 +259,19 @@ static const struct repo_info_field repo_info_field[] = { { "object.format", get_object_format }, { "path.commondir.absolute", get_path_commondir_absolute }, { "path.commondir.relative", get_path_commondir_relative }, + { "path.git-prefix", get_path_git_prefix }, { "path.gitdir.absolute", get_path_gitdir_absolute }, { "path.gitdir.relative", get_path_gitdir_relative }, + { "path.grafts.absolute", get_path_grafts_absolute }, + { "path.grafts.relative", get_path_grafts_relative }, + { "path.hooks.absolute", get_path_hooks_absolute }, + { "path.hooks.relative", get_path_hooks_relative }, + { "path.index.absolute", get_path_index_absolute }, + { "path.index.relative", get_path_index_relative }, + { "path.superproject-root.absolute", get_path_superproject_absolute }, + { "path.superproject-root.relative", get_path_superproject_relative }, + { "path.toplevel.absolute", get_path_toplevel_absolute }, + { "path.toplevel.relative", get_path_toplevel_relative }, { "references.format", get_references_format }, }; diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh index c85d390f43..b689445b7a 100755 --- a/t/t1900-repo-info.sh +++ b/t/t1900-repo-info.sh @@ -174,7 +174,11 @@ test_repo_info_path () { cd repo/sub && ROOT="$(test-tool path-utils real_path ..)" && export ROOT && eval "$init_command" && - echo "path.$field_name.absolute=$ROOT/$expected_dir" >expect && + case "$expected_dir" in + /*) EXPECT_ABS="$expected_dir" ;; + *) EXPECT_ABS="$ROOT/$expected_dir" ;; + esac && + echo "path.$field_name.absolute=$EXPECT_ABS" >expect && git repo info "path.$field_name.absolute" >actual && test_cmp expect actual ) @@ -188,7 +192,11 @@ test_repo_info_path () { cd repo/sub && ROOT="$(test-tool path-utils real_path ..)" && export ROOT && eval "$init_command" && - echo "path.$field_name.relative=../$expected_dir" >expect && + case "$expected_dir" in + /*) EXPECT_REL="$(test-tool path-utils relative_path "$expected_dir" "$PWD")" ;; + *) EXPECT_REL="../$expected_dir" ;; + esac && + echo "path.$field_name.relative=$EXPECT_REL" >expect && git repo info "path.$field_name.relative" >actual && test_cmp expect actual ) @@ -207,10 +215,152 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \ '.git' \ 'GIT_DIR="../.git" && export GIT_DIR' +test_expect_success 'path.git-prefix at repository root' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + echo "path.git-prefix=" >expect && + git repo info path.git-prefix >actual && + test_cmp expect actual + ) +' + +test_expect_success 'path.git-prefix in subdirectory' ' + test_when_finished "rm -rf repo" && + git init repo && + mkdir -p repo/sub/dir && + ( + cd repo/sub/dir && + echo "path.git-prefix=sub/dir/" >expect && + git repo info path.git-prefix >actual && + test_cmp expect actual + ) +' + test_repo_info_path 'gitdir standard' 'gitdir' '.git' test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \ '.git' \ 'GIT_DIR="../.git" && export GIT_DIR' +test_repo_info_path 'grafts standard' 'grafts' '.git/info/grafts' + +test_repo_info_path 'grafts with GIT_GRAFT_FILE override' 'grafts' \ + 'custom-graft-file' \ + 'GIT_GRAFT_FILE="$ROOT/custom-graft-file" && export GIT_GRAFT_FILE' + +test_repo_info_path 'hooks standard' 'hooks' '.git/hooks' + +test_repo_info_path 'hooks with core.hooksPath override' 'hooks' \ + 'custom-hooks' \ + 'git config core.hooksPath "$ROOT/custom-hooks" && mkdir -p "$ROOT/custom-hooks"' + +# /dev/null is not a real, canonicalizable filesystem path on Windows, +# so path resolution for core.hooksPath=/dev/null cannot be expected to +# produce a literal "/dev/null" the way it does on POSIX systems. +if ! test_have_prereq MINGW +then + test_repo_info_path 'hooks with core.hooksPath=/dev/null' 'hooks' \ + '/dev/null' \ + 'git config core.hooksPath /dev/null' +fi + +test_repo_info_path 'index standard' 'index' '.git/index' + +test_repo_info_path 'index with GIT_INDEX_FILE override' 'index' \ + 'custom-index-file' \ + 'GIT_INDEX_FILE="$ROOT/custom-index-file" && export GIT_INDEX_FILE' + +test_expect_success 'path.index in a bare repository returns default index location' ' + test_when_finished "rm -rf bare.git" && + git init --bare bare.git && + ( + cd bare.git && + ROOT="$(test-tool path-utils real_path .)" && + + echo "path.index.absolute=$ROOT/index" >expect.abs && + git repo info path.index.absolute >actual.abs && + test_cmp expect.abs actual.abs && + + echo "path.index.relative=index" >expect.rel && + git repo info path.index.relative >actual.rel && + test_cmp expect.rel actual.rel + ) +' + +test_expect_success 'path.superproject-root absolute and relative' ' + test_when_finished "rm -rf sub super" && + git init sub && + test_commit -C sub initial && + git init super && + ( + cd super && + git -c protocol.file.allow=always submodule add "../sub" sub && + git commit -m "add submodule" && + + cd sub && + ROOT="$(test-tool path-utils real_path ..)" && + + echo "path.superproject-root.absolute=$ROOT" >expect.abs && + git repo info path.superproject-root.absolute >actual.abs && + test_cmp expect.abs actual.abs && + + echo "path.superproject-root.relative=../" >expect.rel && + git repo info path.superproject-root.relative >actual.rel && + test_cmp expect.rel actual.rel + ) +' + +test_expect_success 'path.superproject-root returns empty when not in a submodule' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + + echo "path.superproject-root.absolute=" >expect.abs && + git repo info path.superproject-root.absolute >actual.abs && + test_cmp expect.abs actual.abs && + + echo "path.superproject-root.relative=" >expect.rel && + git repo info path.superproject-root.relative >actual.rel && + test_cmp expect.rel actual.rel + ) +' + +test_expect_success 'path.toplevel absolute and relative' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + mkdir -p repo/sub && + cd repo/sub && + + ROOT="$(test-tool path-utils real_path ..)" && + + echo "path.toplevel.absolute=$ROOT" >expect.abs && + git repo info path.toplevel.absolute >actual.abs && + test_cmp expect.abs actual.abs && + + echo "path.toplevel.relative=../" >expect.rel && + git repo info path.toplevel.relative >actual.rel && + test_cmp expect.rel actual.rel + ) +' + +test_expect_success 'path.toplevel absolute and relative in a bare repository' ' + test_when_finished "rm -rf bare.git" && + git init --bare bare.git && + ( + cd bare.git && + + echo "path.toplevel.absolute=" >expect.abs && + git repo info path.toplevel.absolute >actual.abs && + test_cmp expect.abs actual.abs && + + echo "path.toplevel.relative=" >expect.rel && + git repo info path.toplevel.relative >actual.rel && + test_cmp expect.rel actual.rel + ) +' + test_done