264

I am trying to create an alias that uses both multiple Git commands and positional parameters. There are Stackoverflow pages for each, and it would appear painfully obvious to do both, but I am having trouble.

As an example, I want to switch to branch foo and perform a status. So in my .gitconfig, I have:

  [alias] 
     chs = !sh -c 'git checkout $0 && git status'

which doesn't work. Whereas something like this will work.

chs = !sh -c 'git checkout $0'

echoes = !sh -c 'echo hi && echo bye'

Any insight would be appreciated.

2
  • My alias: git config --global alias.go '!git commit -a && git pull --rebase && git push && git status'. Note: Use simple quotes. Commented Aug 19, 2016 at 18:55
  • also, you always have the option to make an alias in your "bash" config. Commented Mar 1, 2023 at 17:06

13 Answers 13

212

This will work (tested with zsh and bash):

[alias] chs = !git checkout $1 && git status
Sign up to request clarification or add additional context in comments.

10 Comments

Interesting, git actually does fill in the positional variables now in shell aliases. But it's still broken, because it also tacks them on as arguments. An alias of echo $1 && echo done, when invoked with the argument 'foo', will output both "foo" and "done foo".
Wow... Unfortunately, this was all a version problem. I was using Git 1.7.3, and neither of these methods worked. I updated to 1.7.6 and voila, everything worked. Thanks guys!
What is the preceding exclamation point for on the first invocation of git?
@ElijahLynn: In a git alias, a leading ! means pass the whole thing to the shell. Otherwise, it assumes you're trying to run another git command and passes it as arguments to the git binary.
@Brondahl Clever. I'd recommend ;: instead of && : though. And that works just fine on unix as well.
|
144

This targets Windows batch / msysgit bash; might not work on other environments.

As Olivier Verdier and Lily Ballard have said

[alias] chs = !git checkout $1 && git status

almost works, but gives a spurious extra insertion of the argument ...

git chs demo -> git checkout demo && git status demo

But if you add && : to the end of your alias, then the spurious argument is consumed into a location tag.

So

[alias] chs = !git checkout $1 && git status && :

gives the correct output ... git chs demo -> git checkout demo && git status

9 Comments

The && : is gold and makes this solution work for commands where the extra argument would be problematic.
@Clay (or anyone else) - Could someone explain to the BASH-challenged what && : does?
@JustinMorgan && means if the previous command turns 0 (success), then run the command after &&. ':', the colon, is a shell builtin command, which does nothing beyond expanding arguments and performing redirections and return the status 0. Here're some usages: 1. a=123;$a errors, but a=123; : $a does not. 2. : > hello.txt empties hello.txt. 3. if [ "$a" = "hello" ];then : ;fi runs okay but errors without ':'. It's like pass in python. 4. : this is a comment, the colon followed by space works as # in a comment line.
this is such a hack... git should accept multiple command mode
@kchoi So true :) And yet it appears to be a well-valued hack by the community :D
|
98

You can define a shell function.

[alias] chs = "!f(){ git checkout \"$1\" && git status; };f"

10 Comments

I saw this on another stackoverflow page, but my cygwin terminal says that the function is not recognized when I try to run it.
@Stella: I left a closing quote in there that's not useful this config file syntax. Make sure you didn't have it.
Wow... Unfortunately, this was all a version problem. I was using Git 1.7.3, and neither of these methods worked. I updated to 1.7.6 and voila, everything worked. Thanks guys!
if using Windows I think you have to surround the shell function definition with double-quotes "
Olivier's answer didn't work for me using parameters (OSX). This worked perfectly.
|
43

I was able to create multi-line and quite complex git aliases. They work fine on Windows but I assume they'd work elsewhere too, for example:

safereset = "!f() { \
                trap 'echo ERROR: Operation failed; return' ERR; \
                echo Making sure there are no changes...; \
                last_status=$(git status --porcelain);\
                if [[ $last_status != \"\" ]]; then\
                    echo There are dirty files:;\
                    echo \"$last_status\";\
                    echo;\
                    echo -n \"Enter Y if you would like to DISCARD these changes or W to commit them as WIP: \";\
                    read dirty_operation;\
                    if [ \"$dirty_operation\" == \"Y\" ]; then \
                        echo Resetting...;\
                        git reset --hard;\
                    elif [ \"$dirty_operation\" == \"W\" ]; then\
                        echo Comitting WIP...;\
                        git commit -a --message='WIP' > /dev/null && echo WIP Comitted;\
                    else\
                        echo Operation cancelled;\
                        exit 1;\
                    fi;\
                fi;\
            }; \
            f"

I wrote a post and have a few more examples here.

5 Comments

one imrovement to this would be to add !f() { : reset to get completions from reset command github.com/git/git/blob/master/contrib/completion/…
Great job! What licence is that article published under? Would you mind if I translate parts of it for StackOverflow in Russian?
@NickVolynkin Sorry about late reply. Thank you and of course, go ahead :)
@NickVolynkin, did you translate it eventually?
For something like this, it may be easier to maintain as a shell script. Then, your alias would be something like safereset = !bash ~/safereset.sh where bash is whatever shell you want, or just safereset = !~/safereset.sh if the script is executable and you want to run it in your user's login shell. Using an absolute path is probably a good idea, though ~ appears to be handled just fine.
29
[alias]
chs = !git branch && git status

