Mercurial a un moyen d'imprimer le répertoire racine (qui contient .hg) via
hg root
Y a-t-il quelque chose d'équivalent dans git pour obtenir le répertoire qui contient le répertoire .git?
bzr root
été beaucoup utilisée dans Bazaar
Mercurial a un moyen d'imprimer le répertoire racine (qui contient .hg) via
hg root
Y a-t-il quelque chose d'équivalent dans git pour obtenir le répertoire qui contient le répertoire .git?
bzr root
été beaucoup utilisée dans Bazaar
Réponses:
Oui:
git rev-parse --show-toplevel
Si vous souhaitez répliquer la commande Git plus directement, vous pouvez créer un alias :
git config --global alias.root 'rev-parse --show-toplevel'
et git root
fonctionnera maintenant comme hg root
.
Remarque : dans un sous-module, cela affichera le répertoire racine du sous - module et non le référentiel parent. Si vous utilisez Git> = 2.13 ou supérieur, il existe un moyen pour les sous - modules d'afficher le répertoire racine du superprojet. Si votre git est plus ancien que cela, consultez cette autre réponse.
git config --global alias.exec '!exec '
pour pouvoir faire des choses comme git exec make
. Cela fonctionne car les alias du shell sont toujours exécutés dans le répertoire de niveau supérieur.
hg root
fait. Il imprime le répertoire de niveau supérieur de votre référentiel extrait. Il ne vous y fait pas basculer (et il ne pouvait pas, en fait, le faire en raison de l'interaction de l'ensemble du concept de répertoire actuel et de votre shell).
~/my.proj/foo/bar
et ~/my.proj
auquel vous avez un lien symbolique ~/src/my.proj
, la commande ci-dessus vous amènera ~/src/my.proj
. Cela pourrait être un problème si tout ce que vous voulez faire après cela n'est pas indépendant de l'arbre.
.git
répertoire du projet .
La man
page pour git-config
(sous Alias ) dit:
Si l'extension d'alias est préfixée d'un point d'exclamation, elle sera traitée 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 n'est pas nécessairement le répertoire courant.
Donc, sous UNIX, vous pouvez faire:
git config --global --add alias.root '!pwd'
.zshrc
, et je définis `alias cg =" cd $ (racine git) ", la partie $ () est évaluée au moment de la source, et pointe toujours vers ~ / dotfiles, car c'est là que se trouve mon zshrc .
A --show-toplevel
récemment été ajouté git rev-parse
ou pourquoi personne ne le mentionne?
Depuis la git rev-parse
page de manuel:
--show-toplevel
Show the absolute path of the top-level directory.
git-rev-parse
- en raison de son nom suggérant qu'il s'agit de traiter les spécifications de révision. BTW, je serais heureux de voir un git --work-tree
travail similaire à git --exec-path[=<path>]
: "Si aucun chemin n'est donné, git imprimera le paramètre actuel"; au moins, OMI, ce serait un endroit logique pour rechercher une telle fonctionnalité.
root = rev-parse --show-toplevel
créer un alias dans votre gitconfig.
git config --global alias.root "rev-parse --show-toplevel"
et ensuite git root
vous pourrez faire le travail
git rev-parse --show-toplevel
fonctionne quand je l'ai essayé dans un sous-module. Il affiche le répertoire racine du sous-module git. Qu'est-ce que cela imprime pour vous?
--show-cdup
à --show-top-level
en février 2011 (après la soumission de cette réponse).
Et " git rev-parse --git-dir
"?
F:\prog\git\test\copyMerge\dirWithConflicts>git rev-parse --git-dir
F:/prog/git/test/copyMerge/.git
L' --git-dir
option semble fonctionner.
Depuis la 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 git setup-sh
script .
Si vous êtes dans un dossier de sous-module, avec Git> = 2.13 , utilisez :
git rev-parse --show-superproject-working-tree
Si vous utilisez git rev-parse --show-toplevel
, assurez-vous que c'est avec Git 2.25+ (Q1 2020) .
.git
si vous êtes déjà dans le répertoire racine. (Au moins, il le fait sur msysgit.)
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 puis vous voudrez peut-être ajouter ce qui suit à votre ~/.bashrc
:
alias cdroot='cd $(git root)'
afin que vous puissiez simplement utiliser cdroot
pour aller en haut de votre repo.
Si vous êtes déjà au niveau supérieur ou pas dans un référentiel git cd $(git rev-parse --show-cdup)
vous ramènera à la maison (juste un cd). cd ./$(git rev-parse --show-cdup)
est une façon de résoudre ce problème.
cd "$(git rev-parse --show-cdup)"
. Cela fonctionne car cd ""
vous ne mène nulle part, plutôt que de revenir $HOME
. Et il est préférable de citer les invocations $ () de toute façon, au cas où ils produiraient quelque chose avec des espaces (pas que cette commande le fasse dans ce cas, cependant).
$PWD
. Cet exemple résoudra la racine de git par rapport à $PWD
au lieu de realpath $PWD
.
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 git rev-parse:
gitroot=$(readlink -f ./$(git rev-parse --show-cdup))
git-rev-parse --show-cdup
vous donne le bon nombre de ".." pour accéder à la racine depuis votre cwd, ou la chaîne vide si vous êtes à la racine. Ajoutez ensuite "./" pour traiter le cas de chaîne vide et utilisez
readlink -f
pour traduire en un chemin complet.
Vous pouvez également créer une git-root
commande 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.)
Et puis vous seriez en mesure d'exécuter git root
pour obtenir la racine de votre arbre actuel. Notez que dans le script shell, utilisez "-e" pour provoquer la fermeture du shell si le rev-parse échoue afin que vous puissiez correctement obtenir l'état de sortie et le message d'erreur si vous n'êtes pas dans un répertoire git.
"$(git rev-parse ...)"
au lieu de hacks comme ./$(git rev-parse ...)
.
Comme d'autres l'ont noté, le cœur de la solution est d'utiliser git rev-parse --show-cdup
. Cependant, il y a quelques cas marginaux à résoudre:
Lorsque le cwd est déjà la racine de l'arborescence de travail, la commande renvoie une chaîne vide.
En fait, il produit une ligne vide, mais la substitution de commande supprime la coupure de ligne de fin. Le résultat final est une chaîne vide.
La plupart des réponses suggèrent d'ajouter la sortie de ./
telle sorte qu'une sortie vide devienne "./"
avant d'être alimentée cd
.
Lorsque GIT_WORK_TREE est défini sur un emplacement qui n'est pas le parent du cwd, la sortie peut être un chemin d'accès absolu.
La préparation ./
est incorrecte dans cette situation. Si a ./
est ajouté à un chemin absolu, il devient un chemin relatif (et ils ne font référence au même emplacement que si le cwd est le répertoire racine du système).
La sortie peut contenir des espaces.
Cela ne s'applique vraiment que dans le deuxième cas, mais il a une solution simple: utilisez des guillemets doubles autour de la substitution de commande (et toute utilisation ultérieure de la valeur).
Comme d'autres réponses l'ont noté, nous pouvons le faire cd "./$(git rev-parse --show-cdup)"
, mais cela casse dans le deuxième cas de bord (et le troisième cas de bord si nous laissons les guillemets doubles).
De nombreux shells traitent cd ""
comme un no-op, donc pour ces shells, nous pourrions le faire cd "$(git rev-parse --show-cdup)"
(les guillemets doubles protègent la chaîne vide comme argument dans le premier cas de bord et préservent les espaces dans le troisième cas de bord). POSIX indique que le résultat de cd ""
n'est pas spécifié, il est donc préférable d'éviter de faire 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"
Aucun cd
n'est fait pour le premier cas de bord.
S'il est acceptable d'exécuter cd .
pour le premier cas de bord, alors le conditionnel peut être fait dans l'expansion du paramètre:
cdup="$(git rev-parse --show-cdup)" && cd "${cdup:-.}"
git root
' que la réponse ci-dessus utilise?
Au cas où si vous alimentez ce chemin vers le 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)
Solutions courtes qui fonctionnent avec des sous-modules, dans des crochets et à l'intérieur du .git
répertoire
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 une arborescence de travail git (y compris à l'intérieur du .git
répertoire), mais suppose que les répertoires du référentiel sont appelés .git
(ce qui est la valeur par défaut). Avec les sous-modules, cela ira à la racine du référentiel contenant le plus à l'extérieur.
Si vous souhaitez accéder à 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 votre racine de 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 qui prend en charge des répertoires nommés différemment ou externes .git
ou $GIT_DIR
.
Notez que cela $GIT_DIR
peut pointer quelque part vers l'extérieur (et ne pas être appelé .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 1 # git_root will print any errors
cd "$root"
}
Exécuter en tapant git_root
(après avoir redémarré votre shell: exec bash
)
(root=$(git rev-parse --git-dir)/ && cd ${root%%/.git/*} && git rev-parse && pwd)
mais cela ne couvre pas les $GIT_DIR
s externes qui sont nommés autrement que.git
Si vous êtes à la recherche d'un bon alias pour le faire, sans exploser cd
si vous n'êtes pas dans un répertoire git:
alias ..g='git rev-parse && cd "$(git rev-parse --show-cdup)"'
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
$ brew install git-extras
$ apt-get install git-extras
Cet alias de 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 une syntaxe moderne au lieu 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 ..'
Tout le reste échoue à un moment donné, soit en allant dans le répertoire personnel, soit en échouant lamentablement. C'est le moyen le plus rapide et le plus court pour revenir au GIT_DIR.
$GIT_DIR
est détaché de l'arbre de travail à l'aide de -Files .git
et gitdir: SOMEPATH
. Par conséquent, cela échoue également pour les sous-modules, où $GIT_DIR
contient .git/modules/SUBMODULEPATH
.
Voici un script que j'ai écrit qui gère les deux cas: 1) référentiel avec un espace de travail, 2) référentiel nu.
https://gist.github.com/jdsumsion/6282953
git-root
(fichier exécutable sur 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 vous sera utile.
git exec
idée est plus utile dans les référentiels non dénudés. Cependant, ce script dans ma réponse gère correctement le cas nu contre non nu, ce qui pourrait être utile à quelqu'un, donc je laisse cette réponse ici.
git submodule
s où $GIT_DIR
contient quelque chose comme /.git/modules/SUBMODULE
. Vous supposez également que le .git
répertoire fait partie de l'arbre de travail dans le cas non nu.
$ git config alias.root '!pwd'
# then you have:
$ git root
[alias] findroot = "!f () { [[ -d ".git" ]] && echo "Found git in [
pwd ]" && exit 0; cd .. && echo "IN
pwd" && f;}; f"
git config --global alias.root '!pwd'
marche. Je n'ai pu repérer aucun cas où il agit différemment de la variante non globale. (Unix, git 1.7.10.4) BTW: Votre findroot
requiert un /.git
pour éviter une récursion sans fin.
Depuis Git 2.13.0 , il prend en charge une nouvelle option pour afficher le chemin du projet racine, qui fonctionne même lorsqu'il est utilisé depuis l'intérieur d'un sous-module:
git rev-parse --show-superproject-working-tree
Si vous utilisez un framework shell, un alias shell peut déjà être disponible:
$ grt
en 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) (change le répertoire courant au niveau supérieur de l'arborescence de travail.)Je voulais développer l'excellent commentaire de Daniel Brockman.
La définition git config --global alias.exec '!exec '
vous permet de faire des choses comme git exec make
parce que, comme l' man git-config
indique:
Si l'extension d'alias est préfixée d'un point d'exclamation, elle sera traitée 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 n'est pas nécessairement le répertoire courant.
Il est également utile de savoir que ce $GIT_PREFIX
sera le chemin d'accès au 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 du shell le rend plutôt difficile à utiliser. Je suggère donc d'utiliser bash -c
comme ça:
git exec bash -c 'ls -l $GIT_PREFIX'
les autres commandes incluent:
git exec pwd
git exec make
Dans le cas où quelqu'un aurait besoin d'une méthode compatible POSIX pour le faire, sans avoir besoin d'un git
exécutable:
#$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 le shebang et remplacez la dernière git_root_recurse_parent
ligne par:
git_root() {
(git_root_recurse_parent)
}
if
instruction était censée vérifier si vous avez changé de répertoire dans la récursivité. S'il reste dans le même répertoire, il suppose que vous êtes coincé quelque part (par exemple /
) et freine la récursivité. Le bogue est corrigé et devrait maintenant fonctionner comme prévu. Merci de l'avoir signalé.
J'ai dû résoudre ce problème moi-même aujourd'hui. Résolu en C # comme j'en avais besoin pour un programme, mais je suppose qu'il peut être réécrit en toute simplicité. 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;
}