Git
Chapters ▾ 2nd Edition

7.11 Git Araçları - Alt Modüller

Alt Modüller

Bir projede çalışırken, sıklıkla onun içindeki başka bir projeyi kullanmanız gerekebilir. Bu üçüncü tarafın geliştirdiği bir kütüphane veya ayrı olarak geliştirdiğiniz ve birden fazla ana projede kullandığınız bir kütüphane de olabilir. Bu senaryolarda ortak bir sorun ortaya çıkar: İki projeyi ayrı olarak işlemek, ancak birini diğerinin içinden kullanabilmek istersiniz.

İşte bir örnek. Bir web sitesi geliştirirken, Atom beslemesi oluşturuyorsunuz. Atom oluşturmak üzere kendi kodunuzu yazmak yerine bir kütüphane kullanmaya karar veriyorsunuz. Muhtemelen bu kodu ya bir CPAN yüklemesi veya Ruby gem gibi paylaşılan bir kütüphaneden dahil etmek ya da kaynak kodunu kendi proje ağacınıza kopyalamak zorunda kalacaksınız. Kütüphaneyi dahil etmenin sorunu, kütüphaneyi herhangi bir şekilde özelleştirmenin zor olması ve her istemcinin o kütüphaneye erişimi olması gerektiği için, dağıtmanın genellikle daha da zor olmasıdır. Kodunuzu kendi projenize kopyalamanın sorunu ise, yukarı akış (upstream) değişiklikleri mevcutsa, herhangi bir özel değişiklik yapmanın zorlaşmasıdır.

Git bu sorunu alt modüller kullanarak ele alır. Alt modüller, bir Git reposunu başka bir Git reposunun bir alt dizini olarak tutmanıza izin verir. Bu yöntem, diğer depoyu projenize kopyalayarak (git clone) ve katkılarınızı ayrı tutmanızı sağlar.

Alt Modüllere Giriş

Bir ana proje ve birkaç alt proje olarak bölünmüş basit bir proje geliştirme örneği inceleyeceğiz.

Çalıştığımız repoya bir alt modül olarak varolan bir Git reposunu eklemeye başlayarak başlayalım. Yeni bir alt modül eklemek için, git submodule add komutuna, izlemek istediğiniz projenin mutlak veya göreli URL’sini eklersiniz. Bu örnekte, ``DbConnector`` adında bir kütüphane ekleyeceğiz.

$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.

Alt modüller, projeyi varsayılan olarak ``DbConnector`` adında bir alt dizine ekler. Başka bir yere gitmesini istiyorsanız komutun sonuna farklı bir yol ekleyebilirsiniz.

Bu noktada git status komutunu çalıştırırsanız, birkaç şey fark edeceksiniz.

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   .gitmodules
	new file:   DbConnector

İlk olarak, yeni .gitmodules dosyasını fark etmişsinizdir. Bu, projenin URL’si ile onu çektiğiniz yerel alt dizin arasındaki eşleştirmeyi saklayan bir yapılandırma dosyasıdır:

[submodule "DbConnector"]
	path = DbConnector
	url = https://github.com/chaconinc/DbConnector

Eğer birden fazla alt modülünüz varsa, bu dosyada birden fazla girişiniz olacaktır. Bu dosyanın, .gitignore dosyanız gibi diğer dosyalarınızla birlikte sürüm denetimi altında olduğunu belirtmek önemlidir. Bu, bu projeyi kopyalayan diğer kişilerin alt modül projelerini nereden alacaklarını bildikleri anlamına gelir.

Not

Başkalarının ilk olarak kopyalamaya veya çekmeye çalışacakları yer .gitmodules dosyasındaki URL olduğu için, mümkünse onların erişebileceği bir URL kullanmaya özen gösterin. Örneğin, başkalarının çekmek için kullandığından farklı bir URL’yi siz itmek için kullanıyorsanız, diğerlerinin de erişimine açık olanı kullanın. Kendi kullanımınız için bu değeri yerel olarak değiştirmek isterseniz, git config submodule.DbConnector.url PRIVATE_URL komutu ile varolan değerin üzerine yazabilirsiniz. Mümkünse, göreli bir URL kullanmak da faydalı olabilir.

git status çıktısındaki diğer bilgi, proje klasörü girişidir. Bunu üzerinde git diff komutunu çalıştırırsanız, ilginç bir şey göreceksiniz:

$ git diff --cached DbConnector
diff --git a/DbConnector b/DbConnector
new file mode 160000
index 0000000..c3f01dc
--- /dev/null
+++ b/DbConnector
@@ -0,0 +1 @@
+Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc

DbConnector, çalışma dizininizde bir alt dizin olsa dahi, Git onu bir alt modül olarak görür ve siz o dizinde olmadığınızda içeriğini izlemez. Git bunun yerine onu, o repodan belirli bir katkı olarak görür.

Daha düzgün bir diff çıktısı istiyorsanız, git diff komutuna --submodule seçeneğini ekleyebilirsiniz.

$ git diff --cached --submodule
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..71fc376
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "DbConnector"]
+       path = DbConnector
+       url = https://github.com/chaconinc/DbConnector
Submodule DbConnector 0000000...c3f01dc (new submodule)

Eğer katkılarsanız, şöyle bir şey göreceksiniz:

$ git commit -am 'added DbConnector module'
[master fb9093c] added DbConnector module
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 DbConnector

DbConnector girişinin 160000 moduna dikkat edin. Bu, Git’te bir dizin girişini, bir alt dizin veya bir dosya olarak kaydetmek yerine bir katkı olarak kaydettiğiniz anlamına gelen özel bir moddur.

Son olarak, bu değişiklikleri itin:

$ git push origin master

Alt Modülleri Olan Bir Projeyi Kopyalama

İşte bir alt modül içeren bir projeyi kopyalayacağız. Böyle bir projeyi kopyaladığınızda, varsayılan olarak alt modülleri içeren dizinleri alırsınız, ancak henüz içlerindeki dosyaları almazsınız:

$ git clone https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
$ cd MainProject
$ ls -la
total 16
drwxr-xr-x   9 schacon  staff  306 Sep 17 15:21 .
drwxr-xr-x   7 schacon  staff  238 Sep 17 15:21 ..
drwxr-xr-x  13 schacon  staff  442 Sep 17 15:21 .git
-rw-r--r--   1 schacon  staff   92 Sep 17 15:21 .gitmodules
drwxr-xr-x   2 schacon  staff   68 Sep 17 15:21 DbConnector
-rw-r--r--   1 schacon  staff  756 Sep 17 15:21 Makefile
drwxr-xr-x   3 schacon  staff  102 Sep 17 15:21 includes
drwxr-xr-x   4 schacon  staff  136 Sep 17 15:21 scripts
drwxr-xr-x   4 schacon  staff  136 Sep 17 15:21 src
$ cd DbConnector/
$ ls
$

DbConnector dizini vardır, ancak içi boştur. İki komut çalıştırmanız gerekiyor: Yerel yapılandırma dosyanızı başlatmak için git submodule init ve o projeden tüm verileri almak ve süperprojenizde listelenen uygun katkıları kontrol etmek için git submodule update:

$ git submodule init
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
$ git submodule update
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

Şimdi DbConnector alt diziniz, daha önce katkıladığınız durumda. Ancak, biraz daha basit olan bir yol daha var. git clone komutuna --recurse-submodules bayrağını geçirirseniz, repodaki her alt modülü otomatik olarak başlatır ve güncellersiniz.

$ git clone --recurse-submodules https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

Alt Modülleri Olan Projelerle Çalışmak

Şimdi alt modülleri olan bir projenin bir kopyasına sahibiz ve ana projede ve alt modül projede ekibimizle işbirliği yapacağız.

Üst-akım Değişikliklerini Çekmek

Eğer yalnızca bir alt projeyi tüketiyorsanız ve zaman zaman ondan güncellemeler almak istiyorsanız, ancak gerçekte kopyanızda hiçbir şeyi değiştirmiyorsanız; alt modülleri bir projede kullanmanın en basit modelini uygulayabilirsiniz. Bir örnek ile üzerinden basitçe geçelim.

Eğer bir alt modüldeki yeni çalışmayı kontrol etmek istiyorsanız, dizine gidip yerel kodu güncellemek için git fetch ve git merge komutlarını kullanabilirsiniz.

$ git fetch
From https://github.com/chaconinc/DbConnector
   c3f01dc..d0354fc  master     -> origin/master
$ git merge origin/master
Updating c3f01dc..d0354fc
Fast-forward
 scripts/connect.sh | 1 +
 src/db.c           | 1 +
 2 files changed, 2 insertions(+)