4 Comments

What is the ! for?
I couldn't find doc for the ! but as far as I can see git by default will assume that the alias is a git command so t = status will work. However if you try t = git status it won't work (will say "git" command not found). So that where the ! comes in, it tells it to run the command as if it was a shell, and normally you need this when you have multiple git commands per alias, so you can have t = !git status && git log for example and it will work.
Here's a StackOverflow question that deals with exclamation points (!) in Git aliases: stackoverflow.com/questions/21083933/….
If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. git-scm.com/docs/git-config
16
[alias]
      chs = !git checkout && git status
      ac = !git add . && git commit -m 

what is ! ?

If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. What does the exclamation mark mean in git config alias?

To call alias from .gitconfig file

git chs
git ac "write_your_commit_message" 

alias is more useful for add and commit in git you can do more fast Gif show more datails

Comments

7

Try this one:

[alias]
    chs = "!sh -c 'git checkout \"$0\" && git status'"

Call it like this: git chs master

Comments

7

It's possible to have multiline git alias by appending \ at the end of each line.

[alias] 
   chs = "!git checkout $1 \ 
          ; git status     \
         "

1 Comment

Thanks! I actually had no problem using "!git fetch --prune --all; git pull --rebase upstream master; git push origin master" for my alias.
5

The problem here is that the positional parameters seem to be getting sent to the shell command twice (as of git 1.9.2). To see what I mean, try this:

[alias]
  test = !git echo $*

Then, do git test this is my testing string. You should observe the following output (last two lines edited here for clarity):

03:41:24 (release) ~/Projects/iOS$ git test this is my testing string
this is my testing string this is my testing string
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
          #1                         #2

One way to work around this would be to

[alias]
  chs = !git checkout $1 && git status && git echo x >/dev/null

This will consume the extra positional parameter as it gets applied to that last echo command and have no effect on the results.

4 Comments

Your example will expand to: git echo this is my testing string this is my testing string Adding a # to the end will fix it (don't forget the double quotes around the command alias).
in .gitconfig: a0 = "!echo $*" and a1 = "!echo $* #" . To test them: git a0 hallo daar ; git a1 hallo daar
@imme What does the # at the end do? It worked for me but I'd like to understand it.
See this "demo" stackoverflow.com/a/63890372/165330 (it seems that git adds the arguments after replacing the alias with the defined alias, using $* in the alias already puts the arguments in there, so the rest should be ignored, which happens with the # at the end, making the rest of the command a comment.
4

An example for people who want to try out what different aliases do.

Putting this in the alias-section of GIT's configuration-file (e.g. ~/.gitconfig) :

[alias]
    a0 = "!echo $*"
    a1 = "!echo $* #"
    a2 = "!f () { echo \"$*\"; }; f "
    a3 = "!f () { echo \"$*\"; }; f #"
    a4 = "!f () { echo \"$*\"; }; f \"$*\" #"
    a5 = "!f () { echo \"$*\"; }; f \"$@\" #"
    a6 = "!f () { echo \"$*\"; }; f \"$1\" #"

And then executing this command:

cat ~/.gitconfig | grep --extended-regexp -- '(a[0-9])|(alias)' ; \
echo "" ; \
export CMD ; \
for I in {0..6} ; \
do \
    CMD="a""${I}" ; \
    echo -n "Executing alias.${CMD} = " ; \
    git config --global alias."${CMD}" ; \
    git $CMD 'hoi daar' en nu ; \
    git $CMD hoi daar en nu ; \
    echo "" ; \
done ; \
unset CMD ;

Should give this as output:

[alias]
    a0 = "!echo $*"
    a1 = "!echo $* #"
    a2 = "!f () { echo \"$*\"; }; f "
    a3 = "!f () { echo \"$*\"; }; f #"
    a4 = "!f () { echo \"$*\"; }; f \"$*\" #"
    a5 = "!f () { echo \"$*\"; }; f \"$@\" #"
    a6 = "!f () { echo \"$*\"; }; f \"$1\" #"

Executing alias.a0 = !echo $*
hoi daar en nu hoi daar en nu
hoi daar en nu hoi daar en nu

Executing alias.a1 = !echo $* #
hoi daar en nu
hoi daar en nu

Executing alias.a2 = !f () { echo "$*"; }; f 
hoi daar en nu
hoi daar en nu

Executing alias.a3 = !f () { echo "$*"; }; f #



Executing alias.a4 = !f () { echo "$*"; }; f "$*" #
hoi daar en nu
hoi daar en nu

Executing alias.a5 = !f () { echo "$*"; }; f "$@" #
hoi daar en nu
hoi daar en nu

Executing alias.a6 = !f () { echo "$*"; }; f "$1" #
hoi daar
hoi

Comments

1

This is an old post, but no one has provided what I think is the simplest solution. This works in my *nix-based CLI, your mileage may vary.

[alias]
    chs = "!git checkout $1; git status"

A simple semi-colon (;) is all you need to run multiple git commands.

Comments

0
cmu = "!f() { git add . && git commit -m $1 && git push -u origin $2; }; f"

1 Comment

Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?
-1

As far as Lily Ballard have a good answer with a shell function, it looks better when we pipe all the alias arguments:

[alias] chs = "!f(){ git checkout \"${@:1}\" && git status; };f"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.