Rust使用libgit2实现git add --force *的方法及报错解决
如何在Rust中使用libgit2实现等价于
git add --force *的操作 问题复现
参考官方示例编写的初始代码如下:
use git2::{Index, IndexAddOption, Repository}; let repo = Repository::open("/path/to/a/repo").expect("failed to open"); let mut index = repo.index().expect("cannot get the Index file"); index.add_all(["*"].iter(), IndexAddOption::DEFAULT, None); index.write();
使用IndexAddOption::DEFAULT参数时代码可正常运行,将参数替换为IndexAddOption::FORCE后,在Linux内核源码仓库测试触发如下报错:
Error { code: -1, klass: 10, message: "invalid path: 'arch/i386/'" }
问题原因
IndexAddOption::FORCE模式下,libgit2会跳过默认的路径过滤逻辑,尝试将所有通配符匹配到的路径(包括被.gitignore忽略的文件、目录等)加入暂存区。而Git暂存区本身不支持直接存储目录,原生git add --force *命令执行时会自动过滤掉匹配到的目录,递归遍历目录下的所有文件进行添加;libgit2在FORCE模式下没有内置这个目录过滤逻辑,因此传入目录路径时会触发非法路径错误。
实现方案
方案1:添加路径回调过滤目录项
调用add_all时传入路径校验回调,手动过滤掉所有目录路径,和原生git行为对齐,不需要引入额外依赖:
use git2::{Index, IndexAddOption, IndexMatchedPath, Repository}; use std::path::Path; let repo = Repository::open("/path/to/a/repo").expect("failed to open repo"); let mut index = repo.index().expect("failed to get index"); index.add_all( ["*"].iter(), // 强制添加模式,同时保留路径规范检查 IndexAddOption::FORCE | IndexAddOption::CHECK_PATHSPEC, // 回调中跳过所有目录路径 Some(|path: &Path, _matched_kind: IndexMatchedPath| -> i32 { if path.is_dir() { return 1; // 返回1表示跳过当前路径 } 0 // 返回0表示添加当前路径 }) ).expect("failed to add files to index"); index.write().expect("failed to write index changes");
方案2:手动遍历工作区文件逐个添加
如果需要更灵活的路径控制,可以手动遍历工作区下的所有文件,过滤掉.git目录后逐个调用添加接口,这种方式逻辑更直观,不会出现路径匹配异常:
use git2::{Index, Repository}; use std::path::Path; use walkdir::WalkDir; let repo = Repository::open("/path/to/a/repo").expect("failed to open repo"); let mut index = repo.index().expect("failed to get index"); let workdir = repo.workdir().expect("bare repository does not support add operation"); // 遍历工作区所有文件,跳过.git目录 for entry in WalkDir::new(workdir) .into_iter() .filter_entry(|e| e.file_name() != ".git") .filter_map(|res| res.ok()) .filter(|e| e.file_type().is_file()) { let rel_path = entry.path().strip_prefix(workdir).unwrap(); index.add_path(rel_path).expect("failed to add path {rel_path:?}"); } index.write().expect("failed to write index changes");
注意事项
- Git暂存区仅存储文件对象,所有传入index操作接口的路径必须是具体文件路径,不能是目录路径,无论是否使用FORCE选项都需要遵守这个规则。
- 上述两种实现的行为和原生
git add --force *完全一致,会强制添加所有被.gitignore忽略的文件,不会误添加目录项。
内容的提问来源于stack exchange,提问作者Ngahy Be
相关产品推荐
相关产品推荐

