Git alias could run in dash, not bash — even if `/bin/bash` exists

I Wrote a git alias using [[ ... ]] and =~. Works fine in my interactive bash. Run the alias and it explodes:

$ git co master
... Syntax error: "(" unexpected (expecting "then")

First instinct: "but I have bash installed":

$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1298416 ... /bin/bash
$ /bin/bash --version
GNU bash, version 5.2.37(1)-release ...

Doesn't matter. A git alias starting with ! is hardcoded to run under /bin/sh — it doesn't read $SHELL, doesn't care what your login shell is. On Debian/Ubuntu /bin/sh -> dash, and dash doesn't understand [[, =~, == or other bash extensions:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 ... /bin/sh -> dash
$ echo '[[ "a" == a* ]]' | /bin/sh
/bin/sh: 1: [[: not found

The source is run-command.c::prepare_shell_cmd() in git itself — it literally calls sh -c.

Two fixes:

Wrap in bash -c explicitly (minimal change, but the nested quoting inside an alias gets ugly fast):

co = "!bash -c 'f() { ...bash syntax... }; f \"$@\"' _"

Rewrite as POSIX sh (preferred). Common substitutions:

  • [[ "$x" == -* ]]case "$x" in -*) ... ;; esac
  • [[ "$x" =~ ^HEAD~ ]]case "$x" in HEAD~*) ... ;; esac
  • [[ "$a" == "$b" ]][ "$a" = "$b" ]
  • [[ -f foo ]][ -f foo ] (already POSIX, no reason to use [[)

Lesson: write git aliases as if dash is the only shell on the planet. Don't reach for [[ because it feels nicer — either wrap with bash -c or just use case.

Comments

  1. Markdown is allowed. HTML tags allowed: <strong>, <em>, <blockquote>, <code>, <pre>, <a>.