Git裸仓库post-receive钩子切换目录执行git pull失败排查
问题
服务器上有两个Git仓库:一个是裸仓库(bare repo),另一个是带工作区的普通仓库(non-bare repo)。在裸仓库的post-receive钩子脚本中,通过os.chdir()切换到普通仓库目录,执行git pull origin master命令以同步推送到裸仓库的内容,但返回subprocess.CalledProcessError(状态码128)。不过直接在钩子目录运行该脚本却能正常执行,请问为何Git不识别通过os.chdir()完成的目录切换?
仓库结构
home/my_machine ├── Git │ └── search_movie.git │ ├── branches │ ├── config │ ├── description │ ├── HEAD │ ├── hooks │ │ └── post-receive │ ├── info │ │ └── exclude │ ├── objects │ │ ├── 93 │ │ │ └── 68286a3e6b37235950d0b369d6aa955bc6699b │ │ ├── info │ │ └── pack │ │ ├── pack-8710cd10d53b27cda2228c33ff59e337426563b9.idx │ │ └── pack-8710cd10d53b27cda2228c33ff59e337426563b9.pack │ └── refs │ ├── heads │ │ └── master │ └── tags └── Git_real └── search_movie ├── find_movie.py ├── output.txt └── search_vid.py
钩子代码
#!/usr/bin/env python3 import sys, os, subprocess path_2_non_bare_repo = "/home/my_machine/Git_real/search_movie" #cwd is the path to my_repo/.git/hooks cwd = os.path.dirname(os.path.realpath(__file__)) #change dir to real repository os.chdir(path_2_non_bare_repo) try: pll = subprocess.check_output(["git", "pull", "origin", "master"], shell=False) print("fetch =", pll) except subprocess.CalledProcessError as err: print(err)
原因与解决办法
核心原因
- 环境变量干扰:Git执行钩子时会保留
GIT_DIR环境变量,指向裸仓库的.git目录。即便你用os.chdir()切换了目录,Git依然会读取这个环境变量,误以为当前操作的是裸仓库——而裸仓库没有工作区,无法执行pull命令,直接触发状态码128错误。 - 执行环境差异:钩子由Git进程(通常是推送操作的用户,比如
git用户或SSH登录用户)执行,该用户可能没有普通仓库目录的读写权限,或者缺少拉取远程仓库的权限(比如SSH密钥未配置)。而你手动运行脚本时用的是自己的用户,权限足够,所以能正常执行。
解决步骤
- 清除
GIT_DIR环境变量:在执行git pull前删除该变量,让Git自动识别当前目录的普通仓库:
修改钩子代码,在os.chdir()后添加:# 移除干扰的GIT_DIR环境变量 if 'GIT_DIR' in os.environ: del os.environ['GIT_DIR'] - 直接指定工作目录(更可靠):不需要依赖
os.chdir(),在subprocess中通过cwd参数指定普通仓库路径,同时清除环境变量:try: # 复制环境变量并移除GIT_DIR env = os.environ.copy() env.pop('GIT_DIR', None) # 直接指定工作目录执行git pull pll = subprocess.check_output( ["git", "pull", "origin", "master"], shell=False, cwd=path_2_non_bare_repo, env=env ) print("fetch =", pll) except subprocess.CalledProcessError as err: print(err) - 检查权限:确保执行钩子的用户对
/home/my_machine/Git_real/search_movie目录有读写权限,且该用户能正常拉取origin仓库(比如配置好对应SSH密钥)。 - 调试错误输出:可以捕获错误输出辅助排查,比如修改异常处理部分:
except subprocess.CalledProcessError as err: print(f"错误状态码: {err.returncode}") print(f"错误输出: {err.output.decode('utf-8')}")
内容的提问来源于stack exchange,提问作者Module_art
相关产品推荐
相关产品推荐