Şimdi ana projeye geri döner ve git diff --submodule komutunu çalıştırırsanız, alt modülün güncellendiğini ve ona eklenen katkıların bir listesini görebilirsiniz. git diff komutunu her çalıştırdığınızda --submodule yazmak istemiyorsanız, diff.submodule yapılandırma değerini ``log`` olarak değiştirerek bu formatı varsayılan olarak ayarlayabilirsiniz.

$ git config --global diff.submodule log
$ git diff
Submodule DbConnector c3f01dc..d0354fc:
  > more efficient db routine
  > better connection routine

Bu noktada bir katkı işlerseniz, başkaları güncellediğinde alt modülü yeni koda kilitlenmiş olursunuz.

Alt dizinde manuel olarak çekme ve birleştirme yapmak istemiyorsanız, bunu yapmanın daha kolay bir yolu daha vardır. git submodule update --remote komutunu çalıştırırsanız, Git sizin için alt modüllere çekip, güncelleyecektir.

$ git submodule update --remote DbConnector
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
   3f19983..d0354fc  master     -> origin/master
Submodule path 'DbConnector': checked out 'd0354fc054692d3906c85c3af05ddce39a1c0644'

Bu komut, varsayılan olarak alt modül reposunun master dalını izlemek istediğinizi varsayar. Ancak dilerseniz, bunu farklı bir şeye de ayarlayabilirsiniz. Örneğin, DbConnector alt modülünün o reposunun ``stable`` dalını izlemesini istiyorsanız, bunu ya .gitmodules dosyanızda (böylece diğer herkes de izleyebilir) ya da sadece yerel .git/config dosyanızda ayarlayabilirsiniz. Şimdi bunu .gitmodules dosyasına ayarlayalım:

$ git config -f .gitmodules submodule.DbConnector.branch stable

$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
   27cf5d3..c87d55d  stable -> origin/stable
Submodule path 'DbConnector': checked out 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687'

-f .gitmodules seçeneğini kullanmazsanız, sadece sizin için değişiklik yapar, ancak muhtemelen bu bilgiyi herkesin takip etmesi daha mantıklı olacaktır.

Bu noktada git status komutunu çalıştırdığınızda, Git alt modülde "yeni katkılar" olduğunu gösterecektir.

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  modified:   .gitmodules
  modified:   DbConnector (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

status.submodulesummary yapılandırmasını ayarlarsanız, Git ayrıca alt modüllerinizdeki değişikliklerin kısa bir özetini de gösterecektir:

$ git config status.submodulesummary 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   .gitmodules
	modified:   DbConnector (new commits)

Submodules changed but not updated:

* DbConnector c3f01dc...c87d55d (4):
  > catch non-null terminated lines

Bu noktada git diff komutunu çalıştırırsanız, hem .gitmodules dosyasını değiştirdiğimizi hem de indirdiğimiz bir dizi katkının alt modül projemize işlenmeye hazır olduğunu görebilirsiniz.

$ git diff
diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "DbConnector"]
        path = DbConnector
        url = https://github.com/chaconinc/DbConnector
+       branch = stable
 Submodule DbConnector c3f01dc..c87d55d:
  > catch non-null terminated lines
  > more robust error handling
  > more efficient db routine
  > better connection routine

Alt modülümüze işleyebileceğimiz katkıların günlüğünü görebilmemiz harika. Bir kez katkılandıktan sonra, git log -p komutunu çalıştırdığınızda, sonradan bu bilgiyi de görebilirsiniz.

$ git log -p --submodule
commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Sep 17 16:37:02 2014 +0200

    updating DbConnector for bug fixes

diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "DbConnector"]
        path = DbConnector
        url = https://github.com/chaconinc/DbConnector
+       branch = stable
Submodule DbConnector c3f01dc..c87d55d:
  > catch non-null terminated lines
  > more robust error handling
  > more efficient db routine
  > better connection routine

git submodule update --remote komutunu çalıştırdığınızda Git, (varsayılan olarak) tüm alt modüllerinizi güncellemeye çalışacaktır, bu yüzden birçok alt modülünüz varsa, sadece güncellemek istediğiniz alt modülün adını geçirmek isteyebilirsiniz.

Bir Alt Modülle Çalışmak

Alt modüller kullanıyorsanız bunun sebebi muhtemelen, ana projedeki kodla aynı anda alt modüldeki (veya birkaç alt modüldeki) kod üzerinde de çalışmak istemenizdir. Aksi takdirde, muhtemelen daha basit bir bağımlılık yönetim sistemi kullanıyor olurdunuz (örneğin Maven veya Rubygems gibi).

