我有一个包含子模块main
的项目foo
。对于此特定项目,我想对仅适用于此特定项目foo
的{{1}}进行少量更改。
main
一个常见的解决方案是转到我的子模块,在名为main/
+ .git
+ main.c
+ lib/
| + bar.c
+ foo/ # My `foo` submodule
+ .git
+ config.h # The file I want to patch from `main`
+ ...
的新分支上进行提交Applied patch for main
,然后推送它。不幸的是,这是一种非常糟糕的方法,因为我对main-project
进行的更改只对foo
有意义。此外,当我将main
更新为最新版本时,我将不得不挑选补丁,这会在foo
的历史中引入很多噪音。
另一个解决方案是在foo
上有一个真正的补丁文件,该文件在构建之前应用于main
。不幸的是,由于这会修改子模块内容,并且我将在foo
上更改未提交,因此它也不是一个好的解决方案。
理想的解决方案是使用Git跟踪我的补丁,但是在顶层(例如直接在foo
上,而不是在main
上)。从理论上讲,可以在指向子模块位置的Git foo
上添加blob
:
tree
有了这个想法,将在blob <sha> main.c
tree <sha> lib/
commit <sha> foo
blob <sha> foo/config.h
上跟踪属于config.h
的修补文件foo
。
怎么可能这样做?
答案 0 :(得分:6)
在供应商历史记录上携带特定于项目的补丁的最简单方法是克隆供应商存储库,将更改作为项目分支进行携带,并将该克隆广告作为.gitmodules
上游发布。
这使您对供应商上游项目所做的更改完全正常,git clone --recurse-submodules yourproject
可以正常工作,您对子模块的更改可以被推回到您对项目子模块的上游(子模块存储库的origin
远程),一切正常。
唯一的补充信息是,将项目的子模块版本更新为最新的供应商代码,某些人必须从(上游)供应商仓库中获取并合并
...但是那也很普通:从供应商仓库中获取并合并的方法就是这样做。 git remote add vendor u://r/l; git fetch vendor; git merge vendor/master
。或者,如果您希望合并基准,请执行此操作。完成此操作后,照常将结果推送到子模块的origin
,即项目的版本。
答案 1 :(得分:4)
理想的解决方案是使用Git跟踪我的补丁,但在顶层 (例如,直接位于main而不是foo上)。从理论上讲, 在Git树上添加一个指向子模块位置的blob:
即使这样做可行,我个人还是觉得很困惑。除非这是 本机实现,并包括易于使用的面向用户的命令 了解和管理,我会远离它。
是否有更优雅/精简的方式在git中打补丁 子模块,而不是可接受的答案?
如果您想完全避免与子模块混淆,我建议
复制/检出其他地方的工作树并仅使用
在构建期间。这样,子模块始终是“干净的”(也许
从main
的角度来看,“不可变”),您只需要担心它
在构建目录中。
简化的构建过程示例:
cd main
mkdir -p build
cp -R foo/ build/
cp myconfig.patch build/
cd build
patch <myconfig.patch
make
请注意,这仅生成foo
,并且main
的生成过程不需要
除了必须指向build/
而不是foo/
之外,还需要进行更改。
如果您不打算自己修改foo
,而是将其保留为“原始”,
您也可以将其变成一个裸仓库并使用
GIT_WORK_TREE="$PWD/build" git checkout HEAD
而非cp
,因此它是
仅在构建期间签出。这类似于
makepkg(8)(至少以我对AUR的经验)按顺序进行
以避免修改原始来源($source
数组与$srcdir
)。它
还从构建本身中拆分了源检索(prepare()
与build()
)。
另请参见PKGBUILD(5)和Creating packages。
在您的情况下,还涉及开发和IDE,因此可能会比较棘手
如果要一次检查原始文件和构建文件。
专家:
main
不影响foo
缺点:
如果您的补丁很小和/或非常针对main
,我会选择这个。
P.S。:可以更进一步并跟踪foo
的版本
直接在构建过程中使用,而不是使用子模块:
将foo
移至一个目录,然后在构建过程中:
cd build
GIT_DIR='../../foo/.git' git checkout "$myrev"
patch <myconfig.patch
make
此外,当我将foo更新到最新版本时,我将不得不选择 补丁也给foo的历史带来了很大的干扰。
您不必真正挑选它,只需将更改保留在其中即可。
而不是您的分支,并偶尔合并 master
。
我个人会避免这种情况,除非您的更改更为重要 比由于保持同步而产生的噪音(即:合并和冲突)。 我发现合并提交非常不透明,尤其是在涉及冲突时, 因为无关或意外的变化更难检测。
您也可以 重新设置您对master
的提交。
专家:
缺点:
foo
的存储库进行不相关的提交(合并时)foo
的存储库进行不相关的提交对象污染(重新设置基准时)config.h
所做更改的演变的模糊历史记录(重新定基时)此外,当我将foo更新到最新版本时,我将不得不选择 补丁也给foo的历史带来了很大的干扰。
不幸的是,这是一个非常糟糕的方法,因为我正在对foo进行更改 才是主要的
如果您想更改foo
以适合main
,但又不想与上游的foo
发生冲突,
为什么不创建foo
的软叉?如果你不在乎
foo-fork
的历史记录,您只需在main-project
上进行更改
分支,并通过重新设置使其与foo
的{{1}}保持同步:
创建叉子:
master
保持同步:
cd foo
git remote add foo-fork 'https://foo-fork.com'
git branch main-project master
git push -u foo-fork main-project
专家:
git checkout main-project
git pull --rebase foo/master
# (resolve the conflicts, if any)
git push foo-fork
同步)缺点:
pull --rebase
所做更改的演变的模糊历史记录(由于
重新定标)使用补丁而不是重新定基础的附加好处是您可以保留 补丁程序的历史记录。但是,如果您想让事情保持真正简单的同步, 我想这就是方法。
如果您发现config.h
的更改太多/太频繁和/或您也需要修补
很多事情,您最好的选择可能是创建完整的叉子和采摘樱桃
而是他们的更改。
答案 2 :(得分:2)
您可能会发现,使用git子树而不是子模块更有意义。子树将另一个项目复制到本地存储库中的某个位置,而不是将其作为当前存储库中的单独存储库进行管理。
您可以将更改作为主master分支的一部分本地应用,然后定期更新,并从上游合并更改。
答案 3 :(得分:1)
我仍然会选择第二个选项(在main上有一个真正的补丁文件),但我的构建过程适应:
config.h
config.h
恢复为原始内容。这样,我保持子模块状态不变。
OP在评论中添加:
但是你的解决方案不能在IDE中运行,Intellisense会感到困惑 -
正确:为此,我会在结账时自动应用补丁,并在检查时通过smudge/clean content filter driver将其删除。 这样,补丁在整个会话期间保持不变,但在任何git status / diff / checkin上都会消失。
虽然这并不理想,但似乎没有一种原生的Git方法可以解决这个问题。