git alias: multiple commands, variadic parameters
Asked Answered
C

4

4

I often find myself typing this:

git push remote1 branch1 branch2 tag1 tag2 tag3..
git push remote2 branch1 branch2 tag1 tag2 tag3..

I would prefer an alias where I can type this instead:

git pushall branch1 branch2 tag1 tag2 tag3 ..

Note: I am aware I could create a new remote "all" with multiple urls. Let's not discuss this here, but focus on the alias instead!

I am ok to hardcode the remote names, because I have a number of projects with the same multiple remote names (usually "drupal" and "github").

Progress so far

I already figured out a non-variadic version:

[alias]
pushall = "!git push github $1; git push drupal $1; #"

Two tricks here were

  • using double quotes to prevent ';' from having a special meaning in .ini files
  • # to ignore the rest of the line.

But this only pushes one branch (or tag) at a time. So I would have to type this:

git pushall branch1
git pushall branch2
git pushall tag1
git pushall tag2
git pushall tag3
...

I would prefer an alias where I can type this:

git pushall branch1 branch2 tag1 tag2 tag3 ..

Why not a new remote "all" with multiple push urls?

As said, let's focus on the aliases, so that readers find what they are looking for.

Anyway, here is why I am not creating a remote "all":

  • I would have to do this once per project, and could not do it globally. In my case, hardcoding the remote names in a global alias is actually fine!
  • Afaik, I would pollute my history with refs like "all/branch1" instead of or in addition to "remote1/branch1" and "remote2/branch1".

The correct place to discuss this would be here, pull/push from multiple remote locations

See also

The following are related, but they do not address variadic parameters:

The following might be helpful, but it addresses pure shell script, not specifically git aliases:

Corroboree answered 29/12, 2016 at 3:55 Comment(4)
see stackoverflow.com/questions/3321492/…Dallman
@Dallman This link falls into the same category as the ones I already mentioned. I added it in the "See also" list for completeness.Corroboree
The relevant piece of information I learn from the multiple answers is that $@ means variadic arguments in shell script, and this also works for git aliases. I was not aware of this. Now I don't know which of those answers I should accept.. I will see.Corroboree
The overall trick is to define your alias as a shell function, which then takes as its parameters, all the arguments passed to the alias. Then you get to use shell script, which is a real (nominally Turing-complete) language. In some special cases you don't need to resort to the full-power mode (axiac's answer) but I usually go straight to functions anyway, they're not that complex.Dallman
U
2

This really is answered by the other questions which you linked to, but for clarity:

[alias]
    pushall = "!git push github \"$@\"; git push drupal \"$@\"; :"

Or setting from the command line:

git config --global alias.pushall '!git push github "$@"; git push drupal "$@"; :'
Unalloyed answered 29/12, 2016 at 7:36 Comment(3)
I did not see the "$@" mentioned in the other answers. Or at least not explicitly. I was not aware of this construct. For the record, I just found that this will also insert options like -l or --version (for ls). But the --help option is passed to the git command instead, it appears.Corroboree
In a little test run this worked without the double quotes around $@. What could possibly go wrong if I omit those?Corroboree
In this case, probably not much because you cannot have branch names with spaces or other characters that might cause changes to the parameter list during word splitting.Unalloyed
W
2

The idiom to package an arbitrary script into a git alias is to put it inside a shell function:

pushall = "! f() { git push github \"$@\"; git push drupal \"$@\"; }; f"

I want to point out that the correct use of $@ is to place it inside double-quotes: "$@".

Whereby answered 29/12, 2016 at 8:30 Comment(0)
S
1

By extending your initial attempt:

[alias]
pushall = "!git push github $@; git push drupal"

This way, git pushall branch1 branch2 branch3 expands to:

git push github branch1 branch2 branch3; git push drupal branch1 branch2 branch3
#                  |               |                        |               |
#                  +-------+-------+                        +-------+-------+
# these arguments were     |                                        |
# expanded from $@ --------+                                        |
#                                                                   |
#                  these are the arguments of the original command -+

$@ expands to all command line arguments.
There is no need for # at the end of line; the fragment git pushall is replaced by the value of the alias, the rest of the argument

If you have a bigger list of remote repositories you can write it this way:

[alias]
pushall = "!for repo in github drupal bitbucket; do git push $repo $@; done #"
#                         |               |
#                         +-------+-------+
# put all your repos here         |
# separated by spaces ------------+

This time the # sign is required. It turns the original arguments into a comment; otherwise the command has syntax errors and it doesn't run.

If you want to push to all the remotes of the repository then you can write a smarter alias:

pushall = "! for repo in $(git remote); do git push $repo $@; done #"

It runs git remote to find all the remotes and uses command substitution to replace $(...) with the output of the git remote command before continuing.

You can define it as a global alias using:

$ git config alias.pusha '! for repo in $(git remote); do git push $repo $@; done #'

If you have some repos where you don't want to push to all remotes, you can define it as a local alias and customize the list of remotes in each repo using thi command while you are in the repository:

$ git config --local alias.pusha '! for repo in github drupal; do git push $repo $@; done #'
Semination answered 29/12, 2016 at 7:53 Comment(2)
I suppose omitting the $@; # at the end saves a few characters. But it also makes the entire thing less "symmetric". So maybe I prefer the slightly longer version.Corroboree
I prefer the for version; it is more versatile. You can squeeze more commands (terminated by ;) between do and done. For example, after push you can invoke a program (like notify-send on Ubuntu) that displays a desktop notification.Semination
C
0

Just for the record, the following "cheat" does work. It is not really a complete answer but it might be good enough for many.

[alias]
pushall = "!git push github $1 $2 $3 $4 $5; git push drupal $1 $2 $3 $4 $5; :"

Yes this is not truly variadic, because it is limited to 5 parameters (or whichever number you choose when creating the alias). Also it cannot pass options like --key=value. But as said it might be just good enough for you.

In my specific use case, most of the time I just push one branch and one release tag, so two parameters ($1 $2) would be enough.

Note that : at the end seems to have the same effect as #. I learned this somewhere else here on stackoverflow.

I will not "accept" this answer, because I want to leave the opportunity for someone to come up with something better.

Corroboree answered 29/12, 2016 at 4:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.