Existe uma maneira de obter o diretório raiz do git em um comando?

O Mercurial tem uma maneira de imprimir o diretório raiz (que contém .hg) via

hg root 

Existe algo equivalente no git para obter o diretório que contém o diretório .git?

Sim:

 git rev-parse --show-toplevel 

A página man do git-config (sob Alias ) diz:

Se a expansão de alias for prefixada com um ponto de exclamação, ela será tratada como um comando de shell. Por exemplo, definindo “alias.new =! Gitk –all –not ORIG_HEAD”, a invocação “git new” é equivalente a executar o comando shell “gitk –all –not ORIG_HEAD”. Observe que os comandos shell serão executados no diretório de nível superior de um repository, que pode não ser necessariamente o diretório atual.

Então, no UNIX você pode fazer:

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

O --show-toplevel foi adicionado recentemente ao git rev-parse ou por que ninguém está mencionando isso?

Na página do manual git rev-parse :

  --show-toplevel Show the absolute path of the top-level directory. 

Que tal ” git rev-parse --git-dir “?

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

A opção --git-dir parece funcionar.

Da página de manual do git rev-parse :

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

Você pode vê-lo em ação neste script git setup-sh .

Se você já está no nível superior ou não em um repository git, o cd $(git rev-parse --show-cdup) o levará para casa (apenas cd). cd ./$(git rev-parse --show-cdup) é uma maneira de corrigir isso.

Para escrever uma resposta simples aqui, para que possamos usar

 git root 

para fazer o trabalho, basta configurar o seu git usando

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

e então você pode querer adicionar o seguinte ao seu ~/.bashrc :

 alias cdroot='cd $(git root)' 

para que você possa usar o cdroot para ir até o topo do seu repo.

Para calcular o caminho absoluto do diretório raiz git atual, por exemplo, para uso em um script de shell, use essa combinação de readlink e git rev-parse:

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

git-rev-parse --show-cdup fornece o número correto de “..” s para obter a raiz do seu cwd, ou a string vazia se você estiver na raiz. Em seguida, coloque “./” para lidar com o caso da cadeia vazia e use readlink -f para traduzir para um caminho completo.

Você também pode criar um comando git-root no seu PATH como um script de shell para aplicar esta técnica:

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

(O acima pode ser colado em um terminal para criar a raiz git e definir os bits de execução; o script real está nas linhas 2, 3 e 4.)

E então você seria capaz de executar o git root para obter a raiz da sua tree atual. Observe que, no script de shell, use "-e" para fazer com que o shell seja encerrado se a análise de análise falhar, para que você possa obter o status de saída e a mensagem de erro corretamente, caso não esteja em um diretório git.

Como outros notaram, o núcleo da solução é usar o git rev-parse --show-cdup . No entanto, existem alguns casos de limites a serem abordados:

  1. Quando o cwd já é a raiz da tree de trabalho, o comando gera uma string vazia.
    Na verdade, ele produz uma linha vazia, mas a substituição de comando tira a quebra da linha final. O resultado final é uma string vazia.

    A maioria das respostas sugere a pré-formatação da saída com ./ modo que uma saída vazia se torne "./" antes de ser alimentada para o cd .

  2. Quando GIT_WORK_TREE é definido para um local que não é o pai do cwd, a saída pode ser um nome de caminho absoluto.

    Prefácio ./ está errado nesta situação. Se um ./ é prefixado em um caminho absoluto, ele se torna um caminho relativo (e eles referem-se apenas ao mesmo local se o cwd for o diretório raiz do sistema).

  3. A saída pode conter espaço em branco.

    Isso realmente só se aplica no segundo caso, mas tem uma correção fácil: use aspas duplas em torno da substituição de comando (e quaisquer usos subseqüentes do valor).

Como outras respostas notaram, nós podemos fazer cd "./$(git rev-parse --show-cdup)" , mas isso quebra no segundo caso de borda (eo terceiro caso de borda se nós deixarmos as aspas duplas).

