Undermoduler
Git undermoduler bäddar in ett arkiv i ett annat. En undermodul representeras i det överordnade trädet som en gitlink-post (läge 160000) som pekar på en specifik commit i undermodulens arkiv. Undermodulens URL och sökväg anges i filen .gitmodules.
Begrepp
I det överordnade arkivets träd:
100644 blob abc123... README.md
040000 tree def456... src/
160000 commit 789012... lib/external <-- gitlink entry (submodule)
Gitlink-posten anger vilken commit i undermodulen som ska checkas ut. Själva undermodularkivet finns i .git/modules/<name>/.
SubmoduleManager
use Pitmaster\Submodule\SubmoduleManager;
$manager = new SubmoduleManager(
$repo->objectDatabase(),
$repo->workDir(),
$repo->gitDir(),
);
Lista undermoduler
$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";
}
}
Undermodulens värdeobjekt
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)
Tolka .gitmodules
Filen .gitmodules anger undermodulens konfiguration:
[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";
}
Parsern hanterar:
- Sektionsrubriker:
[submodule "<name>"] - Nyckel-värde-par:
path = ...,url = ...,branch = ... - Kommentarer (rader som börjar med
#) - Blanksteg och indrag
Poster utan både path och url hoppas över. Värden avkodas inte från citattecken.
Gitlink-poster
Gitlink-poster är trädposter med läge 160000. De pekar på en commit (inte ett träd eller en blob) i undermodulens arkiv.
$tree = $repo->readObject($treeHash);
foreach ($tree->entries as $entry) {
if ($entry->isGitlink()) {
echo "Submodule at {$entry->name}: commit {$entry->hash->hex}\n";
}
}
Identifiering av TreeEntry
$entry->isGitlink(); // true if mode is '160000'
$entry->mode; // '160000'
Initiera undermoduler
$manager->init();
För varje undermodul som deklareras i .gitmodules görs följande:
- En tom
.git/modules/<name>/-katalog skapas - Katalogen för undermodulens sökväg skapas om den inte finns
submodule.<name>.active = trueochsubmodule.<name>.urlregistreras i.git/config
Inget hämtas eller checkas ut, och undermodulens fil .git skrivs inte.
Uppdatera undermoduler
$manager->update($repo->head()->tree);
För varje undermodul som är fixerad i det angivna trädet kopierar update() källarkivets katalog git till .git/modules/<name>/, ersätter undermodulens arbetsfiler med källans, skriver filen .git så att den pekar på modulens katalog, anger core.worktree och kopplar loss HEAD vid den fixerade committen.
Undermodulens URL måste vara en lokal sökväg: absolut eller relativ till det överordnade arbetsträdet (git löser i stället relativa URL:er mot det överordnade fjärrarkivets URL). En URL med ett schema (https://...) utlöser RuntimeException('Submodule update currently supports only local paths: ...').
Efter en uppdatering kan du öppna varje undermodul som ett separat arkiv:
$subRepo = Pitmaster::open($repo->workDir() . '/lib/external');
Kontrollera fixerade commits
$headCommit = $repo->head();
$pinnedId = $manager->pinnedCommit('lib/external', $headCommit->tree);
if ($pinnedId !== null) {
echo "lib/external is pinned to commit {$pinnedId->hex}\n";
}
Metoden pinnedCommit() går igenom trädets hierarki för att hitta gitlink-posten vid den angivna sökvägen och returnerar dess mål-commits hash.
Undermodulens status
$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";
}
Statusfält:
name: undermodulens namnpath: sökväg i arbetsträdetexpected: commits hash fixerad i det överordnade trädet (null om den saknas)actual: undermodulens aktuella HEAD (null om den inte har initierats)dirty: true när båda commits är kända och skiljer sig åt. Ändringar i undermodulens arbetsträd kontrolleras inte.
Katalogstruktur
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
Arbetsflöde för undermoduler
// 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";