Submoduliai
Git submoduliai įterpia vieną saugyklą į kitą. Pagrindinės saugyklos medyje submodulis pateikiamas kaip gitlink įrašas (režimas 160000), nurodantis konkretų commit submodulio saugykloje. Submodulio URL ir kelias deklaruojami faile .gitmodules.
Sąvokos
Pagrindinės saugyklos medyje:
100644 blob abc123... README.md
040000 tree def456... src/
160000 commit 789012... lib/external <-- gitlink entry (submodule)
Gitlink įrašas nurodo, kurį submodulio commit reikia išsiregistruoti. Pati submodulio saugykla yra .git/modules/<name>/.
SubmoduleManager
use Pitmaster\Submodule\SubmoduleManager;
$manager = new SubmoduleManager(
$repo->objectDatabase(),
$repo->workDir(),
$repo->gitDir(),
);
Submodulių sąrašas
$submodules = $manager->list();
foreach ($submodules as $sub) {
echo "{$sub->name}: {$sub->url} at {$sub->path}\n";
if ($sub->branch !== null) {
echo " tracks branch: {$sub->branch}\n";
}
}
Submodulio reikšmės objektas
use Pitmaster\Submodule\Submodule;
$sub->name; // Submodule name (from .gitmodules section header)
$sub->path; // Path in the working tree
$sub->url; // Remote URL
$sub->branch; // Tracked branch (nullable)
.gitmodules analizė
Faile .gitmodules deklaruojama submodulio konfigūracija:
[submodule "lib/external"]
path = lib/external
url = https://github.com/vendor/lib.git
branch = main
[submodule "tools/helper"]
path = tools/helper
url = https://github.com/vendor/helper.git
$content = file_get_contents('.gitmodules');
$submodules = SubmoduleManager::parseGitmodules($content);
foreach ($submodules as $sub) {
echo "{$sub->name} -> {$sub->url}\n";
}
Analizatorius apdoroja:
- Sekcijų antraštes:
[submodule "<name>"] - Rakto ir reikšmės poras:
path = ...,url = ...,branch = ... - Komentarus (eilutes, prasidedančias
#) - Tarpus ir įtraukas
Įrašai, kuriuose trūksta path arba url, praleidžiami. Reikšmės nuo kabučių neatlaisvinamos.
Gitlink įrašai
Gitlink įrašai yra medžio įrašai su režimu 160000. Jie nurodo commit (ne medį ar blob) submodulio saugykloje.
$tree = $repo->readObject($treeHash);
foreach ($tree->entries as $entry) {
if ($entry->isGitlink()) {
echo "Submodule at {$entry->name}: commit {$entry->hash->hex}\n";
}
}
TreeEntry aptikimas
$entry->isGitlink(); // true if mode is '160000'
$entry->mode; // '160000'
Submodulių inicijavimas
$manager->init();
Kiekvienam .gitmodules deklaruotam submoduliui:
- Sukuriamas tuščias
.git/modules/<name>/katalogas - Sukuriamas submodulio kelio katalogas, jei jo nėra
.git/configįrašomisubmodule.<name>.active = trueirsubmodule.<name>.url
Nieko neatsisiunčiama ir neišsiimama į darbinį katalogą, taip pat neįrašomas submodulio .git failas.
Submodulių atnaujinimas
$manager->update($repo->head()->tree);
Kiekvienam nurodytame medyje prisegtam submoduliui update() nukopijuoja šaltinio saugyklos git katalogą į .git/modules/<name>/, pakeičia submodulio darbinius failus šaltinio failais, įrašo .git failą su nuoroda į modulio katalogą, nustato core.worktree ir atjungia HEAD ties prisegtu pakeitimu.
Submodulio URL turi būti vietinis kelias: absoliutus arba santykinis tėvinio darbinio medžio atžvilgiu (git santykinius URL išsprendžia pagal tėvinės saugyklos nuotolinį URL). URL su schema (https://...) sukelia RuntimeException('Submodule update currently supports only local paths: ...').
Atnaujinę galite atidaryti kiekvieną submodulį kaip atskirą saugyklą:
$subRepo = Pitmaster::open($repo->workDir() . '/lib/external');
Prisegtų pakeitimų tikrinimas
$headCommit = $repo->head();
$pinnedId = $manager->pinnedCommit('lib/external', $headCommit->tree);
if ($pinnedId !== null) {
echo "lib/external is pinned to commit {$pinnedId->hex}\n";
}
Metodas pinnedCommit() pereina medžio hierarchiją, randa gitlink įrašą nurodytame kelyje ir grąžina jo tikslinio pakeitimo maišą.
Submodulio būsena
$headCommit = $repo->head();
$statuses = $manager->status($headCommit->tree);
foreach ($statuses as $status) {
$expected = $status['expected'] ?? 'not pinned';
$actual = $status['actual'] ?? 'not initialized';
$dirty = $status['dirty'] ? ' (dirty)' : '';
echo "{$status['name']}: expected={$expected}, actual={$actual}{$dirty}\n";
}
Būsenos laukai:
name: submodulio pavadinimaspath: kelias darbiniame medyjeexpected: tėviniame medyje prisegto pakeitimo maišas (null, jei jo nėra)actual: dabartinis submodulio HEAD (null, jei neinicijuotas)dirty: true, kai abu pakeitimai žinomi ir skiriasi. Submodulio darbinio medžio pakeitimai netikrinami.
Katalogų struktūra
project/
.git/
modules/
lib/
external/ # Submodule's actual git directory (.git/modules/<name>)
HEAD
objects/
refs/
...
.gitmodules # Submodule declarations
lib/
external/
.git # File: "gitdir: ../../.git/modules/lib/external"
src/
README.md
src/
README.md
Darbas su submoduliais
// 1. Open the parent repo
$repo = Pitmaster::open('/path/to/project');
// 2. List submodules
$manager = new SubmoduleManager(
$repo->objectDatabase(),
$repo->workDir(),
$repo->gitDir(),
);
$submodules = $manager->list();
// 3. Populate the submodules pinned at HEAD (local source paths only)
$manager->update($repo->head()->tree);
// 4. Check status
$statuses = $manager->status($repo->head()->tree);
// 5. Open a submodule as its own repo
$subRepo = Pitmaster::open($repo->workDir() . '/lib/external');
$subHead = $subRepo->head();
echo "Submodule HEAD: {$subHead->id->hex}\n";