web-dev-qa-db-fra.com

Est-il possible d’obtenir le répertoire racine git en une seule commande?

Mercurial a le moyen d’imprimer le répertoire racine (qui contient .hg) via

hg root

Y at-il quelque chose d’équivalent dans git pour obtenir le répertoire qui contient le répertoire .git?

542
wojo

Oui:

git rev-parse --show-toplevel

Remarque : Dans un sous-module, cela affichera le répertoire racine du sous-module et non le référentiel parent.

894
baudtack

La page man pour git-config (sous Alias ​​) dit:

Si le développement de l'alias est précédé d'un point d'exclamation, il sera traité comme un shell commander. [...] Notez que Shell les commandes seront exécutées à partir du répertoire de niveau supérieur d'un référentiel, ce qui peut ne pas être le cas être nécessairement le répertoire en cours.

Donc, sous UNIX, vous pouvez faire:

git config --global --add alias.root '!pwd'
90
Scott Lindsay

Est-ce que --show-toplevel a été ajouté récemment à git rev-parse ou pourquoi personne ne le mentionne?

De la page de manuel git rev-parse:

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

Que diriez-vous de "git rev-parse --git-dir"?

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

L'option --git-dir semble fonctionner.

De page de manuel de git rev-parse :

--git-dir

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

Vous pouvez le voir en action dans ce script git setup-sh .

Si vous êtes dans un dossier de sous-modules, avec Git 2.20, utilisez :

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

Pour écrire une réponse simple ici, afin que nous puissions utiliser

git root

pour faire le travail, configurez simplement votre git en utilisant

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

et ensuite vous voudrez peut-être ajouter ce qui suit à votre ~/.bashrc:

alias cdroot='cd $(git root)'

afin que vous puissiez simplement utiliser cdroot pour aller au sommet de votre repo.

27

Si vous êtes déjà au niveau supérieur ou non dans un référentiel git, cd $(git rev-parse --show-cdup) vous ramènera à la maison (cd uniquement). cd ./$(git rev-parse --show-cdup) est un moyen de résoudre ce problème.

25
Karl

Pour calculer le chemin absolu du répertoire racine git actuel, par exemple pour une utilisation dans un script Shell, utilisez cette combinaison de readlink et de git rev-parse:

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

git-rev-parse --show-cdup vous donne le bon nombre de ".." à obtenirà la racine depuis votre cwd, ou la chaîne vide si vous êtes à la racine . Puis ajoutez "./" pour ajouter la chaîne vide case et utilisez readlink -f pour traduire en chemin complet.

Vous pouvez également créer une commande git-root dans votre PATH en tant que script Shell pour appliquer cette technique:

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

(Ce qui précède peut être collé dans un terminal pour créer git-root et définir des bits d’exécution; le script réel se trouve aux lignes 2, 3 et 4.)

Ensuite, vous pourrez exécuter git root pour obtenir la racine de votre arborescence actuelle . Notez que dans le script Shell, utilisez "-e" pour que le shell se ferme en cas d’échec de l’analyse du rev ​​afin de pouvoir Obtenez correctement le statut de sortie et le message d'erreur si vous ne vous trouvez pas dans un répertoire git.

16
Emil Sit

Comme d'autres l'ont noté, le cœur de la solution consiste à utiliser git rev-parse --show-cdup. Cependant, il existe quelques cas Edge à traiter:

  1. Lorsque cwd est déjà la racine de l’arbre de travail, la commande génère une chaîne vide.
    En réalité, il génère une ligne vide, mais la substitution de commande supprime le saut de ligne de fin. Le résultat final est une chaîne vide.

    La plupart des réponses suggèrent de préfixer la sortie avec ./ de sorte qu'une sortie vide devienne "./" avant qu'elle soit transmise à cd.

  2. Lorsque GIT_WORK_TREE est défini sur un emplacement autre que le parent du cwd, la sortie peut être un chemin absolu.

    Le préfixe ./ est incorrect dans cette situation. Si un ./ est ajouté au début à un chemin absolu, il devient un chemin relatif (et ils ne font référence au même emplacement que si cwd est le répertoire racine du système).

  3. La sortie peut contenir des espaces.

    Cela ne s'applique vraiment que dans le second cas, mais la solution est simple: utilisez des guillemets autour de la substitution de commande (et de toute utilisation ultérieure de la valeur).

Comme d'autres réponses l'ont noté, nous pouvons faire cd "./$(git rev-parse --show-cdup)", mais cela casse dans le deuxième cas Edge (et le troisième cas Edge si nous omettons les guillemets doubles).

