35

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.

  • 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 '15 at 19:12
19

Create a function:

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

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

  • 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... – Daniel K. Oct 28 '13 at 14:44
  • 3
    @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. – frostschutz Oct 28 '13 at 15:04
  • I noticed that basename git@github.com:pckujawa/pattern-rec.git results in pattern-rec.git - do you just leave off the .git extension when you clone? – Pat Jun 28 '14 at 5:03
  • 8
    @Pat basename also takes a suffix argument, so you can do: git clone $1 && cd $(basename $1 .git) – Henrik N Nov 13 '15 at 20:40
17

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
}
  • 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 :) – Daniel K. Oct 28 '13 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 '13 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. – Daniel K. Oct 29 '13 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 '16 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 '17 at 19:31
7

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

git clone git@github.com: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 git@github.com: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 git@github.com:npm/npm.git && cd $(basename $_ .git)

or

git clone git@github.com:npm/npm.git && cd ${${_%%.git*}##*/}
  • git clone foo/bar && cd !$:t – Manav Mar 21 '15 at 6:52
  • If your repo url ends in .git you can use !$:t:r to get the folder name. – Michael Stramel Jan 30 '18 at 16:36
2

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 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 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
    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 '16 at 2:40
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.