While working on a CI workflow last week I learnt about something I hadn’t known before: Push options. Using the flag
--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
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"