2

I just wrote the following script: that pushes my current branch to its remote counter part.

#!/usr/bin/env bash

# Usage: pushes current checkout branch to its remote counterpart.

current_branch=$(git symbolic-ref HEAD 2>/dev/null) ||
current_branch="(unnamed branch)"

git push origin ${current_branch}

I got the git symbolic-ref ... thing after googling how to accomplish what I m trying to do. It seems to do what I want it to do, but is there another way to accomplish this?

Also I don't understand what this part means/does 2>/dev/null

EDIT: after reading a bit the bashdocs I guess it is some king of redirection?

4
  • 2
    I believe there’s a code review Stack Exchange
    – Jeff Schaller
    Sep 11, 2018 at 15:00
  • Yep, there's Code Review.
    – muru
    Sep 12, 2018 at 0:49
  • thanks @muru I didn't know that existed.
    – intercoder
    Sep 12, 2018 at 12:48
  • 2> means that you are redirecting (i.e. >) the stderr (i.e. 2) into the black hole (i.e. /dev/null).
    – Pietru
    Jul 31, 2021 at 15:12

2 Answers 2

4

Ok, here goes code review.

# Usage: pushes current checkout branch to its remote counterpart.

Skip the first word. This is a comment describing what the script does, not how to use it.

current_branch=$(git symbolic-ref HEAD 2>/dev/null) ||
current_branch="(unnamed branch)"

You are running git symbolic-ref HEAD, discarding its stderr and assigning the stdout to a variable. In case that fails, you take the branch name (unnamed branch).

git push origin ${current_branch}

In the end you are pushing your local branch to origin.


Here some explanations:

The 2>/dev/null part

Every program running on a Linux/Unix terminal has an input channel called stdin which usually collects your input from keyboard. It also has two output channels called stdout (standard output for printing normal operation information) and stderr (standard error for printing warnings and runtime errors). These channels are having their origin in hardware, but are now a pure software standard every Terminal and every Shell supports. Bash's way of handling the stdout and stderr channels is through pointers represented by the numbers 1 and 2. What the 2> part is doing is telling the program to redirect its stderr to a different target. /dev/null is a virtual device that basically acts as a black hole. Anything sent in there is just lost. Thus the 2>/dev/null means quite literally "do not print any non-standard output".

The || part

The double pipe in bash means: If the program on the left side failed, i.e. returned with an exit code that is not 0, run the right side of the double pipe. Your "right" side continues on the next line, where you assign the same variable a fallback value.


Now back to the review.

You want to push your newly created branch to a remote branch at origin and you want it to have the same name as the local branch.

Your code does not do that. Well, not reliably.

HEAD in git is a special reference that points at the newest commit in your currently checked out branch if there is any. The last part is where your code is dangerous. HEAD is not necessarily always pointing to a branch. It can also be in a so-called detached HEAD state. In that case your command will fail with the error fatal: ref HEAD is not a symbolic ref. This error is discarded by your script and it would instead continue with the branch name (unnamed branch). The last line would then attempt to push your local branch as (unnamed branch) which you probably do not want.

Even if the command succeeds, you will get output like refs/heads/yourbranchname. You can use the option --short to get only the last part:

git symbolic-ref --short HEAD

The result will be:

yourbranchname

What if we don't have a branch? Simple. You do not want to push anything! You should rather exit the script if the branch name could not be determined. You can do it the following ways. Either add an explicit exit $errorcode or use bash's set -e flag:

current_branch=$(git symbolic-ref HEAD) || exit $?

Here $? contains the exit code of the last command, so the failed git call. You don't need to hide the stderr. It's helpful to determine what went wrong.

Alternatively:

set -e
current_branch=$(git symbolic-ref HEAD)

The set -e enables a special mode of bash where it exits the script on every error. I would highly encourage you to use it in every single script.

Next part:

git push origin ${current_branch}

There is nothing wrong with this command, but I think you are making your own life a little too hard. This verbose command is needed only once to create a link between the local branch and the remote. You can save that link using the -u option like this:

git push -u origin ${current_branch}

After you have run this command once, you can just type git push and it will automatically push to the correct remote branch. Keep in mind that when you check out or clone remote branches, this link is automatically established. This is only required for branches that were created locally.

Let's summarise

You might want to rewrite your script like this:

#!/usr/bin/env bash

# Pushes current checkout branch to its remote counterpart.

set -e

current_branch=$(git symbolic-ref --short HEAD)
git push -u origin "${current_branch}"

After running it once for a new branch, just use:

git push
4
  • If you don't want to use push.default, you could also just do what I do for new branches, which is: (1) run git push; (2) copy the suggested command from the error message and run it (to set the upstream); (3) continue using git push.
    – Wildcard
    Sep 12, 2018 at 1:33
  • @Hubert Grzeskowiak thanks a lot man you pretty much demystified a bit more the creation/reading of scripts for me. Thanks :)
    – intercoder
    Sep 12, 2018 at 12:45
  • Double quote $current_branch to avoid word splitting and filename globbing. Using ${variablename} does nothing that is not the same as $variablename (in this code).
    – Kusalananda
    Sep 15, 2018 at 16:01
  • @Kusalananda Quotes added. Good point. The curly brackets are a convention my company is using. They don't hurt much, but may catch an error once in a while
    – Hugo G
    Sep 17, 2018 at 1:59
2

Look at the push.default setting in git help config.

I'll quote part of it:

   push.default
       Defines the action git push should take if no refspec is
       explicitly given. Different values are well-suited for specific
       workflows; for instance, in a purely central workflow (i.e. the
       fetch source is equal to the push destination), upstream is
       probably what you want. Possible values are:

       ...

       o   current - push the current branch to update a branch with
           the same name on the receiving end. Works in both central
           and non-central workflows.

So in short, run git config --global push.default current once on your machine and then just use git push from now on.


The moral of the story is one of my two cardinal rules of script writing:

Always check the docs before you invent a new wheel. More often than not (for widely used tools), there is already an option to do exactly what you need without writing your own "shim" scripts.

(Violations of this rule, where a long complex "wrapper" script was written for a tool in ignorance of a particular command line flag that will do the same thing, are very common and very painful to behold.)

2
  • 1
    Excellent point about reinventing the wheel, although I will say figuring out how to do it and implementing it yourself is a great way to learn how to do all sorts of things. And having done that, I can also say that yes, using the distro/package/program provided feature/function is quite often not only much easier but works better. Of course, there are those few times... but they aren't that often.
    – ivanivan
    Sep 12, 2018 at 2:41
  • @Wildcard I understand and agree with your point, but I'm just using it as a ways of to learn to write scripts. I usually pick up something that I normally use all the time and then try to implement it. Maybe not the best way to learn, but new to this. Now I'm gonna try to write a script about opening apps from the command line, something that I always do :)
    – intercoder
    Sep 12, 2018 at 13:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .