You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于libgit2为已实现的git log添加--name-status选项?

基于libgit2实现git log --name-status的方法

要给你的log命令添加--name-status功能,核心是利用libgit2的diff遍历接口,提取每个提交与父提交(或初始提交)的文件变更状态和路径,具体步骤如下:

1. 添加功能开关变量

在你的命令上下文(比如包含show_diff的结构体或全局变量)中,新增一个布尔变量show_name_status,用来控制是否启用--name-status输出:

bool show_name_status = false;

当命令行解析到--name-status选项时,将该变量设为true。

2. 配置Diff选项

确保diff配置能正确捕获文件变更状态,初始化diffopts时可添加必要标志(按需选择):

git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
// 可选:跟踪文件类型变更等特殊情况
diffopts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;

3. 实现Diff回调函数

这个函数会被libgit2调用,处理每个diff条目并输出符合git log --name-status格式的内容:

static int diff_name_status_cb(
    const git_diff_delta *delta,
    float progress,
    void *payload)
{
    // 跳过无变更的条目
    if (delta->status == GIT_DELTA_UNMODIFIED || 
        delta->status == GIT_DELTA_IGNORED) {
        return 0;
    }

    // 将libgit2的delta状态转换为git标准状态字符
    char status_char;
    switch (delta->status) {
        case GIT_DELTA_ADDED: status_char = 'A'; break;
        case GIT_DELTA_DELETED: status_char = 'D'; break;
        case GIT_DELTA_MODIFIED: status_char = 'M'; break;
        case GIT_DELTA_RENAMED: status_char = 'R'; break;
        case GIT_DELTA_COPIED: status_char = 'C'; break;
        case GIT_DELTA_TYPECHANGE: status_char = 'T'; break;
        default: status_char = '?'; break;
    }

    // 输出状态与文件名
    printf("%c\t%s\n", status_char, delta->new_file.path);
    
    // 重命名/复制场景额外输出原路径(匹配git默认格式)
    if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED) {
        printf("-\t%s\n", delta->old_file.path);
    }

    return 0;
}

4. 修改现有log函数,集成name-status输出

在print_commit之后,添加show_name_status的判断分支,生成diff并触发回调输出:

print_commit(commit);

if (show_name_status) {
    git_tree* a = NULL, * b = NULL;
    git_diff* diff = NULL;
    int parents = (int)git_commit_parentcount(commit);

    // 跳过合并提交(与git log默认行为一致,可按需调整)
    if (parents > 1) {
        printf(" (merge commit, no file status shown)\n");
        goto cleanup;
    }

    // 获取当前提交的树对象
    check_lg2(git_commit_tree(&b, commit), "Get commit tree", NULL);
    // 获取父提交的树对象(初始提交则a为NULL)
    if (parents == 1) {
        git_commit* parent;
        check_lg2(git_commit_parent(&parent, commit, 0), "Get parent commit", NULL);
        check_lg2(git_commit_tree(&a, parent), "Get parent tree", NULL);
        git_commit_free(parent);
    }

    // 生成当前提交与父提交的树diff
    check_lg2(git_diff_tree_to_tree(
        &diff, git_commit_owner(commit), a, b, &diffopts),
        "Generate diff between trees", NULL);

    // 遍历diff条目,输出name-status
    check_lg2(git_diff_foreach(
        diff,
        NULL,
        NULL,
        diff_name_status_cb,
        NULL),
        "Iterate diff for name-status", NULL);

cleanup:
    // 释放资源,避免内存泄漏
    if (diff) git_diff_free(diff);
    if (a) git_tree_free(a);
    if (b) git_tree_free(b);
}

5. 关键注意事项

  • 资源释放:所有创建的git_diff、git_tree、git_commit对象必须手动释放。
  • 合并提交处理:默认git log不显示合并提交的文件状态,若需要支持可修改跳过逻辑。
  • 路径过滤:如果已有pathspec配置,diff会自动过滤符合条件的文件,无需额外处理。

内容的提问来源于stack exchange,提问作者valeriot90

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 06:37:03