git merge时三方合并算法源码解读

三方合并算法简介:

Git 的三方合并算法主要由 merge-recursive.cdiff.c 中的代码实现,核心部分涉及以下几个步骤:找到共同祖先、生成差异、合并变更。这段代码逻辑较复杂,这里只讲解 Git 代码库中的关键函数和其逻辑。

以下是简化和注释版的三方合并算法实现的核心代码片段:


1. 找到共同祖先 (common ancestor)

共同祖先的查找通过 merge-base 实现,使用 Git 的 DAG 结构查找分支的最低公共祖先。

源码:merge-base.c

复制代码
 1 struct commit *get_merge_base(struct commit *one, struct commit *two) {
 2     // 找到分支 one 和 two 的最低公共祖先
 3     struct commit_list *list;
 4     struct commit *result;
 5 
 6     // 使用递归方法遍历提交图
 7     list = get_merge_bases(one, two);
 8     result = pop_commit(&list);
 9 
10     // 如果有多个祖先(Octopus),需要进一步处理
11     while (list) {
12         free_commit_list(list);
13         list = get_merge_bases(one, two);
14     }
15     return result;
16 }
复制代码

 


2. 生成差异 (diff generation)

在找到共同祖先后,Git 会计算当前分支(HEAD)和目标分支相对于共同祖先的差异。

源码:diff.c

复制代码
 1 void diff_tree_merge(const struct tree *ancestor, const struct tree *head, const struct tree *merge) {
 2     // 比较 ancestor 与 head 的差异
 3     diff_tree(ancestor, head, &diffopts);
 4 
 5     // 比较 ancestor 与 merge 的差异
 6     diff_tree(ancestor, merge, &diffopts);
 7 
 8     // 处理两组差异结果,供后续合并使用
 9     diffcore_std(&diffopts);
10 }
复制代码

 


3. 三方合并逻辑 (Three-way merge)

三方合并的核心逻辑在 merge-recursive.c 中,通过 merge_trees 实现。

源码:merge-recursive.c

复制代码
 1 int merge_trees(const struct tree *ancestor, const struct tree *head, const struct tree *merge) {
 2     struct traverse_info info;
 3     struct merge_options options;
 4     int merge_status;
 5 
 6     // 初始化合并选项
 7     memset(&options, 0, sizeof(options));
 8     options.head = head;
 9     options.merge = merge;
10 
11     // 递归合并文件和目录
12     merge_status = traverse_trees_recursive(ancestor, head, merge, &info, &options);
13 
14     if (merge_status < 0) {
15         fprintf(stderr, "Merge conflict detected\n");
16         return -1; // 冲突需要用户手动解决
17     }
18 
19     return 0; // 合并成功
20 }
复制代码

 

核心:递归合并文件
复制代码
int traverse_trees_recursive(const struct tree *ancestor, const struct tree *head, const struct tree *merge, 
                             struct traverse_info *info, struct merge_options *options) {
    // 遍历树,逐文件比较
    if (is_conflict(head, merge)) {
        // 如果同一文件在两个分支中都有修改,则标记为冲突
        add_conflict_entry(info, head, merge);
        return -1;
    }

    // 合并变更
    merge_file(info, ancestor, head, merge, options);
    return 0;
}
复制代码

 

文件级合并(merge_file)

复制代码
void merge_file(struct traverse_info *info, const struct tree *ancestor,
                const struct tree *head, const struct tree *merge,
                struct merge_options *options) {
    struct file_merge_data data;

    // 从 ancestor、head 和 merge 中获取文件内容
    data.base = get_file_content(ancestor);
    data.local = get_file_content(head);
    data.remote = get_file_content(merge);

    // 执行合并操作
    if (!apply_three_way_merge(&data)) {
        fprintf(stderr, "Conflict: Manual resolution required for %s\n", info->path);
    }
}
复制代码

 


4. 冲突处理 (Conflict resolution)

如果三方合并出现冲突,Git 会将冲突区域标记在文件中。例如:

<<<<<<< HEAD
本地修改
=======
远程修改
>>>>>>> branch

 

源码:merge-recursive.c

复制代码
 1 int apply_three_way_merge(struct file_merge_data *data) {
 2     // 调用底层差异计算
 3     diff_result = diff_files(data->base, data->local, data->remote);
 4 
 5     if (diff_result.has_conflict) {
 6         // 如果检测到冲突,将冲突标记写入文件
 7         write_conflict_markers(data);
 8         return -1;
 9     }
10 
11     // 合并成功,写入合并结果
12     write_merged_file(data);
13     return 0;
14 }
复制代码

 


5. 合并提交

当文件成功合并后,Git 创建一个新的提交,将 HEAD 和目标分支的最新提交作为父提交。

源码:commit.c

复制代码
int commit_merge(const char *message, struct commit *head, struct commit *merge) {
    struct commit_list parents;

    // 设置父提交(HEAD 和目标分支的最新提交)
    parents.item = head;
    parents.next = merge;

    // 创建合并提交
    create_commit(message, &parents);
    return 0;
}
复制代码

 

 

总结

  • 共同祖先查找merge-base.c
  • 差异计算diff.c
  • 三方合并算法merge-recursive.c
  • 冲突标记:在 merge-recursive.c 中完成
  • 提交生成commit.c

源码来源:https://github.com/git/git

 

posted on   Love&Share  阅读(77)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示