0

i've made the following bash script to commit the parent repo after some change in submodule. it's all about that the script want to cd .. to check the parent repo current branch but the problem is that the cd .. is not affecting the upcoming commands because i guess the subshell

i've tried to run 1- cd ../ && before each command 2- make alias but didn't succeed 3- run exec but the script didn't continued

#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "post-commit".
commit_msg= git log -1 --pretty=%B
if [[ $(git branch | grep \* | cut -d ' ' -f2) == "int1177/next" ]]; then
    cd ..
    if [[ $(git branch | grep \* | cut -d ' ' -f2) == "B0/next" ]]; then
        git add 6_Tests
        git commit -m "bs esss"
        echo "development branch B0/next has now new commit"
    else
        echo "development branch isn't B0/next"
    fi
else
    echo "current branch isn't int1177/next"    
fi
Mo SAEED
  • 43
  • 1
  • 8
  • Git runs the hook in a subshell and there is no way at that point to change the parent shell's environment *directly.* You have to arrange for the parent shell to somehow receive instructions from your hook. This is not impossible, but also not at all trivial. – tripleee Jun 12 '19 at 02:50
  • A simple idea is to have your interactive shell monitor the current directory for a file and `source` and then remove that if it exists. This is reasonably simple, but of course not entirely unproblematic (security? Corner cases like what if multiple interactive shells have the same working directory?) – tripleee Jun 12 '19 at 02:54
  • i've tried to create foo.sh file inside the repo and added in the hook source foo.sh but it doesn't work as well.. is that what u mean by interactive shell monitor? – Mo SAEED Jun 12 '19 at 03:05
  • I repeat: the hook can't change the environment of the parent shell where you run Git. You have to make your interactive shell cooperate with the hook somehow -- the one which displays the prompt and lets you type in commands like `git commit`. If you use Bash, maybe look at putting something in `PROMPT_COMMAND`. – tripleee Jun 12 '19 at 03:07

1 Answers1

4

Actually, this particular problem is not a bash issue, but rather a Git issue.

Why doesn't "cd" work in a shell script? is valid in general, and is a suitable answer to many other questions. But this particular post-commit hook is trying to chdir out of a submodule into its parent superproject, then make a commit within the parent superproject. That is possible. It may be a bad idea for other reasons—in general it's unwise to have Git commit hooks create commits, even in other repositories1—but in this particular case you're running into the fact that Git finds its directories through environment variables.

In particular, there's an environment variable GIT_DIR that tells Git: The .git directory containing the repository is at this path. When Git runs a Git hook, Git typically sets $GIT_DIR to . or .git. If $GIT_DIR is not set, Git will find the .git directory by means of a directory-tree search, but if $GIT_DIR is set, Git assumes that $GIT_DIR is set correctly.

The solution is to unset GIT_DIR:

unset GIT_DIR
cd ..

The rest of the sub-shell commands will run in the one-step-up directory, and now that $GIT_DIR is no longer set, Git will search the superproject's work-tree for the .git directory for the superproject.

As an aside, this:

$(git branch | grep \* | cut -d ' ' -f2)

is a clumsy way to get the name of the current branch. Use:

git rev-parse --abbrev-ref HEAD

instead, here. (The other option is git symbolic-ref --short HEAD but that fails noisily with a detached HEAD, while you probably want the quiet result to be just the word HEAD, which the rev-parse method will produce.)


1The main danger in this case is that the superproject repository is not necessarily in any shape to handle a commit right now. Edit: or, as discovered in this comment, is not even set up to be a superproject for that submodule, yet, much less to have a submodule-updating commit added.

torek
  • 448,244
  • 59
  • 642
  • 775
  • thanks so much, regarding the raised danger you have mentioned, you are right if the int1177/next branch is a normal branch. however, this is a special branch (integration branch) where only the integrator has access to commit on and is committing or merging only when adding or integrating a new feature – Mo SAEED Jun 13 '19 at 10:10
  • i've tried and it works for me, but another issue comes up , executing `git add 6_Tests` create 6_Tests submodule under 6_Tests with the following warning in the log: `warning: adding embedded git repository: 6_Tests hint: You've added another git repository inside your current repository. hint: Clones of the outer repository will not contain the contents of hint: the embedded repository and will not know how to obtain it. hint: If you meant to add a submodule, use: hint: git submodule add 6_Tests hint: If you added this path by mistake, you can remove it` – Mo SAEED Jun 13 '19 at 11:53
  • Those messages (about adding a git repo that's not already set up as a submodule) mean that the superproject isn't ready to have a submodule yet—it has not been set up. That's another danger with doing this sort of thing. – torek Jun 14 '19 at 01:05
  • Doing the nested git invocation in a cleaned subshell, e.g. `( eval unset ${!GIT_*}; your; commands; here)` might be a better plan, Git might have others set, if not now then in a future release.. – jthill Jun 14 '19 at 01:47
  • @jthill: possibly so. It's not clear that just arbitrarily unsetting *all* `GIT_*` variables would be correct though. – torek Jun 14 '19 at 02:02
  • I think those vars are necessarily set for the current repo, not the one the nested commands are intended to affect. – jthill Jun 14 '19 at 02:32
  • ... so, it's like six weeks later and my hindbrain finally twigged to what you're saying: you don't want to unset things like `GIT_EDITOR` and `GIT_DISCOVERY_ACROSS_FILESYSTEMS` if the user's got those set, you should use `$(git rev-parse --local-env-vars)` instead of `${!GIT_*}`. Right. – jthill Aug 05 '19 at 16:40
  • @jthill: Right, but I didn't know `--local-env-vars` existed, that's quite nice! – torek Aug 05 '19 at 16:41