Positional arguments in git aliases
I wanted to make a git add-and-commit
alias, but getting the positional arguments to handle filenames with spaces in was a lot more complicated than I’d expected.
My naive first try was:
[alias]
add-and-commit = "!git add $@ && git commit"
which works as long as you don’t have filenames that you don’t need to quote, and that you’re happy using git’s default method for capturing your commit message. You can call it like:
git add-and-commit readme.md example.txt
and it will add the two files readme.md
and example.txt
and then ask for a commit message from you.
But if one of your files is e.g. called file with a space.txt
it barfs:
git add-and-commit readme.md "file with a space.txt"
fatal: pathspec 'file' did not match any files
Also, what if you want to pass your commit message at the same time? Now we need to consider positional parameters, how we ensure they’re escaped properly and how we can grab some of them for the git add
portion of the alias, and the others for the git commit
portion. The aim is so that we can call:
git add-and-commit readme.md "file with a space.txt" -m "A nice long commit message"
Capture the filenames
Breaking this down, we want to separately capture all the filename arguments (in this case, readme.md
and file with space.txt
) before the last two, which are the -m
and the commit message itself.
bash parameter expansion is a our friend1 here. This Ask Ubuntu answer has some nice examples that helped me out.
We want args $1
to the total number of args ($#
) minus 2, for the filenames (-2, because of the -m
and then the message itself).
We need each argument quoted in case it has spaces in, so we use the quoted "{$@}"
format instead of "{$*}"
and we use bash arithetic to subtract 2 from the argument length $(($#-2))
.
So to capture all the filenames, correctly quoted:
# arguments, quoted, starting from 1 and going to -2 the total number of arguments
"${@:1:$(($#-2))}"
Capture the commit message arguments
And then we want to capture the last two arguments to pass to git commit
. The space before the -2
is important, and don’t forget to use the quoted "${@}"
style, because our commit message is bound to have spaces in too.
# a negative value needs either a preceding space or parentheses
"${@: -2}"
Wrapping this all up, I ended up with this, which is slightly more complicated by the need to escape the quoting of the arguments to the git alias. First we git add
all the filenames, then we git commit
using our -m
message.
[alias]
add-and-commit = "!git add \"${@:1:$(($#-2))}\" && git commit \"${@: -2}\" #"
-
LOL, no ↩
All links, in order of mention:
- positional parameters: https://www.gnu.org/software/bash/manual/html_node/Positional-Parameters.html
- bash parameter expansion: https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
- 1: #fn:1
- Ask Ubuntu: https://askubuntu.com/a/1011660/233579
- ↩: #fnref:1
Recent posts:
- Patch for aarch64 (aka arm64) openssl 1.0.2 'relocation R_AARCH64_PREL64 against symbol OPENSSL_armcap_P error'
- TIL: the `NO_COLOR` informal standard to suppress ANSI colour escape codes
- Copy the contents of a branch into an existing git branch without merging
- Adding search to a static Jekyll site using pagefind
- asdf, python and automatically enabling virtual envs