6

When working on projects that are version controlled I often end up in a nested folder. Using cd ../../ gets a bit cumbersome, so I was wondering if there is a way to define an alias in bash that cd's to the parent until it reaches a folder with a .git folder, or it reaches ~.

So let's say that

  • ~/my-project is a git repository
  • my cwd is ~/my-project/deeply/nested/folder/structure
  • I run the command and am cd'ed to ~/my-project

What command could do this? Would I need a bash function?

3 Answers3

12

The command you're looking for is git rev-parse --show-toplevel, you can make a bash function that uses it.

gr() {
    cd "$(git rev-parse --show-toplevel)"
}

I also have in my ~/.gitconfig:

[alias]
    root = rev-parse --show-toplevel

Which makes this command a little more reasonable looking.

U2EF1
  • 12,907
  • 3
  • 35
  • 37
  • 2
    Distilled: paste into your `.bash_aliases`: `alias gr='cd "$(git rev-parse --show-toplevel)"'` – Ole Sep 04 '18 at 12:25
4

I think the answer you already have is good (and possibly more efficient) but a basic way of achieving this would be to use a loop:

until [ -d .git ]; do cd ..; done

That is, move into the parent directory until the directory .git exists.

To protect against an infinite loop if you run the command from outside of a git repo, you could add a basic check:

until [ -d .git ] || [ "$PWD" = "$p" ]; do p=$PWD; cd ..; done

I'm not sure how portable it is, but on my system I have a variable $OLDPWD which can be used instead:

until [ -d .git ] || [ "$PWD" = "$OLDPWD" ]; do cd ..; done
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
0

Again, already answered, but I have a script as follows that does essentially that:

#!/bin/bash
filename=$1
[[ ! ${filename} ]] && echo "no filename" && exit -1
path=$(pwd)
while [[ "${path}" != "" && ! -e ${path}/${filename} ]]; do
   path=${path%/*}
done
[[ "${path}" == "" ]] && exit -1;
cd ${path}

where you would do findup .git (which in turn could be turned into an alias)

blackghost
  • 1,730
  • 11
  • 24
  • I would change the first test to either `! [ "$filename" ]`, or if you're going for bash-only, then `[[ ! $filename ]]`. You could also drop the `!` and use `||` instead of `&&`. – Tom Fenech Jun 01 '17 at 16:47