跨仓库应用Git提交的方法

由Jeza Chen 发表于 November 5, 2025

最近在处理公司项目时,遇到需要将一个Git仓库中的某些提交应用到另一个仓库中的情况。由于是两个分离的仓库,直接使用git cherry-pick命令并不可行。经过一番研究,发现可以通过导出补丁文件并应用到目标仓库来实现这一需求。本文分享具体的操作步骤和注意事项。

太长不看

  • 在源仓库中,使用git format-patch命令将所需要的提交导出成补丁文件。如果需要将多个提交合并成一个补丁文件,可以使用git format-patch -<n> HEAD --stdout > <patch-file>命令。

  • 在目标仓库中,使用git apply或者git am命令将补丁文件应用到目标仓库。如果两个仓库的目录结构不一样,可使用-p<n>选项和--directory选项来调整路径。比如,将仓库A中关于文件src/path/main.cpp的修改应用到仓库B中对应的文件Source/path/main.cpp,可以使用如下命令:

git apply <patch-file> \ 
    -p2 \   # remove 2 leading directory ("a/src/") from the path in the patch
    --directory="TargetDirectory"  # prepend "Source/" to the path

适用场景

  • 两个仓库A, B中具有相同名字的文件,且文件的内容大致相同,希望能将仓库A中对此文件的修改”cherry-pick”到仓库B中(当然,cherry-pick只能用于同一仓库的不同分支中,并不支持跨仓库提交)

  • 两个仓库具有几乎一致的源码,希望能将仓库A的某个commit应用在仓库B中。这种情况一般发生在工程结构的重构,比如将一个仓库内的某个目录抽出来,单独作为一个新的仓库来维护。但前面的仓库因为某种历史原因,该目录仍未删除,形成了同一份代码(或工程),两个仓库并行维护的状态。因此在开发的过程中,需要往两个仓库提交几乎一致的代码。

具体过程

示例仓库结构

一个示例仓库为例,我们先用git clone --recursive https://github.com/JezaChen/ApplyPatchAcrossRepoSample将其clone到本地。里面有两个子仓库”repoA”以及”repoB”,其中repoA的目录结构如下:

repoA/
  ├── src/
      └── path
            └── main.cpp

repoB的目录结构如下:

repoB/
  ├── src/
      └── Source
            └── main.cpp

repoA里面的main.cpprepoB里面的main.cpp内容大致相同。除了第8行所传入的参数不一样,其他内容是相同的。

输出补丁

我们对repoA的main.cpp进行改动,切去新的分支dev,在第一个提交(da93915)中,将add函数修改成

int add(int a, int b) {
    int sum = a + b;
    std::cout << "The sum is: " << sum << std::endl;
    return sum;
}

然后,在第二个提交(7669cbd)中,给add函数新增一行注释

// This function adds two integers and returns the result
int add(int a, int b) {
    int sum = a + b;
    std::cout << "The sum is: " << sum << std::endl;
    return sum;
}

如果我们想将这两个提交应用到repoB中,要怎么做呢?最直观的想法是使用cherry-pick,但cherry-pick只能用于同一仓库的不同分支中,并不支持跨仓库提交。这时候,git的补丁机制就派上用场了,我们可以将这两个提交导出成补丁文件,然后再将补丁文件应用到repoB中。

使用git format-patch命令导出补丁文件。在dev分支上,使用以下命令导出最近两次的提交:

git format-patch -2 HEAD

不幸的是,执行之后,会发现这个命令生成了两个补丁文件——能否将其合并成一个呢?当然可以!我们可以通过--stdout选项将其打印到终端上,然后重定向一个文件里(在例中,我们命名为my.patch):

git format-patch -2 HEAD --stdout > my.patch

最后,我们将所输出的my.patch文件移动到repoB里。

应用补丁

补丁有了,怎么应用到目标仓库(repoB)里面呢?我们不难想到git apply命令,但直接应用git apply my.patch命令会报错,毕竟两个仓库的目录结构是有区别的。

还好,Git提供了灵活的选项——我们可以利用-p<n>--directory来fix上述的问题:

git apply my.patch -p2 --directory="Source"

其中,-p<n>选项用于去掉补丁文件中路径的前n个目录层级,在本例中,补丁文件中的路径是a/src/path/main.cpp(注意补丁文件里面有个a/),我们需要去掉前两个目录层级a/src/,使其变成path/main.cpp

--directory="Source"选项则是告诉git apply命令,将补丁应用到当前目录下的Source目录中。最终,我们应用补丁的路径就是Source/path/main.cpp,正好对应上了repoB中的main.cpp

命令执行完毕后,我们可以使用git status命令查看repoB的状态,发现main.cpp文件已经被修改了,且改动的内容是一致的!

除了上述的git apply命令外,我们也可以使用git am命令来应用补丁文件,命令如下,可以发现与git apply命令类似:

git am my.patch -p2 --directory="Source"

两者的区别在于,git am应用补丁后会自动帮你提交,甚至能保留源仓库中的提交信息(比如作者、提交时间、提交说明等);而git apply则只会应用补丁的内容,不会保留原有的提交信息。