Есть ли способ получить корневой каталог git в одной команде?

Mercurial имеет способ распечатать корневой каталог (содержащий .hg) через

hg root

Есть ли что-то эквивалентное в git для получения каталога, содержащего каталог .git?

Ответ 1

Да:

git rev-parse --show-toplevel

Примечание: в субмодуле будет отображаться корневой каталог субмодуля, а не родительский репозиторий. Если вы используете Git> = 2.13 или выше, вот способ, которым подмодули могут показать корневой каталог суперпроекта. Если ваш git старше этого, см. этот другой ответ.

Ответ 2

Страница man для git-config (под псевдонимом) гласит:

Если расширение псевдонима начинается с восклицательного знака, оно будет рассматриваться как оболочка команда. [...] Обратите внимание, что оболочка команды будут выполняться из каталога верхнего уровня репозитория, который может не обязательно быть текущим каталогом.

Итак, в UNIX вы можете сделать:

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

Ответ 3

Добавлен ли --show-toplevel только недавно в git rev-parse или почему его никто не упоминает?

На странице git rev-parse man:

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

Ответ 4

Как насчет "git rev-parse --git-dir"?

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

Кажется, опция --git-dir работает.

Со страницы руководства git rev-parse:

--git-dir

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

Вы можете увидеть это в действии в этом git setup-sh сценарии.

Если вы находитесь в папке субмодуля, с Git> = 2.13, используйте:

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

Ответ 5

Чтобы написать простой ответ здесь, чтобы мы могли использовать

git root

чтобы выполнить задание, просто настройте свой git, используя

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

а затем вы можете добавить следующее к своему ~/.bashrc:

alias cdroot='cd $(git root)'

чтобы вы могли просто использовать cdroot, чтобы перейти в начало своего репо.

Ответ 6

Если вы уже находитесь на верхнем уровне или не находитесь в репозитории git cd $(git rev-parse --show-cdup), вы попадете домой (только cd). cd ./$(git rev-parse --show-cdup) является одним из способов его фиксации.

Ответ 7

Как отмечали другие, суть решения заключается в использовании git rev-parse --show-cdup. Тем не менее, есть несколько крайних случаев для адреса:

  • Когда cwd уже является корнем рабочего дерева, команда дает пустую строку.
    На самом деле он создает пустую строку, но команда замещает полосу отрывной линии. Конечным результатом является пустая строка.

    В большинстве ответов предлагается добавить вывод с помощью ./, чтобы пустой результат становился "./", прежде чем он будет отправлен на cd.

  • Когда GIT_WORK_TREE установлен в местоположение, которое не является родительским элементом cwd, вывод может быть абсолютным именем пути.

    Превращение ./ неверно в этой ситуации. Если a ./ добавляется к абсолютному пути, он становится относительным путем (и они относятся только к одному и тому же адресу, если cwd является корневым каталогом системы).

  • Вывод может содержать пробелы.

    Это действительно применимо только во втором случае, но у него есть легкое исправление: используйте двойные кавычки вокруг подстановки команды (и любых последующих применений значения).

Как отмечали другие ответы, мы можем сделать cd "./$(git rev-parse --show-cdup)", но это ломается во втором краевом случае (и в случае третьего края, если мы опустим двойные кавычки).

Многие оболочки рассматривают cd "" как no-op, поэтому для этих оболочек мы можем сделать cd "$(git rev-parse --show-cdup)" (двойные кавычки защищают пустую строку как аргумент в первом краевом случае и сохраняют пробелы в третьем краевом случае). POSIX говорит, что результат cd "" не указан, поэтому лучше избегать этого предположения.

Решение, которое работает во всех вышеперечисленных случаях, требует какого-то теста. Выполнено явно, это может выглядеть так:

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

Для первого случая края не выполняется cd.

Если допустимо запустить cd . для первого случая края, то условное может быть выполнено при расширении параметра:

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

Ответ 8

Чтобы вычислить абсолютный путь текущего корневого каталога git, скажем, для использования в оболочке script, используйте эту комбинацию readlink и git rev-parse:

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

git-rev-parse --show-cdup дает вам правильное количество "..", чтобы получить к корню из вашего cwd, или пустую строку, если вы находитесь в корне. Затем добавьте "./", чтобы иметь дело с пустой строкой и использовать readlink -f, чтобы перевести на полный путь.

Вы также можете создать команду git-root в PATH в качестве оболочки script, чтобы применить эту технику:

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 -root и установки битов выполнения, фактический script находится в строках 2, 3 и 4.)

