Pereiti prie turinio

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:

Kodas
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

PHP
use Pitmaster\Submodule\SubmoduleManager;

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

Submodulių sąrašas

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";
    }
}

Submodulio reikšmės objektas

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)

.gitmodules analizė

Faile .gitmodules deklaruojama submodulio konfigūracija:

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";
}

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 yra medžio įrašai su režimu 160000. Jie nurodo commit (ne medį ar blob) submodulio saugykloje.

PHP
$tree = $repo->readObject($treeHash);

foreach ($tree->entries as $entry) {
    if ($entry->isGitlink()) {
        echo "Submodule at {$entry->name}: commit {$entry->hash->hex}\n";
    }
}

TreeEntry aptikimas

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

Submodulių inicijavimas

PHP
$manager->init();

Kiekvienam .gitmodules deklaruotam submoduliui:

  1. Sukuriamas tuščias .git/modules/<name>/ katalogas
  2. Sukuriamas submodulio kelio katalogas, jei jo nėra
  3. .git/config įrašomi submodule.<name>.active = true ir submodule.<name>.url

Nieko neatsisiunčiama ir neišsiimama į darbinį katalogą, taip pat neįrašomas submodulio .git failas.

Submodulių atnaujinimas

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

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

Prisegtų pakeitimų tikrinimas

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

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";
}

Būsenos laukai:

  • name: submodulio pavadinimas
  • path: kelias darbiniame medyje
  • expected: 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

Kodas
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

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