Şimdi, ana projede olduğu gibi alt modülde değişiklik yapma ve bu değişiklikleri aynı anda katkılama ve yayınlama örneğini inceleyelim.

Şimdiye kadar öğrendiğimiz üzere, alt modül repolarından değişiklikleri almak için git submodule update komutunu çalıştırdığımızda; Git değişiklikleri alır ve alt dizindeki dosyaları günceller ancak alt repoyu ``detached HEAD`` (bağlı olmayan bir HEAD) durumunda bırakır. Bu, yerel çalışma dalının (örneğin ``master`` gibi) değişiklikleri izlemediği anlamına gelir. Değişiklikleri izleyen bir çalışma dalı olmadığı için, alt modülde değişiklik yaparsanız bile, bu değişikliklerin bir sonraki git submodule update komutunu çalıştırdığınızda kaybolma olasılığı oldukça yüksektir. Bir alt modüldeki değişikliklerin izlenmesini istiyorsanız atmanız gereken ekstra adımlar bulunmaktadır.

Alt modülünüzü daha kolay bir şekilde düzenlemek için iki şey yapmanız gerekiyor. Her alt modüle gitmeli ve üzerinde çalışmak için bir dal açmalısınız. Sonra, eğer değişiklik yapmışsanız ve ardından git submodule update --remote ile yeni çalışmaları üst akımdan çekiyorsanız, Git’e ne yapması gerektiğini söylemelisiniz. Seçenekler, yeni değişiklikleri yerel işinize birleştirmek veya yerel işinizi yeni değişikliklerin üstüne tekrar temellemeyi denemektir.

İlk olarak, alt modül dizinine giderek bir dala geçelim.

$ git checkout stable
Switched to branch 'stable'

Bunu ``merge`` seçeneğiyle deneyelim. Manuel olarak belirtmek için, update çağrımıza --merge seçeneğini ekleyebiliriz. İşte bu alt modül için sunucuda bir değişiklik olduğunu göreceğiz ve bu değişiklik birleştirilir.

$ git submodule update --remote --merge
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
   c87d55d..92c7337  stable     -> origin/stable
Updating c87d55d..92c7337
Fast-forward
 src/main.c | 1 +
 1 file changed, 1 insertion(+)
Submodule path 'DbConnector': merged in '92c7337b30ef9e0893e758dac2459d07362ab5ea'

DbConnector dizinine girdiğimizde, yeni değişikliklerin zaten yerel stable dalımıza birleştirildiğini göreceğiz. Şimdi kütüphanede kendi yerel değişikliğimizi yapalım ve aynı zamanda başka birinin üst akıma başka bir değişiklik gönderdiğinde ne olacağını görelim.

$ cd DbConnector/
$ vim src/db.c
$ git commit -am 'unicode support'
[stable f906e16] unicode support
 1 file changed, 1 insertion(+)

Şimdi alt modülümüzü güncellediğimizde, yerel bir değişiklik yaptığımız ve üst akımın da dahil edilmesi gereken bir değişikliğe sahip olduğumuzda ne olacağını görelim.

$ git submodule update --remote --rebase
First, rewinding head to replay your work on top of it...
Applying: unicode support
Submodule path 'DbConnector': rebased into '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

Eğer --rebase veya --merge seçeneklerini unutursanız, Git sadece alt modülü sunucuda bulunan herhangi bir duruma günceller ve projenizi bağlı bir HEAD durumuna (detached HEAD) sıfırlar.

$ git submodule update --remote
Submodule path 'DbConnector': checked out '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

Eğer böyle bir durumla karşılaşırsanız endişelenmeyin, sadece dizininize geri dönüp dalınıza tekrar geçebilirsiniz (ki bu hala çalışmanızı içerecektir) ve origin/stable (veya istediğiniz uzak dal) dalı manuel olarak birleştirebilir veya tekrar temelleyebilirsiniz.

Alt modüldeki değişikliklerinizi katkılamadıysanız ve bir alt modül güncellemesi çalıştırdığınızda sorunlara neden olabilecek değişiklikleri yapmadıysanız, Git değişiklikleri alır ancak alt modül dizininizde kaydedilmemiş çalışmanın üzerine yeniden yazmaz.

$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 4 (delta 0)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
   5d60ef9..c75e92a  stable     -> origin/stable
