Git多仓库管理

5

我正在一个项目中工作,我们使用git来管理外部库/头文件和质量保证。以下是每个开发者的目录结构:

~/dev/proj 
~/dev/ext 
~/dev/qa

proj、ext和qa是不同的git存储库。在svn下,这些目录的同步很简单:在~/dev下进行一次更新将递归更新所有内容。但是在git中,我们需要为每个目录单独执行“git pull”。这不太好;总有人会忘记更新其中一个目录(git pull),他的项目将不同步(例如,新的qa无法通过旧代码)。我研究了“git子模块”,但它并没有提供一个单点来同时更新这三个分离的模块。[更正:在此我错了,请阅读我的答案below]。

你可以认为我们应该将proj、ext和qa放在同一个git存储库中,但我认为这违背了将不同概念保留在不同存储库中的git哲学。

除了编写脚本以在~/dev下的每个目录上执行git pull之外,是否有其他解决方案(对于这个微不足道的问题)?

谢谢,

Altan

9个回答

4

我的理念是这样的:如果我总是需要将X和Y放在一起,那么从逻辑上讲它们应该属于同一个仓库。只有在需要适当的隔离时才使用子模块 - 比如外部供应商库,您不希望随意引入更新,也不希望您的团队能够直接编辑它们 - 这是有道理的。但无论如何,这都增加了步骤。对我而言,“如果它是一个项目,就把它放在一个仓库中”,不管我怎样理论上把它分解成更具“Git”风格的形式。


_ext_是外部库,可以与其他项目共享。有时候人们可能想要检出qa而不需要代码[以使用某个发布的代码运行qa]。因此将它们分开保存有益处。 - Dr.Altan
我并不否认这种设置的好处,只是我认为这些好处并不能抵消现实情况带来的影响。 - David Fells

3

亲爱的博士,

你在比较苹果和橙子。git-submodules类似于svn:externals,也就是svn-submodules。事实上,当您使用-r将svn子模块附加到特定版本时,行为几乎相同。要使用svn-submodules进行提交,您必须分别在每个子模块目录中进行提交,就像使用git-submodules一样。

然而有一个很大的区别:大多数开发人员,在开发的某个阶段都喜欢连接到每个子模块的分支,而这在git-submodules中不被支持。这对于协调开发非常有用。(谷歌的Repo工具是Git的包装器,旨在与Gerrit(一种代码审查工具)一起使用,有点类似。但请相信我:远离Repo。它解决了不同的问题。)巨大的缺点是您无法恢复代码库的确切轮廓。这似乎还好,但我听说过一些可怕的故事。

你的替代选择不是Subversion,而是一个单一的代码库,可以使用Git、Subversion或其他工具。但你实际上想要一个单一的代码库和多个仓库的组合,对吗?你想要每个方案的好处,因此你需要更复杂的解决方案。
其中一个想法是有一个主项目代码库,在这里进行大部分开发,另外还有几个独立的仓库,从中分发模块。
proj/.git
proj/subA
proj/subB
subA/.git
subB/.git

你可以使用rsync在它们之间移动代码。美妙的是,你已经清晰地区分了开发和分发。你像平常一样开发你的大型项目,包括分支、合并等。当你准备将子目录作为库分发时,你可以决定想要哪个版本的库,并将其复制到自己的仓库中。如果需要进行合并而不是仅仅复制,可以使用git subtree merge strategy

还有另一个系统,建立在子树合并策略之上。它被称为git-subtrees,是git-1.7.11的一部分。 这里是其操作的良好描述。 从图片中可以看出,它的时间轴可能看起来很混乱,但从功能上讲,它正是您想要的。 这里是最近的一篇文章,提供了极好的建议。

如果您不介意使用git-submodules的额外“更新”步骤,但对其处理冲突的方式感到不满,可以尝试giternal。 作者已经包含了一个脚本,以显示其行为与git-submodules和braid(用于售卖子模块,但不合并它们)相比较。

个人而言,我喜欢git-slave,它是一个简单的git包装器。基本上,它将你的gits命令应用到所有的repo中作为git命令。这只是一种便利方式。它非常易于理解,对各个repo没有任何影响,并且非常适合分支切换(这在git-subtrees中尚未得到支持)。


顺便提一下,git-submodules有不同的目的:处理一堆不同应用程序中使用的库的依赖版本控制。您的构建系统可以使用项目级别的存储库。我的公司有数千个项目和数万个库,有类似但更高级的东西。但这不是你所面临的问题。对于你的问题,我个人喜欢git-submodules的清晰度,但我也理解其中的困难。 - cdunn2001
1
git-slave 正是我正在寻找的。感谢你向我指出它。 - Dr.Altan
虽然 git-slave 保持简单,但我认为一个好的组合是使用 git submodule foreach <some-command>。事实上,您可以配置子模块,以便 git submodule update 实际上合并(或变基!)所有分支。 - cdunn2001

