pip-compile 默认不锁 git 依赖,因其将 git+https://... 视为“可变源”而不参与哈希计算;需显式添加 --generate-hashes 和 --allow-unsafe 才能锁定,且推荐用 commit hash 替代 tag 以规避 force-push 风险。

pip-compile 为什么默认不锁 git 依赖?
因为 pip-compile 默认只解析 setup.py 或 pyproject.toml 中声明的静态依赖,而 git+https://... 这类 URL 依赖在解析时被视为“可变源”,不参与哈希计算——除非你显式启用 --generate-hashes 并配合 --allow-unsafe(后者常被忽略)。
常见错误现象:requirements.in 里写了 mylib @ git+https://github.com/user/repo@v1.2.0,但生成的 requirements.txt 里没带 --hash,CI 环境重装时可能拉到不同 commit。
- 必须加
--generate-hashes才会为git依赖算 SHA256 - 但
--generate-hashes默认拒绝git、hg、svn源,所以还得加--allow-unsafe - 如果目标分支有 force-push 风险,建议改用具体 commit hash 替代 tag,例如
git+https://...@a1b2c3d
锁定子依赖时 pip-compile 怎么处理 extras_require?
它默认忽略所有 extras,除非你在 requirements.in 中显式引用,比如写 requests[security] 或 mylib[dev,test]。直接在 setup.py 里定义的 extras_require 不会自动展开进锁文件。
使用场景:你想锁死测试环境用的全部工具链(如 pytest、black),但又不想把它们塞进主 requirements.in。
立即学习“Python免费学习笔记(深入)”;
- 推荐做法:单独建
requirements-dev.in,里面写-r requirements.in和mylib[dev] -
pip-compile requirements-dev.in --output-file=requirements-dev.txt才会真正解析并锁定extras里的依赖 - 注意
pip-tools==7.0+开始,extras解析更严格——如果mylib的pyproject.toml用的是 PEP 621 格式,且dependencies里没包含optional-dependencies的内容,那即使写了[dev]也可能漏锁
pip-compile --upgrade 和 --upgrade-package 的行为差异
--upgrade 是全局更新:重新解析所有依赖树,取每个包的最新兼容版本;--upgrade-package 只升指定包及其直系子依赖,其余保持原锁版本不动——这是灰度升级的关键控制点。
性能影响:对大型项目,--upgrade 可能卡住十几秒甚至分钟级,因为它要重新跑整个依赖求解器;--upgrade-package requests 通常半秒内完成。
- 如果你只想升
django到 4.2.x,但要求djangorestframework仍卡在 3.14.x,就用--upgrade-package django - 但要注意:如果新
django强制要求sqlparse>=0.4.4,而旧锁里是sqlparse==0.4.2,pip-compile仍会升级sqlparse—— 它只“保底不降级”,不“强制不升级” - 想彻底冻结某个子依赖?得在
requirements.in里显式写死,比如加一行sqlparse==0.4.2
CI 中运行 pip-compile 失败,常见 ResolutionTooDeep 错误怎么破?
这不是网络或权限问题,是依赖图太深触发了 pip-tools 内置的递归限制(默认 20 层),常见于大量使用 setup.cfg + install_requires 嵌套、或某些包把开发依赖误写进生产依赖的情况。
错误信息典型长这样:pip._vendor.resolvelib.resolvers.ResolutionTooDeep: Maximum resolution depth reached.
- 先试
--max-rounds 30(别超过 50,否则可能真卡死) - 更治本的办法:检查
requirements.in里有没有间接引入巨量 dev-only 包的项,比如pre-commit或tox—— 它们不该出现在构建依赖里 - 如果用了
pip-tools>=7.3.0,可以加--rebuild强制清空缓存再试,旧版本缓存损坏有时也会假报这个错
最麻烦的是跨 Python 版本锁文件复用:用 3.11 编译的 requirements.txt 在 3.9 环境下跑 pip-compile --upgrade 可能触发深度爆炸——因为解释器约束变了,求解路径完全不同。别混用。










