Mercurial可以通过
在git中是否有类似的东西来获取包含.git目录的目录?
- 好剧本,埃米尔。我使脚本在网上可用,并允许添加一个文件/目录作为参数。github.com/dieterbe/git脚本/commit/…
- 对任何好奇或寻找的人来说,在集市上也经常使用bzr root。
- 请注意"gcd":在jeetworks.org/node/52自动完成时,与存储库根目录相关的git-aware"cd"。
- 请考虑我下面的回答,如本评论中所述。
- @Michawiedenmann:链接已断开。
- @d33tah archive link web.archive.org/web/20130526035714/http://jeetworks.org/node‌&8203;/52
对:
1
| git rev-parse --show-toplevel |
注意:在子模块中,这将显示子模块的根目录,而不是父存储库。如果您使用的是git>=2.13或更高版本,下面是子模块显示超级项目根目录的方法。如果你的Git比这个年龄大,请看另一个答案。
- 这也是我所能找到的;可能想把它命名为git top或类似的东西。
- 这给了我到顶级/root目录根目录的路径,这正是我要查找的。
- 我总是定义git config --global alias.exec '!exec ',这样我就可以做git exec make之类的事情。这是因为shell别名总是在顶级目录中执行。
- 这就是hg root所做的。它打印出已签出存储库的顶级目录。它不会将您切换到它(事实上,它不能这样做,因为当前目录和shell的整个概念是如何交互的)。
- @帕特里克:有趣的编辑历史。有人忘了表扬…
- SMAL警告这个解决方案-它将跟踪任何符号链接。因此,如果您在~/my.proj/foo/bar中,并且~/my.proj与~/src/my.proj符号链接,上面的命令会将您移动到~/src/my.proj中。可能是个问题,如果在那之后你想做的不是树不可知论者。
- 感谢您指出这一点;如果没有您的帮助,我很难猜测是否要查看git-rev-parse,因为它的名称表明它是关于处理修订规范的。顺便说一句,我很高兴看到git --work-tree的工作类似于git --exec-path[=]:"如果没有指定路径,Git将打印当前设置";至少在imo,这将是一个合理的地方寻找这样的功能。
- 注意,这在子模块中不起作用。
- 我如何在Git钩子内使用这个?具体来说,我正在做一个后合并挂钩,我需要得到本地git repo的实际根目录。
- git exec别名可以与pwd组合打印根目录:git exec pwd,有时,在根目录上临时放置一个子shell,然后退出返回原始任务是很有用的。
- @jondavidjohn见gist.github.com/hilbix/7724772 git config --global alias.top '!f() { GIT_TOP="${GIT_DIR%%/.git/modules/*}"; [".$GIT_TOP" !=".$GIT_DIR" ] && cd"$GIT_TOP"; exec"$@"; }; f',然后是git top pwd。
- 这个解决方案(以及我尝试过的这个页面上的许多其他解决方案)在Git钩子内不起作用。更准确地说,当您在项目的.git目录中时,它不起作用。
- 这个git rev-parse && cd"$(git rev-parse --show-cdup)在一般情况下不起作用,因为git rev-parse --show-cdup返回空字符串,以防在git根目录中准备就绪。骄傲的是你回家了。
- 在Windows中,您将得到反向斜杠-使用类似这样的方法来反转斜杠:git rev-parse --show-toplevel > %TEMP%\GITROOT.TXT & SET /p BranchRoot=<%TEMP%\GITROOT.TXT & SET BranchRoot=%BranchRoot:/=\%
- 正如@dennis所指出的,如果在存储库的.git目录(或外部$GIT_DIR目录)中,这就不起作用。在存储库中的任何地方都可以工作,并且在外部$GIT_DIR中优雅地失败,请参阅我的答案。
- 命令的名称是什么?wtf是"rev parse"…reverse parse?"Hg根"非常有意义
- @Alexandermills可能是"修订解析",但是在那里有这个选项仍然很奇怪。
- @AlexanderMills显示"rev-parse"的名字来源于它最初是"rev-list"(修订列表)的助手。在git-rev-parse的手册页中:"许多git-opercelainish命令混合了用于底层git-rev-list命令的标志和参数,它们在内部使用,在git rev-list的下游使用其他命令的标志和参数。此命令用于区分它们。"
- @乔达维乔恩希望我早点看到这个评论。以为我疯了。
- 我认为在子模块中说它"不起作用"是不正确的,因为获得子模块的根目录正是我希望得到的。(我的理由是:使用此命令的shell脚本是否总是需要其自己存储库的根目录,而不是父存储库?)我提交了一个编辑,我觉得这增加了一个更有帮助的解释。
- 要让它在.git/hooks文件夹中工作,只需EDOCX1[0]
- 命令的坏名字,我总是忘了…
git-config的man页面(化名)显示:
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.
因此,在Unix上,您可以执行以下操作:
1
| git config --global --add alias.root '!pwd' |
- 它与——rev parse show toplevel相同,但更优雅!
- 不适用于Windows系统。另一个是。
- 那么,如果您有一个"git-root"命令,那么如何将其放到别名中呢?如果我把它放在我的.zshrc中,并且定义了'alias cg="cd$(git root)",那么$()部分将在源时间进行评估,并且始终指向~/dotfiles,就像我的zshrc所在的位置一样。
- @Cormacrelf你不能把它放在shell别名中。您可以将其放入shell函数或脚本中。
- 当您在.git/目录中的任何位置时,这都不起作用。然后它返回根目录以及您在.git/中的任何位置。
- @Cormacrelf将它放在单引号中而不是双引号中,那么它将不会在定义时进行扩展,而是在运行时进行扩展。
- 如果你不在Git回购中,它也会失败。
- @机械蜗牛真的吗?那么,你希望它在回购协议之外做什么呢?
- @事实上,对于我来说,回购协议之外的交易不会失败,它只是简单地报告了CWD。
- @无所不知,我个人不认识任何使用command.com(cmd)作出承诺的人,但在Windows上,我猜你会想要git config --add alias.root '!cd'或'!echo %CD%'。但是,如果您需要跨平台兼容性,接受的答案是最好的。
- 聪明的。使用符号链接也比接受的答案更好
--show-toplevel是不是最近才被添加到git rev-parse中,或者为什么没有人提到?
从git rev-parse手册页:
1 2
| --show-toplevel
Show the absolute path of the top-level directory. |
- 感谢您指出这一点;如果没有您的帮助,我很难猜测是否要查看git-rev-parse,因为它的名称表明它是关于处理修订规范的。顺便说一句,我很高兴看到git --work-tree的工作类似于git --exec-path[=]:"如果没有指定路径,Git将打印当前设置";至少,imo,它将是寻找这样一个功能的一个合乎逻辑的地方。
- 特别是,您可以在Gitconfig中别名root = rev-parse --show-toplevel。
- 换言之,你可以做git config --global alias.root"rev-parse --show-toplevel",然后git root就可以做这项工作了。
- 遗憾的是,它在存储库裸露的情况下不起作用。
- 这也不适用于子模块。
- @当我在子模块中尝试时,Ryantheleasgit rev-parse --show-toplevel工作。它打印git子模块的根目录。它为你打印什么?
- 我很困惑为什么这个答案会重复上面的答案。结果发现,2011年2月(在提交该答案之后),顶部答案从--show-cdup编辑到--show-top-level。
- @我期待的是容器的根目录,也就是顶级目录,而不仅仅是最近的顶级目录。
"git rev-parse --git-dir"怎么样?
1 2
| F:\prog\git\test\copyMerge\dirWithConflicts>git rev-parse --git-dir
F:/prog/git/test/copyMerge/.git |
--git-dir选项似乎有效。
从Git Rev Parse Manual页面:
1 2 3
| --git-dir
Show $GIT_DIR if defined else show the path to the .git directory. |
你可以在这个git setup-sh脚本中看到它的实际作用。
如果您在子模块文件夹中,且git大于等于2.13,请使用:
1
| git rev-parse --show-superproject-working-tree |
- 正是我在寻找的,因为它给出了绝对路径。我试图创建一个别名,它也会CD到这个目录中,但这不适用于"cdroot=!cd$(git-rev-parse--git-dir)"很遗憾。
- 哦,等等,这很接近,但它得到了实际的.git目录,而不是git回购的基础。另外,.git目录可能在其他地方,所以这不是我想要的。
- 好吧,我现在知道你到底在找什么了。--那么显示cdup更合适。我留下我的答案来说明这两个选项之间的区别。
- 如果您已经在根目录中,这个命令似乎还为.git提供了一个相对路径。(至少在msysgit上是这样的。)
- +1,这是唯一回答原始问题"获取包含.git目录的目录"的答案。,有趣的是看到op本身提到了".git目录可能在其他地方"。
- 这是在Windows上工作的唯一解决方案,在使用git子模块(例如c:
epositoriesmyrepo.gitmodulesmysubmodule)时会给出一个简单的结果。这使得它成为最健壮的解决方案,特别是在可重用的脚本中。
- 沿着这些线的完整解决方案是:(root=$(git rev-parse --git-dir)/ && cd ${root%%/.git/*} && git rev-parse && pwd),但这不包括外部$GIT_DIR,它的名称不是.git,有关完整解决方案,请参阅下面的答案。
- 谢谢。我实际上想要git dir,这也适用于子模块,在顶层文件夹下不一定有.git/文件夹。
- @乔伊特威德很好!7年后,这个答案仍然是相关的!
在这里写一个简单的答案,这样我们就可以使用
要完成此任务,只需使用
1
| git config --global alias.root"rev-parse --show-toplevel" |
然后,您可能希望在您的~/.bashrc中添加以下内容:
1
| alias cdroot='cd $(git root)' |
这样你就可以使用cdroot进入回购的顶部。
如果你已经在顶级或不在Git存储库中,cd $(git rev-parse --show-cdup)会带你回家(只有CD)。cd ./$(git rev-parse --show-cdup)是解决这个问题的一种方法。
- 另一种选择是引用:cd"$(git rev-parse --show-cdup)"。这是因为cd""没有带你去任何地方,而不是背$HOME回来。最好还是引用$()调用,以防它们输出带有空格的内容(但在本例中,该命令不会引用)。
- 即使您通过symlink将目录更改为git repo,这个答案也能很好地工作。当您的git repo处于symlink下时,其他一些示例无法按预期工作,因为它们无法相对于bash的$PWD解析git的根目录。这个例子将解析git相对于EDOCX1的根(9),而不是realpath $PWD。
- 这不适用于子模块。
正如其他人所指出的,解决方案的核心是使用git rev-parse --show-cdup。但是,有一些边缘案例需要解决:
当CWD已经是工作树的根目录时,该命令生成一个空字符串。实际上它会生成一个空行,但命令替换会去掉尾随的换行符。最终结果是一个空字符串。
大多数答案都建议在输出前加上./,使空输出在被送入cd之前变成"./"。
当Git_工作树设置为不是CWD父级的位置时,输出可能是绝对路径名。
在这种情况下,预支./是错误的。如果一个./被预先设置为一个绝对路径,它就成为一个相对路径(如果cwd是系统的根目录,它们只引用相同的位置)。
输出可能包含空白。
这实际上只适用于第二种情况,但它有一个简单的修复方法:在命令替换(以及随后对值的任何使用)周围使用双引号。
正如其他答案所指出的,我们可以使用cd"./$(git rev-parse --show-cdup)",但这会在第二个边缘案例(如果我们不使用双引号,则会在第三个边缘案例)中中断。
许多shell将cd""视为no op,因此对于这些shell,我们可以执行cd"$(git rev-parse --show-cdup)"(双引号在第一个边缘大小写中保护空字符串作为参数,在第三个边缘大小写中保留空白)。posix说,cd""的结果没有具体说明,因此最好避免作出这种假设。
在上述所有情况下都有效的解决方案都需要某种类型的测试。显式完成后,可能如下所示:
1
| cdup="$(git rev-parse --show-cdup)" && test -n"$cdup" && cd"$cdup" |
对于第一个边缘情况,不执行cd。
如果可以为第一个边缘情况运行cd .,则可以在扩展参数时执行条件:
1
| cdup="$(git rev-parse --show-cdup)" && cd"${cdup:-.}" |
- 为什么不使用"git-config--global--add alias.root"!pwd'"和一个shell别名gitroot='cd git root,您使用上面的答案?
- 使用别名可能是不可能的,例如,如果您想编写它的脚本,并且不能依赖于自定义别名。
- +1个用于列出角落的案例!
- 子模块是另一种情况。
要计算当前git根目录的绝对路径,例如在shell脚本中使用,请使用readlink和git rev parse的组合:
1
| gitroot=$(readlink -f ./$(git rev-parse --show-cdup)) |
git-rev-parse --show-cdup给了你正确的"…"号从CWD的根目录,或者如果在根目录,则为空字符串。然后预加"./"以处理空字符串大小写并使用readlink -f转换为完整路径。
您还可以在路径中创建一个git-root命令作为shell脚本来应用此技术:
1 2 3 4 5 6
| cat > ~/bin/git-root << EOF
#!/bin/sh -e
cdup=$(git rev-parse --show-cdup)
exec readlink -f ./$cdup
EOF
chmod 755 ~/bin/git-root |
(以上内容可以粘贴到终端创建git根,设置执行位,实际脚本在第2、3、4行)
然后你就可以运行git root来获取当前树的根。注意,在shell脚本中,如果rev解析失败,则使用"-e"使shell退出,这样,如果您不在git目录中,就可以正确地获取退出状态和错误消息。
- 如果git"root"目录路径包含空格,则示例将中断。总是使用"$(git rev-parse ...)",而不是像./$(git rev-parse ...)这样的黑客。
- readlink -f在BSD上的作用不同。解决方法见此。python的答案可能在不安装任何东西的情况下工作:python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))'"$(git rev-parse --show-cdup)"。
为了防止您将此路径馈送给Git本身,请使用:/。
1 2 3 4 5
| # this adds the whole working tree from any directory in the repo
git add :/
# and is equal to
git add $(git rev-parse --show-toplevel) |
与子模块、钩子和.git目录内一起工作的简短解决方案
以下是大多数人想要的简短答案:
1
| r=$(git rev-parse --git-dir) && r=$(cd"$r" && pwd)/ && echo"${r%%/.git/*}" |
这在Git工作树(包括.git目录内)的任何位置都可以工作,但假定存储库目录称为.git(默认情况下)。对于子模块,这将转到包含存储库的最外层的根目录。
如果要获取当前子模块的根,请使用:
1
| echo $(r=$(git rev-parse --show-toplevel) && ([[ -n $r ]] && echo"$r" || (cd $(git rev-parse --git-dir)/.. && pwd) )) |
要在您的子模块根目录中的[alias]下轻松执行命令,请在.gitconfig中添加:
1
| sh ="!f() { root=$(pwd)/ && cd ${root%%/.git/*} && git rev-parse && exec "$@"; }; f" |
这样你就可以很容易地做像git sh ag 这样的事情。
支持不同名称或外部.git或$GIT_DIR目录的健壮解决方案。
请注意,$GIT_DIR可能指向外部的某个地方(不称为.git),因此需要进一步检查。
把这个放在你的.bashrc里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| # Print the name of the git working tree's root directory
function git_root() {
local root first_commit
# git displays its own error if not in a repository
root=$(git rev-parse --show-toplevel) || return
if [[ -n $root ]]; then
echo $root
return
elif [[ $(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 repo
first_commit=$(git rev-list --parents HEAD | tail -1) ||
echo"$0: Can't get initial commit" 2>&1 && false && return
root=$(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 ]]; then
pwd
else
echo"$FUNCNAME: git directory is not inside its repository" 2>&1
false
fi
)
else
echo"$FUNCNAME: Can't determine repository root" 2>&1
false
fi
}
# Change working directory to git repository root
function cd_git_root() {
local root
root=$(git_root) || return # git_root will print any errors
cd"$root"
} |
通过键入git_root(在重新启动shell:exec bash之后)来执行它。
- 这个代码正在RobustBash函数中进行代码审查,以找到Git存储库的根。查找更新。
- 很好。比我7年前提出的更复杂(stackoverflow.com/a/958125/6309),但仍然是+1
- 沿着这些线,较短的解决方案是:(root=$(git rev-parse --git-dir)/ && cd ${root%%/.git/*} && git rev-parse && pwd),但这不包括外部$GIT_DIR,后者的名称不是.git。
- 我想知道这是否考虑到现在Git2.5+中可能存在的多个工作树(stackoverflow.com/a/30185564/6309)
- 它打印当前工作树的根目录
要修改"git-config",只需回答一点:
1
| git config --global --add alias.root '!pwd -P' |
把小路清理干净。很不错的。
如果你正在寻找一个好的别名来做这件事,如果你不在git目录中,就不要炸毁cd:
1
| alias ..g='git rev-parse && cd"$(git rev-parse --show-cdup)"' |
git-extras增加$ git root。请参见https://github.com/tj/git extras/blob/master/commands.md git root
1 2 3 4
| $ pwd
.../very-deep-from-root-directory
$ cd `git root`
$ git add . && git commit |
Git Extras的可用性
- 自制(osx)/linuxbrew(linux)$ brew install git-extras
- debian/ubuntu repos(https://packages.debian.org/sid/git-extras)$ apt-get install git-extras。
无论您是在Git子目录中,还是在顶层,此shell别名都有效:
1
| alias gr='[ ! -z `git rev-parse --show-toplevel` ] && cd `git rev-parse --show-toplevel || pwd`' |
更新后使用现代语法而不是背景符号:
1
| alias gr='[ ! -z $(git rev-parse --show-toplevel) ] && cd $(git rev-parse --show-toplevel || pwd)' |
1
| alias git-root='cd \`git rev-parse --git-dir\`; cd ..' |
其他的事情在某个时候都会失败,要么是转到主目录,要么就是非常糟糕的失败。这是返回Git_目录的最快和最短的方法。
- 看起来"git-rev-parse--git-dir"是最干净的解决方案。
- 当使用.git文件和gitdir: SOMEPATH将$GIT_DIR与工作树分离时,此操作失败。因此,当$GIT_DIR包含.git/modules/SUBMODULEPATH时,子模块也无法实现。
下面是我编写的一个处理这两种情况的脚本:1)带有工作区的存储库,2)空存储库。
https://gist.github.com/jdsumsion/6282953
git-root(路径中的可执行文件):
1 2 3 4 5 6 7 8 9 10 11 12
| #!/bin/bash
GIT_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_DIR
fi
pwd
) |
希望这是有帮助的。
- 请在你的答案中加上密码,只有12行。
- 基思,谢谢你的建议,我已经包括了剧本。
- 事实上,我对最重要的答案投了反对票,因为我意识到git exec的想法在非裸机存储库中更有用。但是,我的答案中的这个脚本正确地处理了裸箱和非裸箱,这可能对某些人有用,所以我将把这个答案留在这里。
- 然而,这在git submodule中失败了,其中$GIT_DIR包含类似/.git/modules/SUBMODULE的内容。另外,您假设在非裸机情况下,.git目录是工作树的一部分。
1 2 3
| $ git config alias.root '!pwd'
# then you have:
$ git root |
- 添加--全局用于创建全局别名是否有效?
- 此别名将作为全局别名失败。用这个来代替(在~/.gitconfig中):[alias] findroot ="!f () { [[ -d".git" ]] && echo"Found git in [pwd]" && exit 0; cd .. && echo"IN pwd" && f;}; f"。
- 为什么要投票?请突出显示任何错误或建议改进。
- 对我来说,埃多克斯1〔26〕工作。我无法发现任何与非全球变种不同的情况。(unix,git 1.7.10.4)btw:您的findroot需要/.git以避免无休止的递归。
自从Git2.13.0以来,它支持一个新的选项来显示根项目的路径,它甚至在从子模块内部使用时也能工作:
1
| git rev-parse --show-superproject-working-tree |
- git scm.com/docs/&hellip;。有趣。我一定错过了那个。+ 1
- 但警告:"如果当前存储库未被任何项目用作子模块,则不输出任何内容。"
- 谢谢你的评论!我没注意到。
shell框架中预先配置的shell别名
如果使用shell框架,则可能已经存在可用的shell别名:
- oh my zsh(68K)中的$ grt(cd $(git rev-parse --show-toplevel || echo"."))
- prezto(8.8k)中的$ git-root(显示到工作树根的路径)
- $ g..zimfw(1K)(将当前目录更改为工作树的顶层。)
今天必须自己解决这个问题。在C语言中解决了它,因为我需要它作为一个程序,但我想它可以很容易地重写。考虑这个公共领域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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;
} |
如果任何人需要符合POSIX的方式来完成此操作,而不需要git可执行文件:
git-root:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #$1: Path to child directory
git_root_recurse_parent() {
# Check if cwd is a git root directory
if [ -d .git/objects -a -d .git/refs -a -f .git/HEAD ] ; then
pwd
return 0
fi
# Check if recursion should end (typically if cwd is /)
if ["${1}" ="$(pwd)" ] ; then
return 1
fi
# Check parent directory in the same way
local cwd=$(pwd)
cd ..
git_root_recurse_parent"${cwd}"
}
git_root_recurse_parent |
如果只想将功能作为脚本的一部分,请删除shebang,并将最后一行git_root_recurse_parent替换为:
1 2 3
| git_root() {
(git_root_recurse_parent)
} |
- 警告:如果在git repo中不调用这个函数,那么递归就不会结束。
- @a.h.第二条if语句应该检查您是否在递归中更改了目录。如果它保持在同一个目录中,则假定您被卡在某个位置(例如/),并阻止递归。这个bug已经修复,现在应该可以按预期工作了。谢谢你指出。
我想进一步阐述丹尼尔·布罗克曼的精彩评论。
定义git config --global alias.exec '!exec '允许您执行git exec make之类的操作,因为正如man git-config所述:
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.
知道$GIT_PREFIX是相对于存储库的顶级目录的当前目录的路径也很方便。但是,知道这只是一半的战斗吗?.shell变量扩展使其很难使用。因此,我建议像这样使用bash -c:
1
| git exec bash -c 'ls -l $GIT_PREFIX' |
其他命令包括:
1 2
| git exec pwd
git exec make |