Is there a way to get the git root directory in one command?

前端 未结 22 978
被撕碎了的回忆
被撕碎了的回忆 2020-11-22 09:57

Mercurial has a way of printing the root directory (that contains .hg) via

hg root

Is there something equivalent in git to get the director

相关标签:
22条回答
  • 2020-11-22 10:43
    alias git-root='cd \`git rev-parse --git-dir\`; cd ..'
    

    Everything else fails at some point either going to the home directory or just miserably failing. This is the quickest and shortest way to get back to the GIT_DIR.

    0 讨论(0)
  • 2020-11-22 10:45

    As others have noted, the core of the solution is to use git rev-parse --show-cdup. However, there are a few of edge cases to address:

    1. When the cwd already is the root of the working tree, the command yields an empty string.
      Actually it produces an empty line, but command substitution strip off the trailing line break. The final result is an empty string.

      Most answers suggest prepending the output with ./ so that an empty output becomes "./" before it is fed to cd.

    2. When GIT_WORK_TREE is set to a location that is not the parent of the cwd, the output may be an absolute pathname.

      Prepending ./ is wrong in this situation. If a ./ is prepended to an absolute path, it becomes a relative path (and they only refer to the same location if the cwd is the root directory of the system).

    3. The output may contain whitespace.

      This really only applies in the second case, but it has an easy fix: use double quotes around the command substitution (and any subsequent uses of the value).

    As other answers have noted, we can do cd "./$(git rev-parse --show-cdup)", but this breaks in the second edge case (and the third edge case if we leave off the double quotes).

    Many shells treat cd "" as a no-op, so for those shells we could do cd "$(git rev-parse --show-cdup)" (the double quotes protect the empty string as an argument in the first edge case, and preserve whitespace in the third edge case). POSIX says the result of cd "" is unspecified, so it may be best to avoid making this assumption.

    A solution that works in all of the above cases requires a test of some sort. Done explicitly, it might look like this:

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

    No cd is done for the first edge case.

    If it is acceptable to run cd . for the first edge case, then the conditional can be done in the expansion of the parameter:

    cdup="$(git rev-parse --show-cdup)" && cd "${cdup:-.}"
    
    0 讨论(0)
  • 2020-11-22 10:47

    The man page for git-config (under Alias) says:

    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.

    So, on UNIX you can do:

    git config --global --add alias.root '!pwd'
    
    0 讨论(0)
  • 2020-11-22 10:47

    To calculate the absolute path of the current git root directory, say for use in a shell script, use this combination of readlink and git rev-parse:

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

    git-rev-parse --show-cdup gives you the right number of ".."s to get to the root from your cwd, or the empty string if you are at the root. Then prepend "./" to deal with the empty string case and use readlink -f to translate to a full path.

    You could also create a git-root command in your PATH as a shell script to apply this 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
    

    (The above can be pasted into a terminal to create git-root and set execute bits; the actual script is in lines 2, 3 and 4.)

    And then you'd be able to run git root to get the root of your current tree. Note that in the shell script, use "-e" to cause the shell to exit if the rev-parse fails so that you can properly get the exit status and error message if you are not in a git directory.

    0 讨论(0)
  • 2020-11-22 10:47

    git-extras

    adds $ git root
    see 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
    

    Availability of git-extras

    • homebrew(osx)/linuxbrew(linux) $ brew install git-extras
    • debian/ubuntu repos (https://packages.debian.org/sid/git-extras) $ apt-get install git-extras
    0 讨论(0)
  • 2020-11-22 10:48

    Short solutions that work with submodules, in hooks, and inside the .git directory

    Here's the short answer that most will want:

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

    This will work anywhere in a git working tree (including inside the .git directory), but assumes that repository directory(s) are called .git (which is the default). With submodules, this will go to the root of the outermost containing repository.

    If you want to get to the root of the current submodule use:

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

    To easily execute a command in your submodule root, under [alias] in your .gitconfig, add:

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

    This allows you to easily do things like git sh ag <string>

    Robust solution that supports differently named or external .git or $GIT_DIR directories.

    Note that $GIT_DIR may point somewhere external (and not be called .git), hence the need for further checking.

    Put this in your .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"
    }
    

    Execute it by typing git_root (after restarting your shell: exec bash)

    0 讨论(0)
提交回复
热议问题