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:
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
use Pitmaster\Submodule\SubmoduleManager;
$manager = new SubmoduleManager(
$repo->objectDatabase(),
$repo->workDir(),
$repo->gitDir(),
);
Alt modülleri listeleme
$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
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:
[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";
}
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
Gitlink girdileri, 160000 moduna sahip ağaç girdileridir. Alt modül deposundaki bir commit'i (ağacı veya blob'u değil) gösterirler.
$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
$entry->isGitlink(); // true if mode is '160000'
$entry->mode; // '160000'
Alt modülleri başlatma
$manager->init();
.gitmodules içinde tanımlanan her alt modül için bu işlem:
- Boş bir
.git/modules/<name>/dizini oluşturur - Yoksa alt modül yolu dizinini oluşturur
submodule.<name>.active = truevesubmodule.<name>.urldeğerlerini.git/configiçine kaydeder
Herhangi bir şeyi getirmez veya teslim almaz ve alt modülün .git dosyasını yazmaz.
Alt modülleri güncelleme
$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:
$subRepo = Pitmaster::open($repo->workDir() . '/lib/external');
Sabitlenmiş commit'leri denetleme
$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
$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 yolexpected: ü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ı
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ışı
// 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";