Git commit 遇到的 precommit Not a git repository 错误

问题描述

在提交时遇到执行 hooks 失败,错误提示“Not a git repository”
具体错误如下所示:

xyf$ git commit -m 'feat: test commit'
husky > pre-commit (node v8.11.3)

> pt-lego-service@1.0.0 precommit /Users/lego
> lint-staged

  ✔ Running tasks for **.{js, es6, jsx}
Not a git repository
To compare two paths outside a working tree:
usage: git diff [--no-index] <path> <path>
husky > pre-commit hook failed (add --no-verify to bypass)

husky 的配置如下:

"husky": {
    "hooks": {
      "pre-commit": "npm run precommit && git diff",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "devDependencies": {
    "husky": "^1.0.0-rc.8"
  }

问题原因

参考网上资料,经定位是运行 hooks 时的 Git 环境变量 GIT_DIR 错误造成
https://stackoverflow.com/questions/3542854/calling-git-pull-from-a-git-post-update-hook

验证本地执行 hooks 时的 GIT_DIR=.git,正确的 GIT_DIR 应该为空(即未设置),在正常的电脑执行 hooks 的 GIT_DIR 确认为空

确认原因

确认 GIT_DIR 的值使用这两种方式,一是在 pre-commit hooks 配置里增加 echo $GIT_DIR,二是在 .git/hooks/pre-commit 文件增加打印 echo $GIT_DIR

方法一,hooks 配置增加 echo $GIT_DIR

"husky": {
    "hooks": {
      "pre-commit": "echo $GIT_DIR && npm run precommit && git diff --check --staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }

执行结果如下:

xyf$ git commit -m 'fix: test comit 2'
pre-commit

husky > pre-commit (node v8.11.3)
.git

> pt-lego-service@1.0.0 precommit /Users/xieyufang/Documents/projects/pt-lego/service
> lint-staged

  ✔ Running tasks for **.{js, es6, jsx}
Not a git repository
To compare two paths outside a working tree:
usage: git diff [--no-index] <path> <path>
husky > pre-commit hook failed (add --no-verify to bypass)

方法二,.git/hooks/pre-commit 文件增加 echo $GIT_DIR

#!/bin/sh
# husky

# Hook created by Husky
#   Version: 1.3.1
#   At: 2020-7-15 22:10:18
#   See: https://github.com/typicode/husky#readme

# From npm package
#   Name: husky
#   Directory: /Users/lego/node_modules/husky
#   Homepage: https://github.com/typicode/husky#readme

echo $GIT_DIR;

scriptPath="service/node_modules/husky/run.js"
hookName=`basename "$0"`
gitParams="$*"

echo $hookName;
echo $gitParams;
debug() {
  [ "${HUSKY_DEBUG}" = "true" ] && echo "husky:debug $1"
}

debug "$hookName hook started..."

if ! command -v node >/dev/null 2>&1; then
  echo "Can't find node in PATH, trying to find a node binary on your system"
fi

if [ -f "$scriptPath" ]; then
  # if [ -t 1 ]; then
  #   exec < /dev/tty
  # fi
  if [ -f ~/.huskyrc ]; then
    debug "source ~/.huskyrc"
    source ~/.huskyrc
  fi
  service/node_modules/run-node/run-node "$scriptPath" $hookName "$gitParams"
else
  echo "Can't find Husky, skipping $hookName hook"
  echo "You can reinstall it using 'npm install husky --save-dev' or delete this hook"
fi

运行结果如下:

xyf$ git commit -m 'fix: test comit 2'
.git
pre-commit

husky > pre-commit (node v8.11.3)

> pt-lego-service@1.0.0 precommit /Users/lego
> lint-staged

  ✔ Running tasks for **.{js, es6, jsx}
Not a git repository
To compare two paths outside a working tree:
usage: git diff [--no-index] <path> <path>
husky > pre-commit hook failed (add --no-verify to bypass)

解决问题

即然发现是 GIT_DIR 错误造成,那就想办法更改,试验以下这几种方法

// 1 通过指定 --git-dir 参数改变当前执行的 Git 命令的环境变量
git --git-dir=.git {command}
// 2 重置 Git 运行的所有环境变量
unset $(git rev-parse --local-env-vars)
// 3 直接清除 GIT_DIR 的环境变量
unset GIT_DIR
// 4 Git 命令前追加 env -i 执行
env -i git {command}
// 5 移除 husky 配置中相关的 Git 命令
git diff

直接修改 hooks 的配置

// 方法一:指定 --git-dir 参数,注意这里的执行 hooks 和 .git 的相对目录
// 这里执行 hooks 是在仓库的下一级目录,所以这里指定 ../.git
"husky": {
  "hooks": {
    "pre-commit": "npm run precommit && git --git-dir=../.git diff",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

// 方法二:重置 Git 运行环境变量
"husky": {
  "hooks": {
    "pre-commit": "unset $(git rev-parse --local-env-vars) && npm run precommit && git diff",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

// 方法三:直接清除 GIT_DIR 的值
"husky": {
  "hooks": {
    "pre-commit": "unset GIT_DIR && npm run precommit && git diff",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

// 方法四:在 Git 的命令前加上 env -i
"husky": {
  "hooks": {
    "pre-commit": "npm run precommit && env -i git diff",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

// 方法五:移除有关的 Git 命令
// 即然是 GIT_DIR 环境变量错误造成的执行 Git 命令错误,那直接移除相关命令也可以解决
"husky": {
  "hooks": {
    "pre-commit": "npm run precommit",
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

经验证,以上几种方式都可以成功,完成 git commit

分析原因

上面的解决办法只是临时解决了问题,并没有找到问题的根本原因,为什么会有 GIT_DIR 的不同的值,一开始是怀疑执行 hooks 的环境造成,试图找出 hooks 怎么执行的,查找 Git Hook 和 husky 文档依然未发现如何执行,在接着试图通过比较正常和出问题的电脑的本地环境和配置找出它们的差异性,依然没有发现问题。

针对同一个仓库,比较它们 husky 的安装版本,比较它们 hooks 的命令,这些都没有差异

node_modules/husky
.git/hooks/pre-commit

再后来不经意看到 husky 文档提到的 hooks 不能正确执行的原因有一句话

If not, you may have another Git hooks manager defined in your package.json overwriting husky's hooks. Check also the output during install, you should see:

husky > Setting up git hooks
husky > Done

然后就从 npm install 的 husky 日志方向上入手,首先分别在正常和出问题的电脑上执行 npm i 查看结果没有差异,接着试着在出问题的电脑上新建 git 仓库在安装 husky 发现了差异点

当前仓库 npm i 的结果,husky hooks 安装成功

> husky@1.3.1 install /Users/lego/node_modules/husky
> node husky install

husky > setting up git hooks
husky > done

出问题的电脑,新仓库的 npm i husky 记录,husky hooks 安装失败

xyf$ npm install husky --save-dev

> husky@4.2.5 install /Users/pt/node_modules/husky
> node husky install

Husky requires Node 10 (runtime: v8.11.3), skipping Git hooks installation.

> husky@4.2.5 postinstall /Users/pt/node_modules/husky
> opencollective-postinstall || exit 0

Thank you for using husky!
If you rely on this package, please consider supporting our open collective:
> https://opencollective.com/husky/donate

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN pt-lego-service@1.0.0 No license field.

+ husky@4.2.5
added 48 packages from 22 contributors and audited 48 packages in 8.645s
found 0 vulnerabilities

比较发现出问题原因是 husky@4.0 依赖 node > 10,而本地环境只有 8.11.3 造成 husky hooks 安装失败,而安装成功的 husky 版本是 1.3.1 在当前的 node@8.11.3 版本下能安装成功

综合上面结果,比较出两台电脑的环境如下
husky@1.3.1 & node@8.11.3
husky@1.3.1 & node@v10.15.3
虽然基于上面的环境 husky hooks 都能安装成功,但 node@8.11.3 下执行 hooks 失败,依然是因为 GIT_DIR = .git

在接着发现这个文章,Git 2.18.0 后不在设置 GIT_DIR
$GIT_DIR is no longer set when pre-commit hooks are called

As of the release of 2.18.0, $GIT_DIR is no longer set before calling pre-commit hooks. This change was introduced in "set_work_tree: use chdir_notify" (8500e0de) and is still present in master. I reviewed the discussion when this change was initially submitted, and I don't think this behavior change was intentional. I have several hooks that were broken like this, and from a quick Google search, using $GIT_DIR without setting a default does seem pretty common in hooks.

于是再查看两台机器的 Git 版本,一个是 2.15.1(有问题的机器),一个是 2.21.0(正常的机器),这解释了为什么在node@8.11.3机器(有问题的机器)上执行 hooks 时的环境变量 GIT_DIR 有值

在有问题的机器上新仓库实验 hooks 是成功的,但发现只要 node_module 和 .git 不是同一级目录就会出现这个错误

结论

造成问题的原因还是版本的问题相互影响造成,husky@1.3.1 + node@8.11.3 + git@2.15.1,基于这样的环境的可以成功安装 husky,但只能在 node_module 和 .git 目录同级才能成功执行 husky,如果 .git 和 node_module 在不同目录就会遇到当前的问题

附加

关于 git rev-parse --git-dir 命令的用法的解释

The "right" way to find the directory has always been "git rev-parse --git-dir" (which will use GIT_DIR if set, and otherwise do the normal discovery process).

参考资料

https://github.com/typicode/husky/issues/364
https://stackoverflow.com/questions/3542854/calling-git-pull-from-a-git-post-update-hook
http://jakescruggs.blogspot.com/2010/07/using-git-inside-git-hook.html
https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git.html
https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
https://git-scm.com/docs/git-rev-parse
https://www.npmjs.com/package/husky#ci-servers
https://public-inbox.org/git/20180826004150.GA31168@sigill.intra.peff.net/t/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354