error: Your local changes to the following files would be overwritten by checkout:
	scripts/setup.sh
Please, commit your changes or stash them before you can switch branches.
Aborting
Unable to checkout 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector'

Eğer üst akımda yapılan bir değişiklikle çakışan değişiklikler yaptıysanız, güncelleme işlemini çalıştırdığınızda Git bunu size bildirecektir.

$ git submodule update --remote --merge
Auto-merging scripts/setup.sh
CONFLICT (content): Merge conflict in scripts/setup.sh
Recorded preimage for 'scripts/setup.sh'
Automatic merge failed; fix conflicts and then commit the result.
Unable to merge 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector'

Alt modül dizinine gidip çakışmayı normal şekilde düzeltebilirsiniz.

Alt Modül Değişikliklerini Yayınlama

Şimdi alt modül dizinimizde bazı değişikliklerimiz var. Bu değişikliklerden bazıları güncellemelerimizle üst akımdan getirildi, diğerleri ise yerel olarak yapıldı ama henüz itmediğimiz için başkaları tarafından erişilebilir durumda değil.

$ git diff
Submodule DbConnector c87d55d..82d2ad3:
  > Merge from origin/stable
  > updated setup script
  > unicode support
  > remove unnecessary method
  > add new option for conn pooling

Ana projeyi katkılayıp ittiğimizde ve alt modül değişikliklerini itmediğimizde; bağımlı olunan alt modül değişikliklerini almanın bir yolu olmayacağı için değişikliklerimizi çekmek isteyenler sıkıntı yaşayacaklar. Bu değişiklikler yalnızca bizim yerel kopyamızda var olacaktır.

Bunu önlemek için, ana projeyi itmeden önce, tüm alt modüllerinizin düzgün bir şekilde itildiğinden emin olmak için Git’ten istekte bulunabilirsiniz. git push komutu ``check`` ya da ``on-demand`` olarak ayarlanabilecek olan --recurse-submodules argümanını alır. ``check`` seçeneği, katkılanmış alt modül değişikliklerinden herhangi biri henüz itilmediyse push komutunu geçersiz kılar.

$ git push --recurse-submodules=check
The following submodule paths contain changes that can
not be found on any remote:
  DbConnector

Please try

	git push --recurse-submodules=on-demand

or cd to the path and use

	git push

to push them to a remote.

Gördüğünüz gibi, bize ne yapmamız gerektiği konusunda bazı yardımcı önerilerde bulunulmaktadır. En basit seçenek, her alt modüle gidip manuel olarak uzak sunuculara itmek ve sonra ana itme işlemini tekrar denemektir. Eğer bu denetleme davranışının tüm itmeler için gerçekleşmesini istiyorsanız, bu davranışı varsayılan olarak yapmak için git config push.recurseSubmodules check komutunu kullanabilirsiniz.

Diğer seçenek, bunu sizin için bunu yapmaya çalışacak olan ``on-demand`` değerini kullanmaktır.

$ git push --recurse-submodules=on-demand
Pushing submodule 'DbConnector'
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done.
Total 9 (delta 3), reused 0 (delta 0)
To https://github.com/chaconinc/DbConnector
   c75e92a..82d2ad3  stable -> stable
Counting objects: 2, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 266 bytes | 0 bytes/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To https://github.com/chaconinc/MainProject
   3d6d338..9a377d1  master -> master

Görüldüğü gibi, Git DbConnector modülüne girdi ve ana projeyi itmeden önce bu alt modülü itti. Eğer bu itme bir nedenle başarısız olursa, ana proje itmesi de başarısız olacaktır. Bu davranışı varsayılan olarak ayarlamak için git config push.recurseSubmodules on-demand komutunu kullanabilirsiniz.

Alt Modül Değişikliklerini Birleştirme

Eğer bir başkasıyla aynı anda bir alt modül referansını değiştirirseniz, bazı sorunlarla karşılaşabilirsiniz. Yani, alt modül geçmişleri ayrışmış ve üstproje içinde ayrık (divergent) dallara katkı işlenmişse, bunu düzeltmeniz gerekebilir.

Eğer katkılardan biri diğerinin doğrudan önceliyse (bir hızlı ileri birleştirme), Git sadece birleştirme için sonuncuyu seçecektir, bu yüzden bu sorunsuz çalışır.

