有没有办法在一个命令中获取git根目录?

Mercurial有一种通过以下方式打印根目录(包含. hg)的方法

hg root

git中是否有等效的东西来获取包含. git目录的目录?

324778 次浏览

有:

git rev-parse --show-toplevel

如果您想更直接地复制Mercurial命令,您可以创建别名

git config --global alias.root 'rev-parse --show-toplevel'

现在git root将与hg root一样起作用。


说明:在子模块中,这将显示子模块没有父存储库的根目录。如果您使用的是Git>=2.13或更高版本,有一种方法子模块可以显示超级项目的根目录。如果您的git早于此,看看另一个答案。

git rev-parse --git-dir”怎么样?

F:\prog\git\test\copyMerge\dirWithConflicts>git rev-parse --git-dirF:/prog/git/test/copyMerge/.git

--git-dir选项似乎有效。

它甚至在裸仓库中也能工作,而git rev-parse --show-toplevel会触发(在所述裸存储库中)“fatal: this operation must be run in a work tree”。

git reg-parse手册页

--git-dir
Show $GIT_DIR if defined else show the path to the .git directory.

你可以在这个#0脚本中看到它的作用。

如果您在子模块文件夹中,使用Git>=2.13,使用

git rev-parse --show-superproject-working-tree

如果您使用的是git rev-parse --show-toplevel确保它使用Git 2.25+(2020年第一季度)

如果您已经在顶层或不在git存储库中,cd $(git rev-parse --show-cdup)将带您回家(只是cd)。cd ./$(git rev-parse --show-cdup)是解决这个问题的一种方法。

git-configman页面(在别名下)说:

如果别名扩展以感叹号为前缀,它将被视为shell命令。[…]注意shell命令将从存储库的顶级目录执行,可能不会必须是当前目录。

在UNIX上,你可以这样做:

git config --global --add alias.root '!pwd'

要计算当前git根目录的绝对路径,例如在外壳脚本中使用,请使用readlink和git reg-parse的组合:

gitroot=$(readlink -f ./$(git rev-parse --show-cdup))

git-rev-parse --show-cdup为您提供正确的“…”数量从您的cwd到根,或者如果您在根,则为空字符串。然后在前面加上“./”来处理空字符串情况并使用readlink -f转换为完整路径。

您还可以在PATH中创建git-root命令作为外壳脚本来应用此技术:

cat > ~/bin/git-root << EOF#!/bin/sh -ecdup=$(git rev-parse --show-cdup)exec readlink -f ./$cdupEOFchmod 755 ~/bin/git-root

(以上内容可以粘贴到终端中以创建git-root并设置执行位;实际脚本在第2、3和4行。)

然后你可以运行git root来获取当前树的根。请注意,在外壳脚本中,使用“-e”导致shell退出,如果Rev-parse失败,以便您可以正确获取退出状态和错误消息,如果您不在git目录中。

今天必须自己解决这个问题。在C#中解决了它,因为我需要它来编写程序,但我想它可以被esily重写。考虑这个公共领域。

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;
}

正如其他人所指出的,解决方案的核心是使用git rev-parse --show-cdup。然而,有一些边缘情况需要解决:

  1. 当cwd已经是工作树的根时,该命令会产生一个空字符串。
    实际上它产生了一个空行,但是命令替换去掉了尾随换行符。最终结果是一个空字符串。

    大多数答案建议在输出之前加上./,这样空输出在被馈送到cd之前就变成了"./"

  2. 当GIT_WORK_TREE设置为不是cwd父级的位置时,输出可能是绝对路径名。

    在这种情况下,前置./是错误的。如果./前置到绝对路径,它将成为相对路径(如果cwd是系统的根目录,它们仅引用相同的位置)。

  3. 输出可能包含空格。

    这实际上只适用于第二种情况,但它有一个简单的修复:在命令替换周围使用双引号(以及该值的任何后续使用)。

正如其他答案所指出的,我们可以做cd "./$(git rev-parse --show-cdup)",但这在第二个边缘情况下中断(如果我们省略双引号,则在第三个边缘情况下中断)。

许多shell将cd ""视为no-op,因此对于这些shell,我们可以执行cd "$(git rev-parse --show-cdup)"(双引号在第一种边缘情况下保护空字符串作为参数,并在第三种边缘情况下保留空格)。POSIX说cd ""的结果是未指定的,所以最好避免做出这种假设。

在上述所有情况下都有效的解决方案需要某种测试。显式完成,它可能如下所示:

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

对第一个边缘情况不执行cd

如果对第一个边缘情况运行cd .是可以接受的,那么条件可以在参数的扩展中完成:

cdup="$(git rev-parse --show-cdup)" && cd "${cdup:-.}"

稍微修改一下“git config”答案:

git config --global --add alias.root '!pwd -P'

把路清理干净。非常好。

--show-toplevel最近才被添加到git rev-parse中,或者为什么没有人提到它?

git rev-parse手册页:

   --show-toplevelShow the absolute path of the top-level directory.
alias git-root='cd \`git rev-parse --git-dir\`; cd ..'

其他所有东西在某些时候都会失败,要么进入主目录,要么只是悲惨地失败。这是返回GIT_DIR的最快和最短的方法。

如果您正在寻找一个好的别名来执行此操作,如果您不在git目录中,则不要炸毁cd

alias ..g='git rev-parse && cd "$(git rev-parse --show-cdup)"'
$ git config alias.root '!pwd'# then you have:$ git root

