Is there a way to get the git root directory in on

2018-12-31 18:42发布

Mercurial has a way of printing the root directory (that contains .hg) via

hg root

Is there something equivalent in git to get the directory that contains the .git directory?

22条回答
何处买醉
2楼-- · 2018-12-31 18:46

Had to solve this myself today. Solved it in C# as I needed it for a program, but I guess it can be esily rewritten. Consider this Public Domain.

public static string GetGitRoot (string file_path) {

    file_path = System.IO.Path.GetDirectoryName (file_path);

    while (file_path != null) {

        if (Directory.Exists (System.IO.Path.Combine (file_path, ".git")))
            return file_path;

        file_path = Directory.GetParent (file_path).FullName;

    }

    return null;

}
查看更多
有味是清欢
3楼-- · 2018-12-31 18:48

As others have noted, the core of the solution is to use git rev-parse --show-cdup. However, there are a few of edge cases to address:

  1. When the cwd already is the root of the working tree, the command yields an empty string.
    Actually it produces an empty line, but command substitution strip off the trailing line break. The final result is an empty string.

    Most answers suggest prepending the output with ./ so that an empty output becomes "./" before it is fed to cd.

  2. When GIT_WORK_TREE is set to a location that is not the parent of the cwd, the output may be an absolute pathname.

    Prepending ./ is wrong in this situation. If a ./ is prepended to an absolute path, it becomes a relative path (and they only refer to the same location if the cwd is the root directory of the system).

  3. The output may contain whitespace.

    This really only applies in the second case, but it has an easy fix: use double quotes around the command substitution (and any subsequent uses of the value).

As other answers have noted, we can do cd "./$(git rev-parse --show-cdup)", but this breaks in the second edge case (and the third edge case if we leave off the double quotes).

Many shells treat cd "" as a no-op, so for those shells we could do cd "$(git rev-parse --show-cdup)" (the double quotes protect the empty string as an argument in the first edge case, and preserve whitespace in the third edge case). POSIX says the result of cd "" is unspecified, so it may be best to avoid making this assumption.

A solution that works in all of the above cases requires a test of some sort. Done explicitly, it might look like this:

cdup="$(git rev-parse --show-cdup)" && test -n "$cdup" && cd "$cdup"

No cd is done for the first edge case.

If it is acceptable to run cd . for the first edge case, then the conditional can be done in the expansion of the parameter:

cdup="$(git rev-parse --show-cdup)" && cd "${cdup:-.}"
查看更多
初与友歌
4楼-- · 2018-12-31 18:49

I wanted to expand upon Daniel Brockman's excellent comment.

Defining git config --global alias.exec '!exec ' allows you to do things like git exec make because, as man git-config states:

If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. [...] Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory.

It's also handy to know that $GIT_PREFIX will be the path to the current directory relative to the top-level directory of a repository. But, knowing it is only half the battle™. Shell variable expansion makes it rather hard to use. So I suggest using bash -c like so:

git exec bash -c 'ls -l $GIT_PREFIX'

other commands include:

git exec pwd
git exec make
查看更多
有味是清欢
5楼-- · 2018-12-31 18:52

The man page for git-config (under Alias) says:

If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. [...] Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory.

So, on UNIX you can do:

git config --global --add alias.root '!pwd'
查看更多
十年一品温如言
6楼-- · 2018-12-31 18:54

If you're already in the top-level or not in a git repository cd $(git rev-parse --show-cdup) will take you home (just cd). cd ./$(git rev-parse --show-cdup) is one way of fixing that.

查看更多
一个人的天荒地老
7楼-- · 2018-12-31 18:55

If you're looking for a good alias to do this plus not blow up cd if you aren't in a git dir:

alias ..g='git rev-parse && cd "$(git rev-parse --show-cdup)"'
查看更多
登录 后发表回答