62

I want to automatically cd to the directory created by the clone command after I git cloned something.

Important: I don't want to alter the syntax for the command (e.g. use an alias/function) because it would break the zsh-completions I get automatically from the Pretzo project.

EDIT: The reason I didn't pick any answer as correct, is because no answer was given that did comply with the condition above.

I use ZSH, but an answer in any other shell is acceptable as well.

2
  • I like the answer unix.stackexchange.com/questions/97920/… but it need to mention to write that function on ~/.bashrc file (for example) and for me at least it didn't work, I needed to fix it with the replacing the line : /usr/bin/git "$@" 2>&1 | tee $tmp
    – lcestari
    Jan 6, 2015 at 19:12
  • I wonder if cloning repositories is something that you do so frequently that issuing a separate cd command after git clone becomes a real burden?
    – Kusalananda
    Jun 12, 2021 at 15:35

16 Answers 16

52

Create a function:

gclonecd() {
  git clone "$1" && cd "$(basename "$1" .git)"
}

(Works for links both with and without ".git")

5
  • 2
    I would like to keep using git clone because of all the tools that I would have to change if I were not using git. Hub is aliased as git, oh-my-zsh provides auto-completion, etc...
    – wildeyes
    Oct 28, 2013 at 14:44
  • 5
    @Wildeyes git can't change the working directory of your shell, so either way you'll have to wrap it in an alias/function that does what you want and follows it up with the builtin cd. If you want to call hub instead of git, do so in the function. If you want it to only affect the clone command, check for the action in the function. etc. Oct 28, 2013 at 15:04
  • 1
    I noticed that basename [email protected]:pckujawa/pattern-rec.git results in pattern-rec.git - do you just leave off the .git extension when you clone?
    – Pat
    Jun 28, 2014 at 5:03
  • 13
    @Pat basename also takes a suffix argument, so you can do: git clone $1 && cd $(basename $1 .git)
    – Henrik N
    Nov 13, 2015 at 20:40
  • 1
    How to write this as a git alias?
    – Ben Keil
    Jan 14, 2022 at 5:05
24

git clone takes an additional argument: the directory to use. You can make it clone into the current working directory with git clone URL . Then, there is no need to change working directory; you are already there.

If you really want the git command to actually change working directory, you can change it to a function which calls the real git when necessary:

