我该如何在使用Composer加载的库中使用Git子模块?

33

我有一个Zend Framework 2应用程序。它包含一些包含业务逻辑和其他实用工具的库代码,这些工具将在以后创建的其他应用程序中通用。

我的意图是使用Composer在项目之间共享它。问题是,如何正确地做到这一点并简化开发流程?我几乎肯定需要从其他项目中对库进行修改和添加。

我尝试设置vendor/stuff作为包含所需软件包的git子模块,并像这样在主要的composer.json中引用它:(ref):

"repositories": [
    {
        "type": "git",
        "url": "vendor/stuff"
    }
],
"require": {
    "stuff/library": "master"
},

Composer无法以这种方式加载它。它抱怨找不到包,可能是因为它忽略了URL既是本地又是相对的事实。 从技术上讲,它不需要这样做;vendor/stuff文件夹是通过git子模块命令单独初始化的。

4个回答

51

很遗憾,Composer不支持Git子模块,因为Composer的主要目的是提供类似于项目间依赖的功能,并且尝试在Composer中复制子模块将是毫无意义的。

我有和你一样的问题,即在开发使用库的应用程序的同时开发该库。有几种方法可以仅使用composer解决该问题。

为库目录创建符号链接

这是最快且最简单的方法。仅需要运行composer update命令,就可以在您的vendors目录中创建适当的库目录,然后将其替换为包含您的库的目录的符号链接。

显然,这并不好,因为您可能会通过运行composer update命令意外地覆盖您可能已经编辑过的代码。

使用Composer偏好源选项

Composer有通过Git克隆(--prefer-src)下载源代码而不是下载zipball(--prefer-dist)的选项,后者是默认设置。这使您可以在vendors目录中编辑源代码,然后通过Git提交它。

例如,假设您有一个需要symfony/yaml等其他库的项目,并且您想要修复其中的错误。您可以执行以下操作:

  1. composer update - 这将下载项目的所有依赖项。

  2. composer update symfony/yaml --prefer-source - 这将仅更新vendors目录中symfony/yaml目录。

  3. 修复错误,然后通过git提交它。

使用Composer本地存储库

我实际上在同时开发项目和其要求时使用的方法是,使用Composer明确设置要使用的存储库来解决依赖关系。例如,如果您的代码位于:

/projects/library/
/projects/project/
在您的项目的composer文件中添加存储库条目:
"repositories": [
    {
        "type": "vcs",
        "url": "/projects/library/"
    }
]

运行composer update现在会查看/projects/library/中的Git条目来解决对库的任何依赖关系,而不是优先于Packagist或其他存储库中列出的依赖关系。

这意味着当您想要测试库代码中的更改时,您需要:

  1. 提交更改以生成Git条目。

  2. 在项目目录中运行Composer update以获得最新版本。

但是,这避免了将提交推送到外部存储库,这很好,因为它意味着您不会推送可能不起作用的代码,并且它意味着您可以离线工作,因为Git提交不需要互联网连接。


虽然这显然是最佳的工作方式,但仍然有点危险,因为很容易意外地检入包含对本地目录的引用的composer.json版本,这显然会使项目对其他人都无法使用。

为了避免这种情况,我写了几个小脚本:i)备份我的真实composer.json文件,ii)添加一些本地存储库,iii)运行 composer update iv)恢复真实的composer.json文件。

localupdate.sh

cp -f composer.json composer.json.bak
php composerLocal.php
composer update
cp -f composer.json.bak composer.json

composerLocal.php

<?php

$srcFile = file_get_contents("composer.json");
$hackFile = file_get_contents("composer.local");
$finalString = str_replace('"LOCALHACK",', $hackFile, $srcFile);
file_put_contents("composer.json", $finalString);

?>

本地 Composer

"LOCALHACK",

"repositories": [
    {
        "type": "vcs",
        "url": "/projects/library1"
    },
    {
        "type": "vcs",
        "url": "/projects/library2"
    }   
],

然后在您项目的 composer.json 文件中的某处放置 "//": "LOCALHACK",。现在运行 localupdate.sh 将会安全地针对本地存储库执行 composer update,而不会有任何提交错误版本的 composer.json 的机会。

使用 Git 自己克隆它

这是我现在的工作方式:

i) 在项目中更新 Composer ii) 进入 vendors 目录并删除我要同时开发的库。 iii) 从要开发该库的 repo 克隆 Git 到相应的 vendors 目录。

Composer 理解 git repo,所以不会覆盖已经克隆的 git 目录(尽管它似乎对编辑库的 composer.json 有点困惑)。

自己进行 git clone,可以完全控制安装内容,并允许您从 Composer 不知道的 repo 或未标记版本中安装,而无需编辑项目中的 composer.json。

这是自己进行 git clone 的关键功能;通过不触及项目的 composer.json,它是完全安全的,没有可能检查到已修改为使用本地/定制 repo 的 composer.json。

  • 编辑-2014年9月6日

composer.json 文件的验证已经变得更加严格,不再可能在文件中有 "//": "LOCALHACK" 条目。这是 Composer 团队不支持 Composer 项目版本控制的另一个原因,这很荒谬。

* 我实际上认为 Git 子模块是一种愚蠢、愚蠢、愚蠢的实现方法,以“解决”一种只会在路上造成更多问题的困难问题,所以 Composer 不支持它们比“不幸”更“幸运”。显然其他人确实使用它们,并且对它们感到满意,所以这只是我的观点,而如果您正在使用 Composer,就不应该需要子模块。


