2

I use Fish shell. Sometimes, I find myself in a child directory of a child directory of a child directory, several layers deep, and I want to return to the closest parent with a .git directory, as this directory represents the root project directory, so to speak.

For example, let's say I have this layout:

./
└── countries/
    ├── .git/
    └── regions/
        └── cities/

Whethere I am in countries/ or countries/regions/ or countries/regions/cities/, I would like this command to change directory to countries/, because it is the directory that contains .git.

How do I do this in Fish?

3 Answers 3

4

git rev-parse has a "--show-cdup" option that'll print an argument to give to cd:

set -l cd (git rev-parse --show-cdup 2>/dev/null); and cd $cd

The 2>/dev/null silences the "not a git repository" error, and git rev-parse --show-cdup will return falsey in that case, so this won't do any cd if used outside of a git repo.

Alternatively you could use git rev-parse --show-toplevel. I'm reasonably sure that --show-cdup will go by the physical working directory, so if you had ended up in a git repo by going through a symlink it might print the wrong answer.

1

Here's a function that accomplishes this:


function cdroot
  set directory_x (pwd)

  while test -n "$directory_x" -a ! -e "$directory_x/.git"
    set parts (string split '/' $directory_x)
    set --erase parts[(count $parts)]
    set directory_x (string join '/' $parts)
  end

  if test -n "$directory_x"
    cd "$directory_x"
  else
    echo "Could not find closest root directory with .git child" >&2
    return 1
  end

end

To install it, put it in ~/.config/fish/functions/cdroot.fish

2
  • 4
    Note that you can use git rev-parse --show-toplevel to get the absolute path of the current repository. This may simplify your function a bit.
    – Kusalananda
    Commented Nov 22, 2023 at 10:51
  • @Kusalananda Thanks. I would upvote other answers that use this approach. I personally want to keep my answer as is, as it is an approach that could be modified for other directories, not just .git
    – Flimm
    Commented May 3 at 10:01
1

While git rev-parse can make this trivial, here's a naïve, generalised solution that could be adapted to a non-.git use case.

set start "$PWD"
while test ! -e .git
  if ! cd .. ; or test "$PWD" = /
    break
  end
end
if test ! -e .git
  cd "$start"
end
2
  • The trouble with this approach is that it breaks returning to the previous current working directory using cd - . It will also cd to / if it can't find the .git directory.
    – Flimm
    Commented Nov 23, 2023 at 14:27
  • Good point about ending up in /, easily rectified. Your question doesn't mention anything about preserving cd - so that wasn't in consideration.
    – bxm
    Commented Nov 23, 2023 at 21:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .