10

I want to use a bash function in a git alias. So I added this to my .bashrc:

fn() {
    echo "Hello, world!"
}
export -f fn

and to my .gitconfig:

[alias]
    fn = !fn

But then git fn produces an error:

fatal: cannot run fn: No such file or directory
fatal: While expanding alias 'fn': 'fn': No such file or directory

Is this a correct way to use a bash function in a git alias definition?

planetp
  • 14,248
  • 20
  • 86
  • 160
  • I’d assume that when Git runs a bash command, it does so in its own process which will very likely skip your `.bashrc`. So you would have to define the function inside of the `.gitconfig`, within that alias line. – Alternatively, you could create a `git-fn` file and put it in the PATH and then `git fn` should also work (at least iirc) – poke Sep 26 '17 at 20:56
  • @poke, the point of `export -f` is that the definition is in the environment, so the shell invoked by git doesn't *have* to source `.bashrc` -- if it's actually bash. If it's `/bin/sh`, then it's a question of which implementation one has on hand. – Charles Duffy Sep 26 '17 at 21:11
  • @CharlesDuffy Interesting, I did not know you could put functions into the environment, that’s cool. Thanks! – poke Sep 26 '17 at 21:20

3 Answers3

11

Thats because git uses /bin/sh (so your .bashrc is not sourced).

You can call bash in a git alias as specified in this answer.

The thing is the bash shell started by the git command is not loading your .profile (which is the one responsible for including the .bashrc).

There may be other ways to do it but you can work around by doing:

[alias]
    fn = !bash -c 'source $HOME/.my_functions && fn'

With the file .my_functions like this:

#!/bin/bash
fn() {
    echo "Hello, world!"
}

You can even source .my_functions into your .bashrc if you want the functions to be available from regular shells.

hugoShaka
  • 4,977
  • 3
  • 17
  • 29
  • 1
    There's no need for the `export -f` if you're `source`ing the file. The only value to that is if you're running it in the user's dotfiles and then trying to access the previously-exported function from a subprocess. – Charles Duffy Sep 26 '17 at 21:17
  • This mostly works. To pass all arguments from the shell into `fn`, you need a ` - ` at the end: `fn = !bash -c 'source $HOME/.my_functions && fn' - ` – DragonBobZ Feb 24 '21 at 20:52
3

I don't know why, but if I just put a space before the function name in .gitconfig, it works without a problem and outputs the message:

[alias]
    fn = ! fn

It might be a bug in git, or I'm missing something in the documentation.

planetp
  • 14,248
  • 20
  • 86
  • 160
-1

If you want to be 100% certain that exported functions will be honored, ensure that the shell being called is bash, not /bin/sh (which will not honor them if it's implemented by ash or dash).

fn() { echo "hello, world"; }
export -f fn
git config --global alias.fn $'!bash -c \'fn "$@"\' _'
git fn

...properly emits:

hello, world

The relevant entry in .gitconfig:

[alias]
    fn = !bash -c 'fn \"$@\"'
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    To properly pass the arguments, you need to provide a dummy argument that will become $0: `git config --global alias.fn $'!bash -c \'fn "$@"\' x'` – glenn jackman Sep 26 '17 at 21:34
  • For me, exporting the function didn't work. I had to add it to ~/.bashrc and use `$'bash -ic \'...` – glenn jackman Sep 26 '17 at 21:35
  • @glennjackman, good point about the need for a dummy arg for `$0`. That said, re: exporting not working, can you give me a reproducer for that? (The above code works for me when copied-and-pasted exactly as-is into a session which previously had no `fn` defined). – Charles Duffy Sep 26 '17 at 22:03
  • (that said, I could definitely see it failing if you had two different bash versions, and one had a vendor or intermediate upstream patch for shellshock and the other had the final upstream patch; environment representations of exported functions haven't been constant for the lifetime of the shell). – Charles Duffy Sep 26 '17 at 22:05