Search code examples
zshrcoh-my-zsh

zshrc alias gets executed when launching a new terminal


I am using zsh and in my config I am adding another alias :

alias recursively_git_pull_all_repo="for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done"

However this alias seems to get executed every time I open a new terminal (or at least it slows down starting up a new terminal considerably).

How can I add this alias without it being launched every time I open a new terminal ?


Solution

  • TL;DR (or should it be TL;WR - "too long, won't read"?)

    alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
    

    You are at least partially correct in that a part of this command is run when the alias is declared. This has two effects:

    1. Loading the alias takes time
    2. The alias does not work

    Explanation

    You are declaring the alias in double quotes, which allows for parameter expansion. That means that the parts $(find . -name ".git") and ${dir%/*} will be expanded and substituted at the time the alias is declared taking the values they produce at the time.

    For $(find . -name ".git") this means that . is most likely $HOME and the construct is replaced by a newline separated list of all .git directories (and other file-like objects) in your home directory.

    ${dir%/*} will probably be substituted with an empty string as dir is most likely not set. Remember: the alias itself is not executed at that time, so dir will not be set by the for loop.

    This means that:

    alias recursively_git_pull_all_repo="for dir in $(find . -name ".git"); do cd ${dir%/*}; git pull ; cd -; done"
    

    will effectively be saved as something like

    alias recursively_git_pull_all_repo="for dir in docs/repo1/.git
    docs/repo2/.git
    otherdocs/repo/.git
    whatever/.git; do
    cd ; git pull ; cd -; done"
    

    This will - fortunately - fail with an zsh: command not found: docs/repo2/.git error because of the newline after the first repository. If it did not, it would change into your home directory repeatedly (cd without parameter) and try to do git pull.

    Solution

    The quick solution would be to just use single quotes, when declaring the alias, that way the command and parameter will substituted when the alias is run, not when it is declared.

    But there are still some other problems with that:

    • if any of the directory names contain white spaces, this will fail because for will interpret it as two values
    • if you cannot switch into one of the found directories, git pull will be run from the current directory instead. This can actually happen if the execute flag is not set on a directory in the path.
    • without additional parameters find will also list non-directories named .git

    Instead I would suggest to use what zsh has to offer:

    alias recursively_git_pull_all_repo='for dir in **/.git(/:h); git -C $dir pull'
    
    • you do not actually need find to get a list of the directories. The ** glob will be recursively expanded to all directories. So **/.git will be expanded to all paths with .git as last element.
    • you can use glob qualifiers to restrict expansions to elements that fulfill certain criteria. In this case **/.git(/) the qualifier / means: "only directories".
    • you can also history expansion modifiers as glob qualifiers. This is done with a : followed by the modifier - in this case h, which removes the last path component (like the program dirname, or ${dir%/*} from your example).
    • you do not actually need do change directory into a git repository in order to do use git; you can tell git where to work with git -C <path>
    • as you are now only running one command inside the loop, you can use the short syntax for for, meaning no do ...; done.

    Fun fact: If you use d instead of dir, another short form of for and the option GLOB_STAR_SHORT is enabled (requires zsh >= 5.2), you can use:

    for d (**.git(/:h))git -C $d pull
    

    which is only 4 characters longer than your alias name. Yes, I know about completion. But there is also history searching 😉.