2

我们尝试过使用“git submodule”,但效果并不令人满意。看起来,“git submodule”是为很少改变的模块设计的。以下是更改和推送任何模块的步骤:

cd ~/dev/proj
git checkout master
git pull
... make changes to your files ...
git commit -a -m "comment"
git push
cd ..   
git commit -a -m "comment"
git push

每个~/dev目录下的模块都需要重复这个步骤。抱歉,但我认为这很荒谬。在svn中,同样的事情可以通过以下方式实现:

cd ~/dev
svn commit -m "done in one line"

我了解git相比svn的优势,但是由于缺乏适当的子模块支持和较差的大文件支持,我们可能会从git转到svn(除非我们在这里找到一个解决方案---我宁愿留在git)。老实说,我很惊讶git完全没有涉及到这一点..不同的项目经常共享共同的模块[即实时模块]。
我反对将proj、ext和qa放在同一个仓库中,因为:
  • ext将与其他项目(仓库)共享
  • qa应该能够在没有代码的情况下被检出(克隆)
Altan

2
你仍然可以使用子模块。 git submodule update 将一次性更新所有子模块。


1

git-multi是答案。 https://github.com/grahamc/git-multi

设置好git-multi,并在“~/dev”文件夹下克隆所有所需的代码库。

从“~/dev”运行“git multi pull”或“git multi status”等命令,它会在所有子代码库中运行相应的命令。


0

我曾面临同样的问题并编写了一个程序(bash脚本)来解决:gws

大致的思路如下:

  1. dev/.projects.gws 中创建项目路径和 URL 列表:

    work/proj  | https://...
    perso/ext  | git@github.com:...
    perso/qa   | https://...
    
  2. 使用以下其中一个 gws 命令:
    • init:用于自动从当前文件夹树中的现有存储库创建 .projects.gws 文件。
    • update:克隆缺失的本地存储库,例如当在 .projects.gws 中添加项目时。
    • status:显示所有存储库的状态(干净、未跟踪的文件、未提交的更改等)。
    • fetch:在所有存储库中执行 git fetch(然后如果在之间修改了源存储库,则 status 将能够检测到差异)。
    • ff:在所有存储库中执行 git fast-forward pull
    • check:验证工作区的状态(已知、未知、工作区中缺少的存储库)

然后,可以使用 git 版本管理 .projects.gws 文件,并在许多计算机上使用它(工作、家庭、笔记本电脑等)。还可以编写一个 .ignore.gws 文件来忽略一些本地路径和 regexp,例如,在家用电脑上使用 ^work/.* 忽略工作文件夹中的存储库。

更多信息请参见 Readme

我每天都使用它,它满足了我的需求(也许也适合你的需求)。请注意,我打算在有时间的时候使用 Python 重写它(另起一个名字)。原因是代码在 bash 中变得难以管理,并且我想添加更多的功能(例如添加对 mercurial、darcs 等的支持)。


0
我编写了一个名为gita的命令行工具来管理多个代码库。它可以将已注册的代码库状态并排显示,并且还可以从任何工作目录委派Git命令/别名。例如:

enter image description here

颜色和额外的符号显示本地/远程关系和编辑状态。

如果您有许多存储库,可以将它们分组到不同的组中并对组进行操作,或者将上下文设置为组。


-1

在我看来,子模块是这里的最佳选择。

与其问自己是否总是需要 X 和 Y 一起,不如问自己是否总是希望 X 和 Y 的完全相同版本一起使用。

Git 子模块为您提供了这个非常强大的工具,可以快速修复 X 中的错误,而无需同时更新 Y。

例如,如果您正在开发在不同操作系统上运行的产品(比如 Mac OS X 和 Windows),那么将操作系统特定代码放入单独的子模块中可能是有意义的。特别是如果不同的人在这些不同的操作系统端口上工作。使用 git 子模块允许您轻松地向客户部署一个操作系统的修复程序,而无需在其他操作系统上经过 QA 流程。

另一个非常强大的用例是“工作区”模块。您只需简单地添加一些本地模块(例如 /Workspace),然后添加所有您正在使用的依赖项。

Git子模块的好处在于它不仅记录了你使用的模块,还记录了它们的具体版本。在修复错误时,我经常需要测试某些依赖项的特定版本 - Git子模块允许我轻松地将这些记录在我的工作区模块的历史记录中,以便稍后轻松返回到该确切状态。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接