2
这个点“使用Composer首选源选项”解决了我在转换到Composer时的困惑。感谢您详细回答问题! - JohnnyQ
我在使用全局安装安装Composer的依赖项时遇到了问题,因为它将文件的所有权设置为root,我的IDE没有权限进行编辑。我通过将Composer移动到~/bin/composer并将~/bin包含在我的PATH中来解决这个问题。 - Corie Slate
我最终做了类似于LOCALHACK的东西,只不过它是一个脚本,将JSON合并并针对临时文件触发composer。 (https://github.com/DHager/composer-haydn) - Darien
@Danack 如果一个功能涉及到5个或更多的包,你如何获得完整的变更图片呢? - bilogic
@Danack,实际上在昨天发布后,我找到了一个相当不错的解决方案:我编写了一个扫描器,扫描整个“vendor”文件夹以查找具有(1)修改、(2)暂存、(3)未跟踪文件的git仓库,并让我的Git客户端带出所有仓库+项目本身,非常有效,但我会和你聊一下,谢谢 :) - bilogic
显示剩余3条评论

2
Composer有一个自动加载映射功能,如果您的库遵循PSR-x标准,这个功能非常有用,也可以通过类似的过程自动加载非PSR-x库。

参考链接: https://getcomposer.org/doc/04-schema.md#autoload

让我们举个例子(假设您的库遵循PSR-4标准)

假设您已经将子模块克隆到了lib/your-private-git-repo文件夹中。

您所需要做的就是在composer.json文件的自动加载部分中添加:

{
    "name": "YourNamespace/YourApplicationName",
    "description": "Describe your library",
    "license": "Your licen",
    "keywords": ["some","keywords"],
    "authors": [
        {
            "name": "Adamo Aerendir Crespi",
            "email": "hello@aerendir.me"
        }
    ],
    "require": {
        "joomla/framework": "*@stable"
    },
    "require-dev": {
        "phpunit/phpunit": "*@stable"
    },
    "autoload": {
        "psr-4": {
            "YourNamespace\\YourSubmodule\\": "lib/your-private-git-repo/src"
        }
    }
}

请注意代码底部第六行的内容。
    "autoload": {
        "psr-4": {
            "YourNamespace\\YourSubmodule\\": "lib/your-private-git-repo/src"
        }
    }

现在更新Composer。
php composer.phar update

以这种方式,Composer会更新包括子模块在内的自动加载文件。
全部完成:现在你的子模块已经被Composer自动加载。
希望这可以帮到你。

1
这不是提问者的问题。问题在于,如果你运行 composer install,它不会安装子模块中 composer.json 中提到的可能依赖项。 - Adam

2

期望的行为:

  • 您有两个 Zend 项目:project_Aproject_B
  • 这两个项目都使用名为 library_C 的相同公共库
  • library_C 通过 composer 被要求从 project_Aproject_B 引用
  • library_C 应该可以在 project_Aproject_Bvendor 文件夹中进行编辑,并且您应该能够推送这些更改
  • 您的库的 git URL 是 https://github.com/stuff/library_C.git

如何做到这一点:

  1. 将您的库放置在 project_Aproject_Bcomposer.json
"require": {
    "stuff/library_C": "master"
},

2.运行 composer install。现在你的库位于文件夹vendor/stuff/library_C中。

3.现在如果你想要编辑 project_A 或者 project_Blibrary_C 中的内容,删除文件夹 vendor/stuff/library_C 并且输入以下命令:

git submodule add -f https://github.com/stuff/library_C.git vendor/stuff/library_C

这个命令将重新创建这个文件夹vendor/stuff/library_C,但是该文件夹现在是另一个git repo(子模块)。
4.进入文件夹vendor/stuff/library_C,对其进行任何更改并提交和推送,就完成了!
之后,当您在其他项目中运行composer update stuff/library_C时,代码将被更新。
注意:在运行git submodule add之后,您将处于主分支上,因此必须切换到正确的分支或标签并在那里进行更改。

0
如果您的项目A包含一个git子模块,那么从项目A调用composer install/update..将忽略您的子模块中的所有composer依赖项。
简而言之,如果它包含composer依赖项,请不要使用git子模块。
这是我更喜欢的做法:
1)如果它不是官方软件包(如fork或私有存储库),请将存储库添加到composer.json
"repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/vendor_name/package_name.git"
    }
],

2) 使用--prefere-source选项安装包:

composer require vendor_name/package_name --prefer-source

注意:如果你将official/mango fork到自己的存储库ralf/mango中,那么你需要在composer.json中指向ralf/mango,但是调用composer require official/mango ..`。你可以在docs中阅读详细信息。
现在,你可以从vendor中访问文件并进行推送和拉取等操作。 --prefere-source命令已经在vendor文件夹中设置了git连接。
如果你想从根目录访问包,例如package/mango而不是vendor/official/mango,你可以设置符号链接。

如果我cd ./vendor/vendor_name/package_name,然后在那里提交更改并带有标签推送它们,那么它们会被推送到“vcs”存储库定义的存储库上吗? - undefined
@DimitriosDesyllas 是的,如果你修改了 vendor 文件夹中你的包内的字段,你可以将它们推送,它们将会在你的包的代码库中得到更新。 - undefined

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