İçeriğe geç

Alt modüller

Git alt modüller bir depoyu başka bir deponun içine yerleştirir. Alt modül, üst ağacında alt modül deposundaki belirli bir commit'i gösteren bir gitlink girdisi (mod 160000) olarak temsil edilir. Alt modülün URL değeri ve yolu .gitmodules dosyasında tanımlanır.

Kavramlar

Üst deponun ağacında:

Kod
100644 blob abc123...  README.md
040000 tree def456...  src/
160000 commit 789012... lib/external    <-- gitlink entry (submodule)

Gitlink girdisi, alt modülün hangi commit'inin teslim alınması gerektiğini kaydeder. Alt modülün asıl deposu .git/modules/<name>/ içinde bulunur.

SubmoduleManager

PHP
use Pitmaster\Submodule\SubmoduleManager;

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

Alt modülleri listeleme

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

Alt modül değer nesnesi

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 dosyasını ayrıştırma

.gitmodules dosyası alt modül yapılandırmasını tanımlar:

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

Ayrıştırıcı şunları işler:

  • Bölüm başlıkları: [submodule "<name>"]
  • Anahtar-değer çiftleri: path = ..., url = ..., branch = ...
  • Yorumlar (# ile başlayan satırlar)
  • Boşluklar ve girintiler

Hem path hem de url içermeyen girdiler atlanır. Değerlerin tırnakları kaldırılmaz.

Gitlink girdileri, 160000 moduna sahip ağaç girdileridir. Alt modül deposundaki bir commit'i (ağacı veya blob'u değil) gösterirler.

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

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

TreeEntry algılama

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

Alt modülleri başlatma

PHP
$manager->init();

.gitmodules içinde tanımlanan her alt modül için bu işlem:

  1. Boş bir .git/modules/<name>/ dizini oluşturur
  2. Yoksa alt modül yolu dizinini oluşturur
  3. submodule.<name>.active = true ve submodule.<name>.url değerlerini .git/config içine kaydeder

Herhangi bir şeyi getirmez veya teslim almaz ve alt modülün .git dosyasını yazmaz.

Alt modülleri güncelleme

PHP
$manager->update($repo->head()->tree);

Verilen ağaçta sabitlenmiş her alt modül için update(), kaynak deponun git dizinini .git/modules/<name>/ içine kopyalar, alt modülün çalışma dosyalarını kaynağın dosyalarıyla değiştirir, modül dizinini gösteren .git dosyasını yazar, core.worktree değerini ayarlar ve HEAD'i sabitlenmiş commit'e ayırır.

Alt modül URL değeri yerel bir yol olmalıdır: mutlak veya üst öğenin çalışma ağacına göreli olmalıdır (git göreli URL'leri bunun yerine üst öğenin uzak deposunun URL değerine göre çözümler). Şema içeren bir URL (https://...) RuntimeException('Submodule update currently supports only local paths: ...') hatasını fırlatır.

Güncellemeden sonra her alt modülü ayrı bir depo olarak açabilirsiniz:

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

Sabitlenmiş commit'leri denetleme

PHP
$headCommit = $repo->head();
$pinnedId = $manager->pinnedCommit('lib/external', $headCommit->tree);

if ($pinnedId !== null) {
    echo "lib/external is pinned to commit {$pinnedId->hex}\n";
}

pinnedCommit() yöntemi, belirtilen yoldaki gitlink girdisini bulmak için ağaç hiyerarşisini dolaşır ve hedef commit özetini döndürür.

Alt modül durumu

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

Durum alanları:

  • name: alt modül adı
  • path: çalışma ağacındaki yol
  • expected: üst ağaçta sabitlenmiş commit özeti (yoksa null)
  • actual: alt modülün geçerli HEAD'i (başlatılmamışsa null)
  • dirty: her iki commit de biliniyor ve farklıysa true. Alt modülün çalışma ağacındaki değişiklikler denetlenmez.

Dizin yapısı

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

Alt modül iş akışı

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