conan 不替代 git,而是通过 conan.tools.scm.git 动态获取 commit、remote url 和脏状态等信息注入元数据,避免硬编码版本或手动维护 conandata.yml,确保构建可重现。

Conan 本身不替代 Git,但能通过 Git 工具类主动读取当前仓库状态,把提交哈希、远程 URL、脏标记等信息注入包元数据——这才是“配合”的本质。直接硬编码版本号或手动更新 conandata.yml 是反模式,容易在 CI 中构建出不可复现的包。
conan.tools.scm.Git 自动抓取 commit 和 remote
Conan 2.x 推荐用 conan.tools.scm.Git 替代旧版 tools.Git。它在 source() 或 export_sources() 阶段调用时,会基于当前工作目录(即 conanfile.py 所在路径)执行 git rev-parse HEAD 和 git config --get remote.origin.url。
- 必须确保该目录是 Git 仓库根目录,否则
get_commit()返回空或报错 -
get_remote_url()仅读取本地配置,不验证远程是否可达;若 CI 使用 shallow clone,可能拿不到完整历史,get_commit()仍有效,但git describe类操作会失败 - 示例写法:
from conan.tools.scm import Git def source(self): git = Git(self) commit = git.get_commit() url = git.get_remote_url() self.output.info(f"Building from {url}@{commit}")
如何防止“脏构建”污染包缓存
未提交的修改(dirty state)会导致同一 commit 构建出不同二进制包,破坏可重现性。Conan 提供 git.is_dirty() 做守门人。
- 在
export_sources()或build()开头加判断,if git.is_dirty(): raise ConanInvalidConfiguration("Dirty repo not allowed") - 注意:
is_dirty()默认忽略.gitignore,但会检测已跟踪文件的修改;若需跳过某些生成文件,可用git.is_dirty(untracked_files=False) - CI 环境中建议强制加
--no-deps或--keep-source,避免因缓存污染导致误判
conandata.yml 不该手写,要靠 Git 动态生成
conandata.yml 的作用是声明源码来源,但它的内容(尤其是 commit 和 url)应来自运行时 Git 调用,而非静态填写。否则每次切分支都要手动改 YAML,极易出错。
- 推荐做法:在
source()中用git.get_commit()获取值,再通过self.conan_data注入(Conan 2.0+ 支持运行时覆盖) - 若必须用
conandata.yml(如预发布流程),应由脚本自动生成,例如:echo "sources:\n mylib:\n commit: $(git rev-parse HEAD)\n url: $(git config --get remote.origin.url)" > conandata.yml
- 错误示范:在
conandata.yml里写死commit: abc123,但开发者本地改了代码又没提交,conan create仍会成功,实际构建的是脏代码
Git 和 Conan 的边界很清晰:Git 管内容快照,Conan 管二进制产物。真正容易被忽略的是,conan.lock 文件记录的是包 ID,不是 Git commit;如果同一个 commit 因编译选项或依赖版本变化生成不同包 ID,conan.lock 就成了唯一真相——这时 Git 信息只是辅助溯源,不能替代锁文件。











