Hoppa till innehåll

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:

Kod
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

PHP
use Pitmaster\Submodule\SubmoduleManager;

$manager = new SubmoduleManager(
    $repo->objectDatabase(),
    $repo->workDir(),
    $repo->gitDir(),
);

Lista undermoduler

PHP
$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

PHP
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:

Ini
[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
PHP
$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 är trädposter med läge 160000. De pekar på en commit (inte ett träd eller en blob) i undermodulens arkiv.

PHP
$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

PHP
$entry->isGitlink();  // true if mode is '160000'
$entry->mode;         // '160000'

Initiera undermoduler

PHP
$manager->init();

För varje undermodul som deklareras i .gitmodules görs följande:

  1. En tom .git/modules/<name>/-katalog skapas
  2. Katalogen för undermodulens sökväg skapas om den inte finns
  3. submodule.<name>.active = true och submodule.<name>.url registreras i .git/config

Inget hämtas eller checkas ut, och undermodulens fil .git skrivs inte.

Uppdatera undermoduler

PHP
$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:

PHP
$subRepo = Pitmaster::open($repo->workDir() . '/lib/external');

Kontrollera fixerade commits

PHP
$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

PHP
$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 namn
  • path: sökväg i arbetsträdet
  • expected: 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

Kod
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

PHP
// 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";