И тогда вы сможете запустить git root, чтобы получить корень вашего текущего дерева. Обратите внимание, что в оболочке script используйте "-e", чтобы заставить оболочку выйти, если сбой rev-parse завершился с ошибкой, чтобы вы могли правильно получить статус выхода и сообщение об ошибке, если вы не находитесь в каталоге git.

Ответ 9

На всякий случай, если вы кормите этот путь к самому Git, используйте :/

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

Ответ 10

Короткие решения, которые работают с подмодулями, в хуках и внутри директории .git

Вот краткий ответ, который больше всего захочется:

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

Это будет работать в любом месте рабочего дерева git (в том числе внутри каталога .git), но предполагает, что каталог хранилища называются .git (который используется по умолчанию). С подмодулями это пойдет в корень самого внешнего содержащего репозитория.

Если вы хотите получить доступ к корню текущего субмодуля, используйте:

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

Чтобы легко выполнить команду в корневом каталоге подмодуля, в разделе [alias] в вашем .gitconfig добавьте:

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

Это позволяет вам легко делать такие вещи, как git sh ag <string>

Надежное решение, которое поддерживает каталоги .git или $GIT_DIR с разными именами.

Обратите внимание, что $GIT_DIR может указывать на что-то внешнее (и не называться .git), поэтому необходима дополнительная проверка.

Поместите это в свой .bashrc:

# Print the name of the git working tree 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 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 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"
}

Выполните его, набрав git_root (после перезапуска вашей оболочки: exec bash)

Ответ 11

Чтобы внести изменения в "git config", ответьте немного:

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

и очистите путь. Очень приятно.

Ответ 12

Если вы ищете хороший псевдоним, чтобы сделать это, а не взорвать cd, если вы не находитесь в директории git:

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

Ответ 14

Этот псевдоним оболочки работает независимо от того, находитесь ли вы в поддиректоре git или на верхнем уровне:

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

обновлен, чтобы использовать современный синтаксис вместо обратных ссылок:

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

Ответ 15

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

Все остальное не удается в какой-то момент либо перейти в домашний каталог, либо просто провалиться. Это самый быстрый и самый короткий способ вернуться к GIT_DIR.

Ответ 16

Вот скрипт, который я написал, который обрабатывает оба случая: 1) хранилище с рабочим пространством, 2) пустое хранилище.

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

git-root (исполняемый файл на вашем пути):

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

Надеюсь, это полезно.

Ответ 17

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

Ответ 18

Начиная с Git 2.13.0, он поддерживает новую опцию показа пути корневого проекта, которая работает даже при использовании изнутри подмодуля:

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

Ответ 19

Предварительно настроенные псевдонимы оболочки в оболочках оболочки

Если вы используете оболочку оболочки, возможно, уже существует псевдоним оболочки:

  • $ grt в о-о-о (68k) (cd $(git rev-parse --show-toplevel || echo "."))
  • $ git-root в prezto (8.8k) (отображает путь к корню рабочего дерева)
  • $ g.. zimfw (1k) (изменяет текущий каталог на верхний уровень рабочего дерева.)

Ответ 20

На случай, если кому-то понадобится POSIX-совместимый способ сделать это без использования исполняемого файла 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

Если вы просто хотите, чтобы функциональность была частью сценария, удалите shebang и замените последнюю строку git_root_recurse_parent на:

git_root() {
    (git_root_recurse_parent)
}

Ответ 21

Я хотел бы остановиться на превосходном комментарии Дэниела Брокмана.

Определение git config --global alias.exec '!exec ' позволяет вам делать такие вещи, как git exec make, потому что, как утверждает man git-config:

Если расширение псевдонима начинается с восклицательного знака, оно будет рассматриваться как команда оболочки. [...] Обратите внимание, что команды оболочки будут выполняться из каталога верхнего уровня репозитория, который не обязательно может быть текущим каталогом.

Также полезно знать, что $GIT_PREFIX будет путем к текущему каталогу относительно каталога верхнего уровня хранилища. Но, зная, что это только полдела ™. Расширение переменной оболочки делает его довольно сложным в использовании. Поэтому я предлагаю использовать bash -c примерно так:

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

другие команды включают в себя:

git exec pwd
git exec make

Ответ 22

Надо было решить это сам сегодня. Решил его на С#, поскольку мне это нужно для программы, но я думаю, что это можно переписать. Рассмотрим этот общедоступный домен.

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;

}