git别名无法将shell命令切换到git根目录

21

我想定义一个别名,使用git rev-parse --show-toplevel将我进入git根目录。

目前,我在我的.bashrc中将其定义为别名,这很好用:

alias gitroot='cd "$(git rev-parse --show-toplevel)"'

但是我知道你可以使用git别名运行shell命令,所以我想把命令放到我的.gitconfig文件中,让我的文件看起来更清晰。

问题是,我不知道为什么我的别名不起作用。目前,在我的.gitconfig文件中的[alias]下有以下内容:

root = "!sh -c 'cd \"$(git rev-parse --show-toplevel)\"'"

但是当我运行git root时,我仍然停留在当前目录。

我尝试了其他的shell别名,它们起作用了,例如别名say = "!sh -c 'echo \"$0\"'"很好地工作:

$ git say hello
hello

虽然我注意到类似的cd命令 (cd = "!sh -c 'cd \"$0\"'") 失败了:

~/repos/myproject$ git cd /home/
~/repos/myproject$

我不确定是因为我的语法还是Git别名的怪癖不支持cd命令,还是完全与其他原因有关。

如果有帮助的话,我正在使用Git 1.7.9.5。

2个回答

27

你的shell在调用Git,而Git会调用另一个shell运行你的cd命令。这个命令是成功的,它改变了子shell的工作目录,但它并没有改变Git或父shell的工作目录。

如果你想改变当前shell的工作目录,你需要在当前的shell中执行该命令,这意味着调用Git将不能完成这个任务,你必须继续使用shell别名。


为了说明这一点,假设你有一个名为up.sh的shell脚本:

#!/bin/sh
cd ..
如果您将此脚本作为./up.sh运行,则从当前shell的角度来看,什么也不会改变,因为cd是在一个新的shell实例中执行的。但是,如果您将其作为. up.sh执行,则这会指示当前shell自己执行文件的内容,而无需生成子shell。在这种情况下,当前shell的工作目录将更改。

这里的关键区别是使用Git别名类似于./up.sh方法,而使用shell别名类似于. up.sh方法。

4
太棒了,这解释得非常清楚。谢谢。 - 3cheesewheel

1
如 @cdhowie 解释过的那样,你无法在实际的 Git 别名中完成这个操作。但是你可以通过使用一个包装函数的方式来实现,这种方式几乎和 Git 别名一样,几乎无法区分。
git() {
    if [[ "$*" == 'root' ]]; then
        local root
        root="$(git rev-parse --show-toplevel)" || return $?
        cd "$root" || return $?
    else
        command git "$@"
    fi
}

除了显而易见的在PWD中进入Git存储库的根目录之外,还有以下功能:

  • 使用local变量避免污染命名空间。
  • 在一些特殊情况下表现正确:
    • 如果git rev-parse --show-toplevel命令失败(通常是因为您不在Git存储库中),返回该命令的退出代码。
    • 如果由于某种原因cd到根目录失败,返回cd的退出代码。
    • 如果上述命令中的任何一个失败,显示其标准错误输出,以进行调试。
  • 将除git root之外的任何命令转发给git命令本身。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接