I recently investigated what terminal commands I used the most. Turns out “git push” is a common one. With that in mind it could make sense to investigate and solve some pain points I have with “git push”.
Pushing Tags
I commonly have to do both “git push” and “git push –tags” after a release. Ideally I would want it to push tags always at the same time.
My first attempt at a solution was using this git alias:
1 |
git config --global alias.p '!git push && git push --tags' |
While that works it takes twice the time because you are actually contacting the remote server twice. Then I found a better answer on Stack Overflow: https://stackoverflow.com/questions/3745135/push-git-commits-tags-simultaneously
1 |
git config --global push.followTags true |
It should be noted that this only pushes annotated tags and not lightweight ones. Luckily we use annotated tags where I work so that’s a non issue for me.
Making annotated tags takes some more writing. So let’s automate the tagging process. I use Maven so I can extract the version number and make the tag automatically by looking at the pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function __gitutils_xpath { local query="${1}" local file="${2}" xmllint --format "${file}" 2> /dev/null | sed '2 s/xmlns=".*"//g' | xmllint --xpath "${query}" - 2> /dev/null } function __gitutils_xpath_pom { local query="${1}" __gitutils_xpath "${query}" "pom.xml" } function __gitutils_xpath_pom_project_version { __gitutils_xpath_pom "/project/version/text()" } function tag { local pomversion="$(__gitutils_xpath_pom_project_version)" if [[ $? -ne 0 ]]; then return 1 # False fi if [[ ! "${pomversion}" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then echo "Invalid Version: ${pomversion}" return 1 # False fi local tagname="v${pomversion}" git tag -a "${tagname}" -m "${tagname}" } |
The act of tagging a new release version now goes something like this:
1 2 3 4 |
git checkout master git merge develop tag push |
Sweet!
Setting the Upstream
Do you also wish that setting the upstream was automatic?
The following Stack Overflow question provides a partial solution: https://stackoverflow.com/questions/6089294/why-do-i-need-to-do-set-upstream-all-the-time
We can configure Git to push to the current branch by default like this:
1 |
git config --global push.default current |
You can now do “git push” but when you do “git pull” it will complain there is no upstream set. Now in theory an alias like this solves this by setting the upstream all the time:
1 |
git config --global alias.p "push -u" |
That will however yield a spammy message about the upstream getting set every time you push. To remedy this we can make a more complex alias for push that sets the upstream only when it is not already set.
Step one is to create a script and set it as the push alias:
1 2 3 4 |
cd touch git-push-alias.sh chmod +x git-push-alias.sh git config --global alias.p '!~/git-push-alias.sh' |
Then we add the following code to our new file ~/git-push-alias.sh:
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/usr/bin/env bash upstream="$(git upstream)" if [[ $? -ne 0 ]]; then exit 1 fi if [[ -z "${upstream}" ]]; then git push -u "$@" else git push "$@" fi |
The upstream will now automatically be set, but only when there is none to avoid log spam.
Sweet!
Scripts for complex aliases
Why use an executable script file for this git push alias? There are alternatives, but they are harder to work with in my opinion.
One alternative approach is the one described here: https://stackoverflow.com/questions/3321492/git-alias-with-positional-parameters
The idea is to create a function that you immediately run:
1 2 |
[alias] files = "!f() { git diff --name-status \"$1^\" \"$1\"; }; f" |
- As you can imagine the quotes can quickly get out of hand.
- The readability from sane indentation disappears as you try to smash everything onto one line.
- You won’t get any syntax highlighting
So I propose using executable script files like previously mentioned for those reasons.
We can however do better than placing them directly in our home directory. Let’s create a dedicated directory for these scripts and add it to the path in .bashrc instead. It could go something like this:
1 2 |
cd mkdir scripts |
Then in your .bashrc add:
1 |
export PATH="${HOME}/scripts:${PATH}" |
We could now create the previous script like this instead:
1 2 3 4 5 |
cd cd scripts touch git-push-alias.sh chmod +x git-push-alias.sh git config --global alias.p '!git-push-alias.sh' |
This is just an example of course. If you have an existing dotfiles strategy in place, these scripts probably resides in there together with you git configuration.
Leave A Comment