Ancak Git sizin için basit birleştirmeyi bile denemez. Eğer alt modül katkıları ayrıksa ve birleştirilmeleri gerekiyorsa, şuna benzer bir uyarı alırsınız:

$ git pull
remote: Counting objects: 2, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 2 (delta 1), reused 2 (delta 1)
Unpacking objects: 100% (2/2), done.
From https://github.com/chaconinc/MainProject
   9a377d1..eb974f8  master     -> origin/master
Fetching submodule DbConnector
warning: Failed to merge submodule DbConnector (merge following commits not found)
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.

Temelde burada olan olay şudur: Git, alt modülün geçmişinde ayrık ve birleştirilmesi gereken noktaları kaydeden iki dal olduğunu anlamıştır. Bu, "merge following commits not found" olarak açıklanmıştır, bu kafa karıştırıcıdır ancak birazdan neden böyle olduğunu açıklayacağız.

Sorunu çözmek için, alt modülün hangi durumda olması gerektiğini belirlemeniz gerekmektedir. Garip bir şekilde, Git size burada yardımcı olacak pek fazla bilgi vermez, hatta tarihin her iki tarafında katkıların SHA-1’lerini bile vermez. Neyse ki, bunu bulmak oldukça basittir. git diff komutunu çalıştırırsanız, birleştirmeye çalıştığınız iki dalda kaydedilen katkıların SHA-1’lerini alabilirsiniz.

$ git diff
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector

Bu durumda, eb41d76 alt modüldeki bizim katkımızı ve c771610'ı yukarı akımın katkısını temsil etmektedir. Alt modül dizinimize gittiğimizde, birleştirme ona dokunmadığı için, eb41d76 üzerinde olması gerekir. Herhangi bir nedenden dolayı değilse bile, ona işaret eden bir dal oluşturabilirsiniz.

Önemli olan, diğer taraftan gelen katkının SHA-1’idir. Çözmeniz ve birleştirmeniz gereken budur. Doğrudan SHA-1 ile birleştirmeyi deneyebilirsiniz, veya onun için bir dal oluşturabilir ve ardından bunu birleştirmeyi deneyebilirsiniz. En azından daha güzel bir birleştirme katkı mesajı oluşturmak için, biz ikincisini öneriyoruz.

Bu nedenle, alt modül dizinimize gireceğiz, git diff ile alınan ikinci SHA-1’e dayalı bir dal oluşturacağız ve manuel olarak birleştireceğiz.

$ cd DbConnector

$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135

$ git branch try-merge c771610
(DbConnector) $ git merge try-merge
Auto-merging src/main.c
CONFLICT (content): Merge conflict in src/main.c
Recorded preimage for 'src/main.c'
Automatic merge failed; fix conflicts and then commit the result.

Burada gerçek bir birleştirme çakışması yaşadık, bu yüzden bunu çözer ve katkılarsak, bu sonuçla ana projeyi güncelleyebiliriz.

$ vim src/main.c (1)
$ git add src/main.c
$ git commit -am 'merged our changes'
Recorded resolution for 'src/main.c'.
[master 9fd905e] merged our changes

$ cd .. (2)
$ git diff (3)
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit eb41d764bccf88be77aced643c13a7fa86714135
 -Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d
++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a
$ git add DbConnector (4)

$ git commit -m "Merge Tom's Changes" (5)
[master 10d2c60] Merge Tom's Changes
  1. İlk olarak çakışmayı çöz

  2. TArdından ana proje dizinine geri dön

  3. SHA-1’leri tekrar kontrol edebiliriz

  4. Çakışmış alt modül girişini çöz

  5. Birleştirmeyi katkı olarak işle

Bu biraz kafa karıştırıcı olabilir, ancak gerçekten çok zor değil.

İlginç bir şekilde, Git’in ele aldığı başka bir durum daha var. Eğer alt modül dizininde hem geçmişteki her iki katkıyı da içeren birleştirme katkısı varsa, Git size bunu olası bir çözüm olarak önerir. Alt modül projesinde, bu iki katkıyı içeren dalların birleştirildiği bir nokta olduğunu görür, bu yüzden belki de bunu isteyebileceğinizi düşünür.

Bu yüzden önceki hata mesajının nedeni "merge following commits not found" idi, çünkü bunu yapamadı. Karışık görünmesinin sebebi kimsenin bunu denemesini beklememesidir.

Eğer Git tek bir kabul edilebilir birleştirme katkısı bulursa, şuna benzer bir şey göreceksiniz:

$ git merge origin/master
warning: Failed to merge submodule DbConnector (not fast-forward)
Found a possible merge resolution for the submodule:
 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: > merged our changes
If this is correct simply add it to the index for example
by using:

  git update-index --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a "DbConnector"

which will accept this suggestion.
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.

Verilen komut, izlemi git add komutunu çalıştırmış gibi günceller (çakışmayı temizler) ve ardından katkıyı işler. Aslında bunu yapmanız pek önerilmez. Bunun yerine, alt modül dizinine girip farkın ne olduğunu görebilir, bu katkıya hızlı bir ileri sarma yapabilir, uygun şekilde test edebilir ve ardından katkınızı işleyebilirsiniz.

$ cd DbConnector/
$ git merge 9fd905e
Updating eb41d76..9fd905e
Fast-forward

$ cd ..
$ git add DbConnector
$ git commit -am 'Fast forwarded to a common submodule child'

Bu da aynı şeyi yapar, ama en azından bu şekilde düzgün çalışıp çalışmadığını doğrulayabilir ve işiniz bittiğinde kodun alt modül dizinizde olduğunu görebilirsiniz.

Alt Modül İpuçları

Alt modüllerle çalışmayı biraz daha kolaylaştırmak için yapabileceğiniz birkaç şey var.

Alt Modül "Foreach" Komutu

Her bir alt modülde belirli bir komut çalıştırmak için foreach alt modül komutu bulunmaktadır. Aynı projede birkaç alt modülünüz varsa, bu gerçekten yardımcı olabilir.

Örneğin, yeni bir özellik başlatmak veya bir hata düzeltmek istediğimizi ve birkaç alt modülde çalışmamız olduğunu varsayalım. Tüm alt modüllerimizdeki çalışmamızı kolayca saklayabiliriz.

$ git submodule foreach 'git stash'
Entering 'CryptoLibrary'
No local changes to save
Entering 'DbConnector'
Saved working directory and index state WIP on stable: 82d2ad3 Merge from origin/stable
HEAD is now at 82d2ad3 Merge from origin/stable

Daha sonra tüm alt modüllerimizde yeni bir dal oluşturabilir ve ona geçebiliriz.

$ git submodule foreach 'git checkout -b featureA'
Entering 'CryptoLibrary'
Switched to a new branch 'featureA'
Entering 'DbConnector'
Switched to a new branch 'featureA'

Fikri anladınız. Yapabileceğiniz en faydalı şey, ana projenizde neyin değiştiğini ve tüm yan projelerinizde neyin değiştiğini gösteren güzel bir birleşik diff oluşturmaktır.

$ git diff; git submodule foreach 'git diff'
Submodule DbConnector contains modified content
diff --git a/src/main.c b/src/main.c
index 210f1ae..1f0acdc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -245,6 +245,8 @@ static int handle_alias(int *argcp, const char ***argv)

      commit_pager_choice();

+     url = url_decode(url_orig);
+
      /* build alias_argv */
      alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
      alias_argv[0] = alias_string + 1;
Entering 'DbConnector'
diff --git a/src/db.c b/src/db.c
index 1aaefb6..5297645 100644
--- a/src/db.c
+++ b/src/db.c
@@ -93,6 +93,11 @@ char *url_decode_mem(const char *url, int len)
        return url_decode_internal(&url, len, NULL, &out, 0);
 }