De nombreux shells considèrent cd "" comme un no-op, nous pouvons donc utiliser cd "$(git rev-parse --show-cdup)" (les guillemets doubles protègent la chaîne vide en tant qu'argument dans le premier cas Edge et préservent les espaces dans le troisième cas Edge). POSIX dit que le résultat de cd "" n'est pas spécifié, il est donc préférable d'éviter cette hypothèse.

Une solution qui fonctionne dans tous les cas ci-dessus nécessite un test quelconque. Fait explicitement, cela pourrait ressembler à ceci:

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

Aucune cd n'est faite pour le premier cas Edge.

S'il est acceptable d'exécuter cd . pour le premier cas Edge, la condition est possible dans le développement du paramètre:

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

Juste au cas où si vous fournissez ce chemin au Git lui-même, utilisez :/

# 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)
11
Nick Volynkin

Solutions courtes fonctionnant avec des sous-modules, dans des crochets et dans le répertoire .git

Voici la réponse courte que la plupart voudront:

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

Cela fonctionnera n'importe où dans un arbre de travail git (y compris à l'intérieur du répertoire .git), mais supposons que le (s) répertoire (s) du référentiel s'appelle .git (par défaut). Avec les sous-modules, cela ira à la racine du référentiel contenant le plus externe. 

Si vous voulez aller à la racine de l'utilisation actuelle du sous-module:

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

Pour exécuter facilement une commande dans la racine de votre sous-module, sous [alias] dans votre .gitconfig, ajoutez:

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

Cela vous permet de faire facilement des choses comme git sh ag <string>

Solution robuste prenant en charge les répertoires .git ou $GIT_DIR portant un nom différent ou externes.

Notez que $GIT_DIR peut pointer quelque part vers l'extérieur (et ne pas s'appeler .git), d'où la nécessité d'une vérification supplémentaire. 

Mettez ceci dans votre .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"
}

Exécutez-le en tapant git_root (après avoir redémarré votre shell: exec bash)

10
Tom Hale

Pour modifier la "configuration de git", répondez un peu:

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

et faire le chemin nettoyé. Très agréable.

8
Bruce K

Si vous cherchez un bon alias pour le faire, ne pas exploser cd si vous n'êtes pas dans un répertoire git

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

Cet alias Shell fonctionne que vous soyez dans un sous-répertoire git ou au niveau supérieur:

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

mis à jour pour utiliser la syntaxe moderne au lieu de backticks: 

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

Voici un script que j'ai écrit qui traite les deux cas: 1) un référentiel avec un espace de travail, 2) un référentiel nu.

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

git-root (fichier exécutable dans votre chemin):

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

J'espère que cela est utile.

5
jdsumsion

git-extras

ajoute $ git root
voir 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

Disponibilité de git-extras

5
Hotschke
alias git-root='cd \`git rev-parse --git-dir\`; cd ..'

Tout le reste échoue à un moment donné, que ce soit dans le répertoire de base ou simplement en échec lamentable. C'est le moyen le plus rapide et le plus rapide de retourner à GIT_DIR.

5
tocororo
$ git config alias.root '!pwd'
# then you have:
$ git root
4
FractalSpace

Depuis Git 2.13.0 , il prend en charge une nouvelle option permettant d’afficher le chemin du projet racine, qui fonctionne même lorsqu’il est utilisé depuis un sous-module:

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

J'ai dû résoudre ce problème moi-même aujourd'hui. Résolu en C # car j'en avais besoin pour un programme, mais je suppose que ça peut être facilement réécrit. Considérez ce domaine public.

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;

}
1
Hylke Bons

Si quelqu'un a besoin d'un moyen conforme à POSIX pour le faire, sans l'exécutable git:

git-root :

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

Si vous souhaitez simplement que la fonctionnalité fasse partie d'un script, supprimez Shebang et remplacez la dernière ligne git_root_recurse_parent par:

git_root() {
    (git_root_recurse_parent)
}
1
swalog

Alias ​​de shell préconfigurés dans les cadres de shell

Si vous utilisez un framework Shell, il est possible qu'un alias Shell soit déjà disponible:

  • $ grt dans oh-my-zsh (68k) (cd $(git rev-parse --show-toplevel || echo "."))
  • $ git-root dans prezto (8.8k) (affiche le chemin d'accès à la racine de l'arbre de travail)
  • $ g..zimfw (1k) (modifie le répertoire actuel au niveau supérieur de l'arbre de travail.)
1
Hotschke

Je voulais développer l'excellent commentaire de Daniel Brockman.

Définir git config --global alias.exec '!exec ' vous permet de faire des choses comme git exec make car, en tant que man git-config, vous indiquez:

Si le développement de l'alias est précédé d'un point d'exclamation, il sera traité comme une commande Shell. [...] Notez que les commandes Shell seront exécutées à partir du répertoire de niveau supérieur d'un référentiel, qui ne correspond pas nécessairement au répertoire actuel.

Il est également utile de savoir que $GIT_PREFIX sera le chemin du répertoire actuel par rapport au répertoire de niveau supérieur d'un référentiel. Mais, sachant que ce n’est que la moitié de la bataille ™. L'expansion variable de Shell le rend plutôt difficile à utiliser. Je suggère donc d'utiliser bash -c comme suit:

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

les autres commandes comprennent:

git exec pwd
git exec make
0
Bruno Bronosky