12

My indention is to have a script that updates all git submodules according to which branch is given. If there's no such branch for a submodule, master is used.

This is what I have now:

#!/bin/bash -x

if [ -z $1 ]; then
    echo "Branch name required."
    exit
fi

function pbranch {
    exists=`git show-ref refs/heads/$branch`

    if [ -z $exists ]; then
        branch="master"
    fi

    git co $branch
    git pull origin $branch
}

branch=$1

git submodule foreach pbranch

But when running this script, the error is thrown:

oleq@pc ~/project> git-fetchmodules major
+ '[' -z major ']'
+ branch=major
+ git submodule foreach pbranch
Entering 'submodule'
/usr/lib/git-core/git-submodule: 1: eval: pbranch: not found
Stopping at 'submodule'; script returned non-zero status.

My guess is that git submodule foreach utilizes eval (according to the documentation), which I don't use correctly in this context.

There are billions of examples of how to use this command with "inline callback" but I couldn't found a single one with the callback in form of the function. Any idea how to solve this?

3 Answers 3

11

I resolved my issue by putting the function inside of the quotes as a callback:

#!/bin/bash

if [ -z $1 ]; then
    echo "Branch name required."
    exit
fi

git submodule foreach "
    branch=$1;
    exists=\$(git show-ref refs/heads/\$branch | cut -d ' ' -f1);

    if [ -z \$exists ]; then
        branch='master';
    fi;

    echo Checking branch \$branch for submodule \$name.;

    git fetch --all -p;
    git co \$branch;
    git reset --hard origin/\$branch;
"

Note that variables like $1 are those from the script's namespace. The "escaped ones" like $\(bar), \$branch are evaluated within "the callback". It was pretty easy.

1
  • $variables in double quotes are replaced by bash with values but not when they are in single quote text. So, two solutions: 1) use double quotes and escape symbol foreach " \$variable` ... ", or 2) use single quote without escape symbol like foreach '... $branch ...'`
    – Artur A
    Apr 2, 2021 at 2:45
11

You can use functions, but you need to export them first:

export -f pbranch

Also, if you want bash syntax extensions, you might want to force a bash shell to be started:

git submodule foreach bash -c 'pbranch'
1
  • Seems with bash 5.0.17 and git 2.25.1 this answer does not work, unfortunately... Oct 16, 2020 at 19:08
6

A shell function only exists inside the shell where it is defined. Similarly, a Java method only exists in the program instance where it is defined, and so on. You cannot invoke a shell function from another program, even if that program happens to be another shell that is run by a child process of the original shell.

Instead of defining a function, make pbranch a separate script. Put it in your PATH.

#!/bin/sh
branch="$1"
ref="$(git show-ref "refs/heads/$branch")"
if [ -z "$ref" ]; then
    branch="master"
fi
git co "$branch"
git pull origin "$branch"

Shell programming note: always put double quotes around variable substitutions and command substitutions: "$foo", "$(foo)", unless you know that you need to leave the quotes out. Unprotected substitutions are interpreted as whitespace-separated lists of glob patterns, which is almost never desired. Also, don't use backticks, for similar reasons, use $(…) instead. Here, it doesn't actually matter because git branch names don't contain special characters, and because [ -z $branch ] is parsed as [ -z ] which is also true when branch is empty. But don't get into the habit of omitting the quotes, it will come back and bite you.

Let's say the script is called pbranch-submodule, you can then run

git submodule foreach pbranch-submodule
1
  • And if you name it git-pbranch-submodule, it can behave like a built-in git command: git pbranch-submodule or git submodule foreach git pbranch-submodule. (Note that foreach accepts a shell command and not a git command.)
    – idbrii
    Aug 1, 2014 at 13:12

You must log in to answer this question.

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