+char *url_decode(const char *url)
+{
+       return url_decode_mem(url, strlen(url));
+}
+
 char *url_decode_parameter_name(const char **query)
 {
        struct strbuf out = STRBUF_INIT;

Burada, bir alt modülde bir fonksiyon tanımlıyoruz ve ana projede onu çağırıyoruz. Bu açıkça basitleştirilmiş bir örnek, ancak umarım bunun nasıl faydalı olabileceğine dair bir fikir verir.

Faydalı Kısaltmalar (Alias)

Bazıları oldukça uzun olan bu komutlar için bazı kısaltmalar oluşturmak isteyebilirsiniz ancak bunları varsayılan yapmak isterseniz, çoğu yapılandırma seçeneğini değiştiremezsiniz. Git’te kısaltmalar kurmayı Komut Kısayolu (Alias) Ayarlama bölümünde ele almıştık, ancak alt modüllerle sıkça çalışmayı planlıyorsanızx kurmak isteyebileceğiniz bir örnek aşağıda verilmiştir.

$ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
$ git config alias.spush 'push --recurse-submodules=on-demand'
$ git config alias.supdate 'submodule update --remote --merge'

Bu şekilde alt modüllerinizi güncellemek istediğinizde sadece git supdate komutunu çalıştırabilir veya alt modül bağımlılığı kontrolü ile itmek için git spush komutunu kullanabilirsiniz.

Alt Modüllerle İlgili Sorunlar

Ancak alt modüllerle çalışmak tamamen sorunsuz değildir.

Örneğin, alt modül içeren dallarda çalışırken dal değiştirmek karmaşık olabilir. Eğer yeni bir dal oluşturup, oraya bir alt modül eklerseniz ve daha sonra o alt modülü içermeyen başka bir dala geçerseniz; alt modül dizinini halen izlenmeyen bir dizin olarak görürsününüz:

$ git checkout -b add-crypto
Switched to a new branch 'add-crypto'

$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
...

$ git commit -am 'adding crypto library'
[add-crypto 4445836] adding crypto library
 2 files changed, 4 insertions(+)
 create mode 160000 CryptoLibrary

$ git checkout master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	CryptoLibrary/

nothing added to commit but untracked files present (use "git add" to track)

Dizini kaldırmak zor değildir, ancak orada bulundurmak biraz kafa karıştırıcı olabilir. Eğer kaldırırsanız ve daha sonra o alt modülü içeren dala geri geçerseniz, yeniden oluşturmak için submodule update --init komutunu çalıştırmanız gerekecektir.

$ git clean -ffdx
Removing CryptoLibrary/

$ git checkout add-crypto
Switched to branch 'add-crypto'

$ ls CryptoLibrary/

$ git submodule update --init
Submodule path 'CryptoLibrary': checked out 'b8dda6aa182ea4464f3f3264b11e0268545172af'

$ ls CryptoLibrary/
Makefile	includes	scripts		src

Tekrardan: gerçekten çok zor değil, ancak biraz kafa karıştırıcı olabilir.

Birçok insanın karşılaştığı diğer ana husus, alt dizinlerden alt modüllere geçiş yapmaktır. Eğer projenizdeki dosyaları izliyor ve onları bir alt modüle taşımak istiyorsanız, dikkatli olmanız gerekir; yoksa Git size kızabilir. Diyelim ki projedeki bir alt dizinde dosyalarınız var ve onu bir alt modüle geçirmek istiyorsunuz. Eğer alt dizini sildikten sonra submodule add komutunu çalıştırırsanız, Git size bağırmaya başlayacaktır:

$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' already exists in the index

Önce CryptoLibrary dizinini izlemden (stage) çıkarmalısınız. Sonra alt modülü ekleyebilirsiniz:

$ git rm -r CryptoLibrary
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.

Şimdi bunu bir dalda yaptığınızı varsayalım. Eğer bu dosyalar bir alt modül yerine hâlâ gerçek bir ağaçta duruyorsa, o dala geri geçmeye çalıştığınızda, şu hatayı alırsınız:

$ git checkout master
error: The following untracked working tree files would be overwritten by checkout:
  CryptoLibrary/Makefile
  CryptoLibrary/includes/crypto.h
  ...
Please move or remove them before you can switch branches.
Aborting

Geçişe zorlamak için checkout -f deneyebilirsiniz, ancak bu komutla kaydedilmemiş değişikliklerinizin üzerine yazılabileceğinden dikkatli olun!

$ git checkout -f master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'

Ardından, geri geçtiğinizde, nedeni bilinmeyen bir şekilde boş bir CryptoLibrary dizini alırsınız ve git submodule update de bunu düzeltemeyebilir. Tüm dosyalarınızı geri almak için submodule dizinine girip git checkout . komutunu çalıştırmanız gerekebilir. Bu komutu birden fazla alt modül için çalıştırmak için submodule foreach bektiğinde çalıştırabilirsiniz.

Bugünlerde alt modüllerin tüm Git verilerini üst projenin .git dizininde tuttuğunu unutmayın. Bu nedenle çok eski Git sürümlerinden farklı olarak, bir alt modül dizinini yok etmek, önceden sahip olduğunuz herhangi bir katkı veya dalı kaybetmez.

Bu araçlarla sayesinde alt modüller, birbiriyle ilişkili ancak ayrı projede bulunan geliştirmeleri aynı anda yapabilmek için oldukça basit ve etkili bir yöntem olabilir.

scroll-to-top