Muitos shells tratam o cd "" como um no-op, então para esses shells nós poderíamos fazer cd "$(git rev-parse --show-cdup)" (as aspas duplas protegem a string vazia como um argumento no primeiro caso de borda) e preservar espaços em branco no terceiro caso de borda). POSIX diz que o resultado do cd "" não é especificado, então pode ser melhor evitar essa suposição.

Uma solução que funciona em todos os casos acima requer um teste de algum tipo. Feito explicitamente, pode parecer com isso:

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

Nenhum cd é feito para o primeiro caso de borda.

Se é aceitável executar o cd . para o primeiro caso de borda, a condicional pode ser feita na expansão do parâmetro:

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

Soluções curtas que funcionam com submódulos, ganchos e dentro do diretório .git

Aqui está a resposta curta que a maioria vai querer:

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

Isso funcionará em qualquer lugar em uma tree de trabalho git (incluindo dentro do diretório .git ), mas assume que os diretórios do repository são chamados .git (que é o padrão). Com submódulos, isso irá para a raiz do repository de contenção mais externo.

Se você quiser chegar à raiz do submódulo atual, use:

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

Para executar facilmente um comando na raiz do submódulo, sob [alias] no seu .gitconfig , adicione:

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

Isso permite que você faça facilmente coisas como git sh ag

Solução robusta que suporta diretórios $GIT_DIR ou $GIT_DIR nome ou externo diferentes.

Note que $GIT_DIR pode apontar para algum lugar externo (e não ser chamado de .git ), daí a necessidade de mais verificações.

Coloque isso no seu .bashrc :

 # 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" } 

Execute-o digitando git_root (depois de reiniciar seu shell: exec bash )

Apenas no caso de você estar alimentando este caminho para o próprio Git, use :/

 # 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) 

Para corrigir a resposta “git config” apenas um pouco:

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

e obter o caminho limpo. Muito agradável.

Se você está procurando um bom alias para fazer isso, além de não explodir o cd se você não estiver em um diretório git:

 alias ..g='git rev-parse && cd "$(git rev-parse --show-cdup)"' 

Este alias de shell funciona se você estiver em um subdiretório git ou no nível superior:

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

atualizado para usar a syntax moderna em vez de backticks:

 alias gr='[ ! -z $(git rev-parse --show-toplevel) ] && cd $(git rev-parse --show-toplevel || pwd)' 
 alias git-root='cd \`git rev-parse --git-dir\`; cd ..' 

Todo o resto falha em algum momento indo para o diretório inicial ou simplesmente falhando miseravelmente. Este é o caminho mais rápido e mais curto para voltar ao GIT_DIR.

Aqui está um script que eu escrevi que lida com os dois casos: 1) repository com um espaço de trabalho, 2) repository vazio.

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

git-root (arquivo executável no seu caminho):

 #!/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 ) 

Espero que isso seja útil.

git-extras

adiciona $ git root
veja 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 

Disponibilidade de git-extras

 $ git config alias.root '!pwd' # then you have: $ git root 

Desde o Git 2.13.0 , ele suporta uma nova opção para mostrar o caminho do projeto raiz, que funciona mesmo quando usado dentro de um submódulo:

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

Tive que resolver isso hoje mesmo. Resolvi em C # como eu precisava de um programa, mas acho que pode ser reescrito. Considere este domínio público.

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

Aliases de Shell pré-configurados em estruturas de shell

Se você usar um framework shell, talvez já exista um alias de shell disponível:

  • $ grt em oh-meu-zsh (68k) ( cd $(git rev-parse --show-toplevel || echo ".") )
  • $ git-root no prezto (8.8k) (exibe o caminho para a raiz da tree de trabalho)
  • $ g.. zimfw (1k) (altera o diretório atual para o nível superior da tree de trabalho.)

Caso alguém precise de uma maneira compatível com POSIX, sem precisar executar o git :

git-root :

 #!/bin/sh #$1: Path to child directory recurse_parent() { if is_cwd_git_root ; then pwd return 0 else if [ "${1}" = "$(pwd)" ] ; then return 1 else cd .. recurse_parent "${CWD}" fi fi } is_cwd_git_root() { [ -d .git/branches -a -d .git/objects -a -d .git/refs -a -f .git/HEAD ] } recurse_parent