While working on a CI workflow last week I learnt about something I hadn’t known before: Push options. Using the flag -o=<string>
or --push-option=<string>
you can pass an additional string to the pre-receive/post-receive on the repository receiving the push. Why would you want to do that? Well, GitLab for instance allows you, for instance, inject additional environment variables into a CI pipeline or skip the pipeline altogether:
# Do not trigger CI
$ git push --push-option=ci.skip
# Inject another environment variable into CI
$ git push --push-option=ci.variable="name=value"
If you want to provide more than a single option then just use the flag multiple times.
How does that work under the hood? To find out I’ve created a little bare repository (repo.git
) with just a single commit in it to which I then added the following pre-receive hook script:
#!/bin/bash
echo "BEGIN ENV"
env | grep GIT_PUSH
echo "END ENV"
exit 1
If I then run git push origin master --push-option hello_world
on it, the receive hook will print the following output before rejecting the push:
remote: BEGIN ENV
remote: GIT_PUSH_OPTION_0=hello_world
remote: GIT_PUSH_OPTION_COUNT=1
remote: END ENV
As you can see from that you can access the push options from $GIT_PUSH_OPTION_n
environment variables with n
being a zero-indexed counter with the maximum value being $GIT_PUSH_OPTION_COUNT - 1
. That’s pretty much it. The rest is completely up to the hook script.
fatal: the receiving end does not support push options
Initially I got the error shown above which means that the repository I wanted to push to didn’t advertise its support for push options. That was easily fixed with the following commands:
$ cd repo.git
$ git config receive.advertisePushOptions true
Playground setup
In case you want to play around with that feature, I’ve prepared a little setup script that automates everything I’ve written above:
#!/bin/bash
set -e
# Clean slate
rm -rf repo
rm -rf repo.git
rm -rf clone
# Create simple repository
mkdir repo
cd repo
git init
touch README.md
git add README.md
git commit --no-gpg-sign -m "Initial commit"
cd ..
# Make the repo a bare one so that we can directly push to it:
git clone repo repo.git --bare
rm -rf repo
# Let's create a simple pre-receive hook
cp pre-receive.sh repo.git/hooks/pre-receive
chmod +x repo.git/hooks/pre-receive
cd repo.git
git config receive.advertisePushOptions true
cd -
# Now let's clone that repository
git clone repo clone
cd clone
touch otherfile
git add otherfile
git commit --no-gpg-sign -m "otherfile added"
git push origin master -o "hello_world"
Do you want to give me feedback about this article in private? Please send it to comments@zerokspot.com.
Alternatively, this website also supports Webmentions. If you write a post on a blog that supports this technique, I should get notified about your link 🙂