Podmoduly
Podmoduly Git vkladajú jeden repozitár do druhého. V nadradenom strome je podmodul zastúpený záznamom gitlink (režim 160000), ktorý odkazuje na konkrétnu revíziu v repozitári podmodulu. URL a cesta podmodulu sú uvedené v súbore .gitmodules.
Základné pojmy
V strome nadradeného repozitára:
100644 blob abc123... README.md
040000 tree def456... src/
160000 commit 789012... lib/external <-- gitlink entry (submodule)
Záznam gitlink určuje, ktorá revízia podmodulu sa má prevziať. Samotný repozitár podmodulu sa nachádza v .git/modules/<name>/.
SubmoduleManager
use Pitmaster\Submodule\SubmoduleManager;
$manager = new SubmoduleManager(
$repo->objectDatabase(),
$repo->workDir(),
$repo->gitDir(),
);
Zoznam podmodulov
$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";
}
}
Objekt hodnoty podmodulu
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)
Analýza súboru .gitmodules
Súbor .gitmodules obsahuje konfiguráciu podmodulov:
[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";
}
Analyzátor spracuje:
- Hlavičky sekcií:
[submodule "<name>"] - Dvojice kľúč – hodnota:
path = ...,url = ...,branch = ... - Komentáre (riadky začínajúce sa znakom
#) - Biele znaky a odsadenie
Položky, ktorým chýba path alebo url, sa vynechajú. Hodnoty sa neodstraňujú z úvodzoviek.
Záznamy gitlink
Záznamy Gitlink sú záznamy stromu s režimom 160000. Odkazujú na commit (nie na strom ani blob) v úložisku submodulu.
$tree = $repo->readObject($treeHash);
foreach ($tree->entries as $entry) {
if ($entry->isGitlink()) {
echo "Submodule at {$entry->name}: commit {$entry->hash->hex}\n";
}
}
Detekcia TreeEntry
$entry->isGitlink(); // true if mode is '160000'
$entry->mode; // '160000'
Inicializácia submodulov
$manager->init();
Pre každý submodul deklarovaný v .gitmodules:
- Vytvorí prázdny adresár
.git/modules/<name>/ - Vytvorí adresár na ceste k submodulu, ak ešte neexistuje
- Zaznamená
submodule.<name>.active = trueasubmodule.<name>.urlv.git/config
Nič nenačítava ani nevykonáva checkout a nezapisuje súbor .git submodulu.
Aktualizácia submodulov
$manager->update($repo->head()->tree);
Pre každý submodul pripnutý v zadanom strome skopíruje update() adresár git zdrojového úložiska do .git/modules/<name>/, nahradí pracovné súbory submodulu súbormi zo zdroja, zapíše súbor .git s odkazom na adresár modulu, nastaví core.worktree a odpojí HEAD na pripnutom commite.
Cesta k submodulu URL musí byť lokálna: absolútna alebo relatívna ku koreňu pracovného stromu nadradeného úložiska (git namiesto toho rozpoznáva relatívne URL vzhľadom na vzdialené úložisko nadradeného úložiska URL). Hodnota URL so schémou (https://...) vyvolá RuntimeException('Submodule update currently supports only local paths: ...').
Po aktualizácii môžete každý submodul otvoriť ako samostatné úložisko:
$subRepo = Pitmaster::open($repo->workDir() . '/lib/external');
Kontrola pripnutých commitov
$headCommit = $repo->head();
$pinnedId = $manager->pinnedCommit('lib/external', $headCommit->tree);
if ($pinnedId !== null) {
echo "lib/external is pinned to commit {$pinnedId->hex}\n";
}
Metóda pinnedCommit() prechádza hierarchiu stromu, aby našla záznam gitlink na zadanej ceste, a vráti hash cieľového commitu.
Stav submodulu
$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";
}
Polia stavu:
name: názov submodulupath: cesta v pracovnom stromeexpected: hash commitu pripnutého v nadradenom strome (null, ak chýba)actual: aktuálny HEAD submodulu (null, ak nie je inicializovaný)dirty: true, ak sú známe oba commity a líšia sa. Zmeny v pracovnom strome submodulu sa nekontrolujú.
Štruktúra adresá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
Práca so submodulmi
// 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";