这是我写的一个脚本,可以处理这两种情况:1)带有工作区的存储库,2)裸存储库。

https://gist.github.com/jdsumsion/6282953

git-root(路径中的可执行文件):

#!/bin/bashGIT_DIR=`git rev-parse --git-dir` &&(if [ `basename $GIT_DIR` = ".git" ]; then# handle normal git repos (with a .git dir)cd $GIT_DIR/..else# handle bare git repos (the repo IS a xxx.git dir)cd $GIT_DIRfipwd)

希望这是有帮助的。

无论您是在git subdir中还是在顶层,这个shell别名都有效:

alias gr='[ ! -z `git rev-parse --show-toplevel` ] && cd `git rev-parse --show-toplevel || pwd`'

更新为使用现代语法而不是反引号:

alias gr='[ ! -z $(git rev-parse --show-toplevel) ] && cd $(git rev-parse --show-toplevel || pwd)'

在这里写一个简单的答案,以便我们可以使用

git root

要完成这项工作,只需使用配置您的git

git config --global alias.root "rev-parse --show-toplevel"

然后,您可能希望将以下内容添加到您的~/.bashrc中:

alias cdroot='cd $(git root)'

因此,您可以使用cdroot转到存储库的顶部。

如果您将此路径提供给Git本身,请使用:/

# this adds the whole working tree from any directory in the repogit add :/
# and is equal togit add $(git rev-parse --show-toplevel)

git-extras

添加$ git root
https://github.com/tj/git-extras/blob/master/Commands.md#git-root

$ pwd.../very-deep-from-root-directory$ cd `git root`$ git add . && git commit

git附加功能的可用性

使用子模块、钩子和.git目录内的简短解决方案

以下是大多数人想要的简短答案:

r=$(git rev-parse --git-dir) && r=$(cd "$r" && pwd)/ && echo "${r%%/.git/*}"

这将在git工作树中的任何地方(包括.git目录内)工作,但假设存储库目录称为.git(这是默认值)。对于子模块,这将转到最外层包含存储库的根目录。

如果要获取当前子模块的根,请使用:

echo $(r=$(git rev-parse --show-toplevel) && ([[ -n $r ]] && echo "$r" || (cd $(git rev-parse --git-dir)/.. && pwd) ))

要在子模块根目录中轻松执行命令,请在.gitconfig[alias]下添加:

sh = "!f() { root=$(pwd)/ && cd ${root%%/.git/*} && git rev-parse && exec \"$@\"; }; f"

这可以让你轻松地做git sh ag <string>这样的事情

支持不同命名或外部.git$GIT_DIR目录的强大解决方案。

请注意,$GIT_DIR可能指向外部的某个地方(而不是被称为.git),因此需要进一步检查。

把这个放在你的.bashrc

# Print the name of the git working tree's root directoryfunction git_root() {local root first_commit# git displays its own error if not in a repositoryroot=$(git rev-parse --show-toplevel) || returnif [[ -n $root ]]; thenecho $rootreturnelif [[ $(git rev-parse --is-inside-git-dir) = true ]]; then# We're inside the .git directory# Store the commit id of the first commit to compare later# It's possible that $GIT_DIR points somewhere not inside the repofirst_commit=$(git rev-list --parents HEAD | tail -1) ||echo "$0: Can't get initial commit" 2>&1 && false && returnroot=$(git rev-parse --git-dir)/.. &&# subshell so we don't change the user's working directory( cd "$root" &&if [[ $(git rev-list --parents HEAD | tail -1) = $first_commit ]]; thenpwdelseecho "$FUNCNAME: git directory is not inside its repository" 2>&1falsefi)elseecho "$FUNCNAME: Can't determine repository root" 2>&1falsefi}
# Change working directory to git repository rootfunction cd_git_root() {local rootroot=$(git_root) || return 1  # git_root will print any errorscd "$root"}

通过键入git_root执行它(重新启动shell后:exec bash

Shell框架中预配置的Shell别名

如果您使用shell框架,可能已经有一个可用的shell别名:

git 2.13.0开始,它支持一个新的选项来显示根项目的路径,即使在子模块内部使用时也有效:

git rev-parse --show-superproject-working-tree

如果有人需要POSIX兼容的方式来执行此操作,而不需要git可执行文件:

git-root

#$1: Path to child directorygit_root_recurse_parent() {# Check if cwd is a git root directoryif [ -d .git/objects -a -d .git/refs -a -f .git/HEAD ] ; thenpwdreturn 0fi
# Check if recursion should end (typically if cwd is /)if [ "${1}" = "$(pwd)" ] ; thenreturn 1fi
# Check parent directory in the same waylocal cwd=$(pwd)cd ..git_root_recurse_parent "${cwd}"}
git_root_recurse_parent

如果您只想将该功能作为脚本的一部分,请删除sheang,并将最后git_root_recurse_parent行替换为:

git_root() {(git_root_recurse_parent)}

我想扩展丹尼尔·布罗克曼的精彩评论。

定义git config --global alias.exec '!exec '允许您做类似于git exec make的事情,因为man git-config指出:

如果别名扩展以感叹号为前缀,则将其视为shell命令。[…]请注意,shell命令将从存储库的顶级目录执行,该目录不一定是当前目录。

知道$GIT_PREFIX将是相对于存储库顶级目录的当前目录的路径也很方便。但是,知道这只是战斗的一半™。Shell变量扩展使其很难使用。所以我建议像这样使用bash -c

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

其他命令包括:

git exec pwdgit exec make