git()
{
   local tmp=$(mktemp)
   local repo_name

   if [ "$1" = clone ] ; then
     /usr/bin/git "$@" | tee $tmp
     repo_name=$(awk -F\' '/Cloning into/ {print $2}' $tmp)
     rm $tmp
     printf "changing to directory %s\n" "$repo_name"
     cd "$repo_name"
   else
     /usr/bin/git "$@"
   fi
}
10
  • That was exactly what I was looking for. After I realized there are no internal hooks or something like that, I wrote an almost exact function :)
    – wildeyes
    Oct 28, 2013 at 19:38
  • 2
    @Wildeyes Yes: have a /tmp volume which is in RAM, like any modern Linux does via tmpfs. Anyway, why would you care about writing to the disk, when you've just done a git clone, which creates directories and writes perhaps hundreds of files to the disk ...
    – Kaz
    Oct 29, 2013 at 19:02
  • 1
    You're probably right about the whole "nvm if it writes to disk" thing, I was just looking to save extra operations. Thanks a lot Kaz.
    – wildeyes
    Oct 29, 2013 at 19:24
  • 3
    Be careful. it is git [flags] command [arguments], so blindly asking for clone in certain position will fail.
    – vonbrand
    Feb 13, 2016 at 22:19
  • 1
    Would adding the "real" path help? Like: git_real_home=which git git() { ... $git_real_home "$@" | tee $tmp
    – aakoch
    Jan 5, 2017 at 19:31
15

If you provide the local name of the repo, it's really easy using $_:

git clone [email protected]:npm/npm.git npm-local-dir && cd $_

But if you don't want to re-type long names, it's a little ugly but doable with sed:

git clone [email protected]:pckujawa/pattern-rec.git && 
cd `echo $_ | sed -n -e 's/^.*\/\([^.]*\)\(.git\)*/\1/p'`

EDIT: Well, I was going to thank Manav (comment below) for the terse

git clone foo/bar && cd !$:t

but it doesn't work in zsh (something about how zsh does expansions causes it not to treat the first command as input to !$). I had to look up what !$:t did (Advancing in the Bash Shell | ${me:-whatever}). !$ grabs the last part of the previous command and :t will "Remove all leading file name components, leaving the tail." Good information, but I wish I could get it to work with zsh (I tried noglob with no success).

EDIT: Instead of using sed or !$:t (which doesn't work in zsh, for me anyway), I found two other options (one adapting https://unix.stackexchange.com/users/5685/frederik-deweerdt 's answer with https://unix.stackexchange.com/users/129926/henrik-n 's comment):

git clone [email protected]:npm/npm.git && cd $(basename $_ .git)

or

git clone [email protected]:npm/npm.git && cd ${${_%%.git*}##*/}
2
  • git clone foo/bar && cd !$:t
    – Manav
    Mar 21, 2015 at 6:52
  • If your repo url ends in .git you can use !$:t:r to get the folder name. Jan 30, 2018 at 16:36
4

A simple solution for any POSIX shell is to specify the directory to clone to explicitly (any filesystem path) and reuse it:

git clone <repository> <directory> &&
cd "$_"

and

cd -

when you're done.

2

You could do something like:

clone_check() {
  (($? != 0)) && return
  local cmd=$history[$((HISTCMD-1))]
  cmd=("${(@Q)${(z)cmd}}")
  if [[ $cmd = "git clone "* ]]; then
    local dir
    if (($#cmd == 3)); then
      dir=${cmd[3]:t}
      dir=${dir%.git}
      dir=${dir#*:}
    elif (($#cmd > 3)); then
      dir=$cmd[-1]
    else
      return
    fi
    print -r CDing into $dir > /dev/tty
    cd -- $dir
  fi
}
precmd_functions+=(clone_check)

It is quite simplistic. It's a precmd hook (executed before each prompt), that checks whether the last command line looked like git clone .../something or git clone .../something dir, guess the directory from that and cd into it.

It doesn't work if you entered git clone foo; whatever or whatever; git clone foo or git clone --option repo...

1
  • +1 Thank you very much for the code; I'm using it since a few days. I've just noticed one issue; if you close the terminal right after cloning some project, the next time you open a terminal, zsh complains with two error messages: CDing into <project> and clone_check:cd:17: no such file or directory: <project>. The error disappears as soon as the last command you ran before closing the terminal is different from $ git clone.
    – user938271
    Jun 19, 2019 at 22:02
2

Here is an adaptation of the above answer for OSX

  • mktemp requires additional parameter
  • git clone writes to STDERR by default (see this)

Function:

function git {
   local tmp=$(mktemp -t git)
   local repo_name

   if [ "$1" = clone ] ; then
     command git "$@" --progress 2>&1 | tee $tmp
     repo_name=$(awk -F\' '/Cloning into/ {print $2}' $tmp)
     rm $tmp
     printf "changing to directory %s\n" "$repo_name"
     cd "$repo_name"
   else
     command git "$@"
   fi
}
1
  • 1
    There are 7 answers that display "above" yours. But it may change over time. Such references make no sense on SE, please edit your answer and include a link to the answer you were referring to.
    – techraf
    Apr 15, 2016 at 2:40
1

There is a fundamental problem with your request that makes it almost impossible to achieve: git is a child process of the shell in this case, and child processes are unable to modify parent's environment in most operating systems. So, the only way of doing this would be from the shell's side - by implementing an alias or function.

You can find additional details here: https://stackoverflow.com/questions/2375003/how-do-i-set-the-working-directory-of-the-parent-process

0

I wouldn't recommend it, but you could use this one-liner:

echo 'project' | xargs -I % sh -c 'git clone https://github.com/username/%.git && cd %'

0

Include this in your shell:

git(){
  case "$1" in clone) git-clone "${@:2}";; *) command git "$@";; esac
}
git-clone(){ 
   local tgt 
   tgt=$(command git clone "$@" 2> >(tee /dev/stderr |head -n1 | cut -d \' -f2)) ||
      return $?
   cd "$tgt"
}

This works by surreptisiously gleaning the cloning target from git's standard error and cd'ing into the target if (and only if) the cloning was a success.

It accepts all arguments and options that the regular git clone does.

Either you can use the git wrapper and not have to worry about your completions, or you can get rid of the git wrapper and rewire your git clone completions to git-clone.

0

I've made this based on @frederik-deweerdt answer.

So you can user just clone to clone the repo link that are in clipboard, if you inform the repo, it clones than enters.

It is required to install xclip.

clone() {
if [ -z "$1" ]
then
      local repo=$(xclip -o)
else
      local repo=$1
fi
  git clone "$repo" && cd "$(basename "$repo" .git)"
}
0

Another implementation, also handles additional arguments to the git clone command.

Correctly cd's into the folder provided or the default git repo name.

Usage

gclonecd https://github.com/example/repo
gclonecd https://github.com/example/repo my-custom-folder

Bash Function

# Clones git repo and cd's into it
gclonecd() {
  git_dir="$(basename "$1" .git)"
  git_dir_resolved=${2:-$git_dir}
  git clone "$@" && cd "$git_dir_resolved";
}
0

If you want to clone with same name, same directory and cd you can use:

 git clone [clone_url] . && cd $_
1
  • Please explain what does $_ represent.
    – annahri
    Jun 13, 2021 at 0:47
0

All of the answers so far seem overly complicated. This works for SSH URIs and any other way of cloning because it simply uses the fact that the thing you just cloned is going to probably be the last modified item in your directory.

 gclone () {
    git clone $1
    local ldir=`ls -t | head -1`
    echo $ldir
    cd $ldir
    pwd
 }
2
  • Why use ls when you know that the name of the repository's directory is the name of the repository by default? You get that name from basename "$1" .git. Also, note that you need to quote all your variable expansions and probably handle the case where the git clone fails.
    – Kusalananda
    May 11, 2022 at 15:33
  • All fair points :-)
    – Snekse
    May 11, 2022 at 18:22
0

You can use zsh glob qualifiers to expand to the most recently created directory within the current folder.

git clone [email protected]:example/repo.git && cd *(/om[1])

Glob qualifiers are in brackets and can be combined. Here is a breakdown of the qualifiers.

# *(/) directory
# (om) sort by last modified (descendingly)

It is possible that another directory is created right after git creates the working tree, so I wouldn't rely on it in critical scripts. It should be fine for day-to-day terminal usage.

[1] takes the first from the array (yes, it is not 0 in zsh).

0

I don't like the aliases so I solved it like this:

Add this to your zsh nano ~/.zshrc or bash nano ~/.bashrc:

## git extra features 
# - clone + cd 
function git() {
    if [ "$1" = "clone" ]
    then 
        command git "$@" && cd "$(basename "$_" .git)"
    else
        command git "$@"
    fi
}
0

If you're using ohmyzsh, you can use the take alias for this.

take https://github.com/npm/cli.git

will clone that repo and then change to its directory.

You must log in to answer this question.

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