8

We have a specific requirement in which we have to push all files which arrive for being committed to a certain branch.

We are planning to achieve this via git hooks specifically commit-msg hook.

While doing so what we do is we clone branch to temporary location (/tmp/) and then in git commit-msg hook, attempt to commit arrived files to certain branch.

But what happens now is we see all files as deleted in /tmp/.

Crude commit-msg script is as under:-

#!/bin/bash
#
#!/usr/bin/env bash

#git config credential.helper store
REPOSRC="https://<USER>:<PASS>@<REPO_URL>"
LOCALREPO="<LOCAL_REPO_DIR>"

echo "Pulling code to temporarry location";

cd /tmp && git clone "${REPOSRC}" || (cd "${LOCALREPO}"; git pull;)

#here when I navigate to /tmp/<LOCALREPO> all files are listed as DELETED

git diff --cached --name-status | while read st file; do

    echo "file == $file and status == $st";

                    if [ "$st" == "A" ]; then
                        cd "${LOCALREPO}" && git add "$file" && git commit "$file" -m "$COMMIT_MSG" && git push origin "$branch"
                    else
                        cd "${LOCALREPO}" && git commit "$file" -m "$COMMIT_MSG" && git push origin "$branch"
                    fi
done

What can be the root cause for this?

EDIT:

GIT_INDEX_FILE shows path of index file from which commit was initiated and not /tmp/ path. Is there any way to change this variable? Also index file prints something like next-index-32419.lock. Regards

Leon
  • 31,443
  • 4
  • 72
  • 97
Jatin Dhoot
  • 4,294
  • 9
  • 39
  • 59
  • 4
    Hooks run in special environments, often with various Git environment variables set. It's therefore tricky to write hooks that can run fully general Git commands elsewhere. Be sure you know what `$GIT_DIR` and `$GIT_INDEX_FILE` are set to, if anything, and what you should do if they are. Read up on how Git uses the index. – torek Mar 13 '18 at 07:08
  • OK @torek, Let me take a look as you have suggested. Regards – Jatin Dhoot Mar 13 '18 at 07:12
  • Seems that is the problem. GIT_INDEX_FILE shows path of index file from which commit was initiated and not /tmp/ path. Is there any way to change this variable? – Jatin Dhoot Mar 13 '18 at 07:43
  • Yes, you can change or unset environment variables, and you will need to do so to deal with commits in the other repository. But you will need to leave the variable unchanged to deal with the pending commit in the original repository (the files to be committed are not necessarily those in the work-tree). – torek Mar 13 '18 at 15:03
  • Try `git --work-tree=$(pwd) pull` and then set `GIT_WORK_TREE=$(pwd)` – VonC Mar 16 '18 at 07:16

3 Answers3

5

Whenever a hook changes folder, it is important to check the value of:

  • GIT_DIR (which should reference the right .git repo folder in your case)
  • GIT_WORK_TREE (which should not reference the expected folder)

That is why, for any Git command, you would need to replace git with:

git --work-tree=$(pwd) ...

(or /path/to/your/working/tree)

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
1

I ran into this same issue while developing pre-commit (which distributes git hooks as installable git repositories).

The GIT_* environment variables are interfering with the git clone commands. These environment variables are set by git internals before invoking the .git/hooks/pre-commit script.

My solution to this was to clear all GIT_* environment variables (except for a whitelist of allowed variables).

My solution is in python but you can probably adapt it into whatever language you're writing in:

def no_git_env(_env=None):
    # Too many bugs dealing with environment variables and GIT:
    # https://github.com/pre-commit/pre-commit/issues/300
    # In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running
    # pre-commit hooks
    # In git 1.9.1 (maybe others), git exports GIT_DIR and GIT_INDEX_FILE
    # while running pre-commit hooks in submodules.
    # GIT_DIR: Causes git clone to clone wrong thing
    # GIT_INDEX_FILE: Causes 'error invalid object ...' during commit
    _env = _env if _env is not None else os.environ
    return {
        k: v for k, v in _env.items()
        if not k.startswith('GIT_') or
        k in {'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND'}
    }
anthony sottile
  • 61,815
  • 15
  • 148
  • 207
0

I would just simply execute my git commands in a clean environment. Now the question is how to do that?

$ env --help | grep env
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
Set each NAME to VALUE in the environment and run COMMAND.
  -i, --ignore-environment  start with an empty environment
  -u, --unset=NAME     remove variable from the environment
A mere - implies -i.  If no COMMAND, print the resulting environment.
Full documentation at: <http://www.gnu.org/software/coreutils/env>

So using a -i would give your git command a clean environment. But you might need certain variables to exists. So I would just add below to the top of my script

alias git='env -i PATH=$PATH HOME=$HOME git'

Now all the git commands your run in your hook will not inherit the environment which the commit hook was run with

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • Thanks for your suggestion. I will check and get back to you. – Jatin Dhoot Mar 23 '18 at 04:57
  • It seems not to work. alias git='env -i PATH=$PATH HOME=$HOME git' REPOSRC="https://git-codecommit.eu-west-1.amazonaws.com/repo" LOCALREPO="auto_merge_test_repo" project_dir="/tmp/""$LOCALREPO" cd /tmp && git clone "${REPOSRC}" – Jatin Dhoot Mar 25 '18 at 12:49
  • Then when I cd into /tmp/auto_merge_test_repo I see changeset WRT to git repository from where commit was made – Jatin Dhoot Mar 25 '18 at 12:49