1

In Git, I want to add some global aliases for convenience.

The command

git config --global alias.unstage 'reset HEAD --'

as found in the documentation, works fine. So I tried using that same syntax, with a ! prepended to the command, to run external, more complex scripts:

# release-major
latest=$(git describe --abbrev=0 --tags 2>/dev/null); latest=${latest:-v0.0.0}; components=(${latest//./ }); major=${components[0]}; major=${major//v/}; minor=${components[1]}; patch=${components[2]}; major=$((major+1)); minor=0; patch=0; next='v'$major'.'$minor'.'$patch; git tag -a $next -m ""

# release-minor
latest=$(git describe --abbrev=0 --tags 2>/dev/null); latest=${latest:-v0.0.0}; components=(${latest//./ }); major=${components[0]}; major=${major//v/}; minor=${components[1]}; patch=${components[2]}; minor=$((minor+1)); patch=0; next='v'$major'.'$minor'.'$patch; git tag -a $next -m ""

# release-patch
latest=$(git describe --abbrev=0 --tags 2>/dev/null); latest=${latest:-v0.0.0}; components=(${latest//./ }); major=${components[0]}; major=${major//v/}; minor=${components[1]}; patch=${components[2]}; patch=$((patch+1)); next='v'$major'.'$minor'.'$patch; git tag -a $next -m ""

If I try to add these three scripts with the syntax

git config --global alias.my-alias-name '!my-alias-code'

however, it doesn't work. The result is always a parser error.

I tried every variation of quoting, unquoting, single quotes and double quotes that I could imagine. I even tried adding these to the .gitconfig file directly. None of that works.

What am I missing? How can these scripts be fixed so that I can add them as aliases?

Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
caw
  • 30,999
  • 61
  • 181
  • 291
  • bash might be to blame. ! has a very special meaning for bash. But I'm not sure show git with aliases interact with bash... just a line of thought, say. – eftshift0 Jul 03 '17 at 16:51
  • Add '#' at the beginning of your bash file. Should work since '!' are considered under /sh/ by default. – anshul Gupta Jul 03 '17 at 17:01
  • @Edmundo The `!` at the start of the command should be [Git-specific syntax](https://git-scm.com/book/tr/v2/Git-Basics-Git-Aliases) and is to express that the following command is an external command instead of a Git subcommand. So if I got this right, the shell shouldn't even see that exclamation mark when executing the code later. – caw Jul 03 '17 at 17:06
  • @anshulGupta There is no separate *file* for these scripts. I'm trying to add these as Git aliases in a (long) one-liner executed in Bash, as shown in the question. – caw Jul 03 '17 at 17:07
  • I C. haven't used aliases myself so thought that bash might be the culprit – eftshift0 Jul 03 '17 at 17:07
  • @Edmundo No problem. Thanks, nevertheless! – caw Jul 03 '17 at 17:14

2 Answers2

4

You can de-bash-ize your one-line scripts. Let's look at this one for instance:

latest=$(git describe --abbrev=0 --tags 2>/dev/null)
latest=${latest:-v0.0.0}
components=(${latest//./ })

This is your first bash-ism: you are making an array out of the value with dots replaced with spaces (which then break up). Plain shell has no arrays; instead, we use the positional parameters, breaking them up with $IFS, using set. To keep it clean we would need this in a function (each function has its own private positionals) but if all positional parameters are already captured into local variables we can just override them:

set -- $(echo $latest | sed "s/\./ /g")

(back to your code)

major=${components[0]}
major=${major//v/}
minor=${components[1]}
patch=${components[2]}

Now we'll use $1, $2, and $3 here. We might as well move the v substitution into the sed above (POSIX sh can do this substitution, but I say let's just let sed do it as it's more obvious and/or more efficient):

set -- $(echo $latest | sed -e s/v// -e "s/\./ /g")
major=$1
minor=$2
patch=$3

Back to your original code:

major=$((major+1))
minor=0
patch=0
next='v'$major'.'$minor'.'$patch
git tag -a $next -m ""

This is all valid POSIX sh, so we are done with this conversion.

torek
  • 448,244
  • 59
  • 642
  • 775
1

You are using bash-specific syntax in your script (e.g. $(( )) for arithmetic). However, git aliases beginning with ! are executed by /bin/sh by default. Since /bin/sh is just a plain POSIX shell, it doesn't have any of those features in its syntax.

Luckily this is easy to fix. Simply add #!/bin/bash to the beginning of your script. (If it's a separate file.)

If you are using these inline then you'd probably have to call bash directly. This could be a quoting headache, but your examples don't look like they would be a big problem. I think this might do it:

# release-major
bash -c "latest=$(git describe --abbrev=0 --tags 2>/dev/null); latest=${latest:-v0.0.0}; components=(${latest//./ }); major=${components[0]}; major=${major//v/}; minor=${components[1]}; patch=${components[2]}; major=$((major+1)); minor=0; patch=0; next='v'$major'.'$minor'.'$patch; git tag -a $next -m ''"

# release-minor
bash -c "latest=$(git describe --abbrev=0 --tags 2>/dev/null); latest=${latest:-v0.0.0}; components=(${latest//./ }); major=${components[0]}; major=${major//v/}; minor=${components[1]}; patch=${components[2]}; minor=$((minor+1)); patch=0; next='v'$major'.'$minor'.'$patch; git tag -a $next -m ''"

# release-patch
bash -c "latest=$(git describe --abbrev=0 --tags 2>/dev/null); latest=${latest:-v0.0.0}; components=(${latest//./ }); major=${components[0]}; major=${major//v/}; minor=${components[1]}; patch=${components[2]}; patch=$((patch+1)); next='v'$major'.'$minor'.'$patch; git tag -a $next -m ''"
Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
  • Thanks! As shown in the question, I'm trying to use the command `git config --global alias.my-alias-name '!...'`, where `...` is one of the three long single-line scripts, in order to add the scripts as aliases. Adding these as aliases works, but when I execute one of those aliases then, e.g. `git release-major`, I get the following warning: `Syntax error: "(" unexpected`. Since there are no separate files involved and these are one-liners, I don't see how I could add `#!/bin/bash` here. – caw Jul 03 '17 at 17:13
  • You could wrap the whole thing in `bash -c "..."` ... but that could be a quoting nightmare. – Dan Lowe Jul 03 '17 at 17:14
  • @caw Actually your quoting doesn't seem to be much of a problem - see my updated answer, it might do what you want. – Dan Lowe Jul 03 '17 at 17:28