Git
Chapters ▾ 2nd Edition

7.11 Git Alətləri - Alt Modullar

Alt Modullar

Çox vaxt bir layihə üzərində işləyərkən içərisindən başqa bir layihəni istifadə etməli oluruq. Bu üçüncü tərəfin inkişaf etdirdiyi və ya ayrıca inkişaf etdirdiyiniz və bir çox əsas layihədə istifadə etdiyiniz bir kitabxanada ola bilər. Bu ssenarilərdə ortaq bir problem ortaya çıxır: iki layihəni ayrı bir hala gətirmək istəyərsən, amma yenə də birini digərindən istifadə edə bilmək istəyirsən.

Bir misal. Deyək ki, bir veb sayt hazırlayırsınız və Atom feed-lər yaradırsınız. Atom yaradan kodunuzu yazmaq əvəzinə bir kitabxanadan istifadə etməyi qərara alırsınız. Çox güman ki, bu kodu bir CPAN quraşdırması və ya Ruby gem kimi ortaq bir kitabxanadan daxil etməlisiniz və ya mənbə kodu öz layihə ağacınıza kopyalayırsınız. Kitabxananın daxil olması ilə əlaqədar problem, kitabxananı hər hansı bir şəkildə düzəltmək və tez-tez onu yerləşdirmək daha çətindir, çünki hər bir müştəridə bu kitabxananın mövcud olduğundan əmin olmalısınız. Kodun öz layihənizə kopyalanması ilə bağlı problem, yuxarı dəyişikliklər mövcud olduqda etdiyiniz hər hansı bir xüsusi dəyişikliklərin birləşməsi çətinləşməsidir.

Git bu məsələni submodullardan istifadə edərək həll edir. submodullar bir Git deponu başqa bir Git deponun alt kataloqu olaraq saxlamağa imkan verir. Bu, layihənizdə başqa bir depo klonlaşdırmağa və ayrılıqda vəzifələrinizi yerinə yetirməyə imkan verir.

Submodullarla başlayaq

Əsas bir layihəyə və bir neçə alt layihəyə bölünmüş sadə bir layihəni inkişaf etdirmək yolu ilə dəvam edəcəyik.

Mövcud Git deponu üzərində işlədiyimiz deponun submodulu olaraq əlavə etməklə başlayaq. Yeni bir submodul əlavə etmək üçün izləməyə başlamaq istədiyiniz layihənin mütləq və ya nisbi URL-i ilə git submodule add əmrindən istifadə edin. Bu nümunədə “DbConnector” adlı bir kitabxana əlavə edəcəyik.

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

Varsayılan olaraq, submodullar alt layihəni “DbConnector” depo ilə eyni adlı bir qovluğa əlavə edəcəkdir. Əmrin sonunda başqa bir yerə getmək istəsəniz fərqli bir yol əlavə edə bilərsiniz.

Bu nöqtədə git status işlədirsinizsə, bir neçə şeyi görəcəksiniz.

$ 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

Əvvəlcə yeni .gitmodules faylını görməlisiniz. Bu, layihənin URL-si ilə çəkdiyiniz local subdirectory arasındakı datanı saxlayan bir konfiqurasiya faylıdır:

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

Bir neçə submodulunuz varsa, bu faylda birdən çox giriş olacaqdır. Bu faylın .gitignore faylı kimi digər fayllarınızla da idarə olunduğunu qeyd etmək vacibdir. Layihənizin qalan hissəsi ilə push və pull edildi. Bu layihəni klonlayan digər insanlar submodul layihələrini haradan əldə edəcəyini bilirlər.

Note
gitmodules faylındakı URL digər insanların əvvəlcə clone/fetch etməyə çalışdıqları bir şey olduqları üçün mümkün olduğundan əldə edə biləcəkləri bir URL istifadə etdiyinizdən əmin olun.

Məsələn, başqalarından çəkmək üçün fərqli bir URL istifadə etsəniz, başqalarının istifadə edə biləcəyi URL-i istifadə edin. Öz istifadəniz üçün bu dəyəri local olaraq git config submodule.DbConnector.url PRIVATE_URL ilə yaza bilərsiniz. Tətbiq edildikdə, nisbi bir URL kömək edə bilər.

git status çıxışındakı digər siyahı, layihə qovluğuna girişdir. Bunun üzərinə git diff işləsəniz, maraqlı bir şey görəcəksiniz:

$ 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 işlədiyiniz qovluğunda bir subdirectory olsa da, Git onu submodul kimi görür və bu qovluqda olmadığınız zaman onun konteksini izləmir. Bunun əvəzinə, Git bunu o depodan xüsusi bir commit kimi görür.

Biraz daha yaxşı fərqli çıxış istəyirsinizsə, --submodule seçimini` git diff-ə əlavə edə bilərsiniz.

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

Nə vaxt commit etsəniz, aşağıdakı kimi görünəcəkdir:

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

DbConnector girişi üçün `160000`rejiminə diqqət yetirin. Bu Git-də xüsusi bir rejimdir ki, bu, bir commiti bir subdirectory və ya bir fayl kimi deyil, bir qovluq girişi kimi qeyd etdiyiniz deməkdir.

Nəhayət, bu dəyişiklikləri push edin:

$ git push origin master

Bir Layihəni Submodullarla Klonlaşdırmaq

Burada bir submodul ilə bir layihə klonlayacağıq. Belə bir layihəni klonlaşdırdıqda, standart olaraq submodulları ehtiva edən qovluqları alırsınız, ancaq bunların içərisindəki faylların heç birini hələ almırsı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 qovluğu var, lakin boşdur. İki əmri işlətməlisiniz: local konfiqurasiya sənədinizi başlatmaq üçün git submodule init və o layihədən bütün məlumatları almaq və layihənizdə sadalanan müvafiq commit-ləri yoxlamaq üçün 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'

İndi DbConnector subdirectory-niz əvvəllər commit etdiyiniz zaman olduğu vəziyyətdədir. Bunu etmək üçün bir az daha sadə başqa bir yol var. Əgər --recurse-submodules əmrini git clone əmrinə əlavə etsəniz, depodakı submodulların hər hansı birində submodullar varsa, avtomatik olaraq depodakı hər bir submodulu işə salır və yeniləyir.

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

Layihəni əvvəlcədən klonlaşdırmısınızsa və --recurse-submodules unutmusunuzsa, git submodule update --init əmrini işlədərək git submodule initgit submodule update addımlarını birləşdirə bilərsiniz. Hər hansı bir iç-içə keçmiş submodulu işə salmaq, almaq və yoxlamaq üçün git submodule update --init --recursive istifadə edə bilərsiniz.

Submodullar ilə bir Layihə üzərində İşləmək

İndi içərisində submodulları olan bir layihənin bir nüsxəsi var və həm əsas layihədə, həm də submodul layihədə komanda yoldaşlarımızla əməkdaşlıq edəcəyik.

Submodule Remote-dan Upstream Dəyişiklikləri Pulling etmək

Uzaqdan Layihədə submodulları istifadə etməyin ən sadə modeli, sadəcə bir layihə istifadə etməyiniz və zaman zaman yeniləmələri almaq istəməyiniz, ancaq həqiqətən yoxlanışda bir şey dəyişdirməməyinizdir. Oradakı sadə bir nümunəyə baxaq.

Bir submodulda yeni işin olub olmadığını yoxlamaq istəyirsinizsə, qovluğa girib local kodu yeniləmək üçün yuxarıdakı branch-ı git fetchgit merge işlədə bilərsiniz.

$ 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(+)

İndi əsas layihəyə qayıdırsınız və git diff --submodule işlədirsinizsə, submodulun yeniləndiyini və ona əlavə olunanların siyahısını əldə etdiyini görə bilərsən. Hər dəfə git diff işlədikdə --submodule yazmaq istəmirsinizsə, onu diff.submodule konfiqurasiya dəyərini “log”-a təyin edərək standart format kimi təyin edə bilərsiniz.

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

Bu anda commit etsəniz, submodulu digər insanlar yenilədikdə yeni kodun içərisinə bağlayacaqsınız.

Submodulda manual olaraq fetch edilib birləşməməyi istəsəniz, bunu etmək üçün daha asan bir yol var. git submodule update --remote işlədirsinizsə, Git submodullara daxil olacaq və sizin üçün yeniləyəcəkdir.

$ 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 əmr standart olaraq, submodul deposunun master branch-na çıxışı yeniləmək istədiyinizi qəbul edəcəkdir. İstəsəniz, bunu fərqli bir şeyə təyin edə bilərsiniz. Məsələn, DbConnector submodulunun o deponun “stable” branch-nı izləməyini istəyirsinizsə, onu ya .gitmodules sənədinizdə (beləcə hər kəs onu izləyər) və ya local .git/config faylında quraşdıra bilərsiniz. Gəlin onu .gitmodules sənədinə qoyaq:

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

Əgər -f .gitmodules deaktiv etsəniz, bu yalnız sizin üçün dəyişikliyə səbəb olacaq, ancaq hər kəsin də olduğu kimi bu məlumatı depo ilə izləməyi daha mənalı olar.

Bu nöqtədə git status işlədikdə, Git submodulda “new commits” olduğunu göstərəcəkdir.

$ 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")

Konfiqurasiya parametrini status.submodulesummary təyin etsəniz, Git sizə submodullarındakı dəyişikliklərin qısa xülasəsini də göstərəcəkdir:

$ 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 nöqtədə git diff işlədirsinizsə, həm də .gitmodules faylımızı dəyişdirdiyimizi, həmçinin endirdiyimiz və submodul layihəmizin yerinə yetirməyə hazır olduğumuz bir sıra işlərin olduğunu görə bilərik.

$ 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

Bu, olduqca əlverişlidir, çünki həqiqətən submodulumuzda verəcəyimiz commit-lərin qeydini görə bilərik. Commit etdikdən sonra bu məlumatı git log -p işlədərkən də görə bilərsiniz.

$ 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 standart olaraq git submodule update --remote-u işlətdiyiniz zaman submodullarınızın hamısını yeniləməyə çalışacaqdır. Əgər bunların çoxu varsa, yeniləməyə çalışmaq istədiyiniz yalnız submodulun adını ötürmək istəyə bilərsiniz.

Upstream Dəyişikliklərini Layihə Uzaqdan Pull edir

İndi MainProject depolarının öz local klonuna sahib olan işçinizin yerinə keçək. Yeni düzəlişlərinizi əldə etmək üçün sadəcə git pull yerinə yetirmək kifayət etmir:

$ git pull
From https://github.com/chaconinc/MainProject
   fb9093c..0a24cfc  master     -> origin/master
Fetching submodule DbConnector
From https://github.com/chaconinc/DbConnector
   c3f01dc..c87d55d  stable     -> origin/stable
Updating fb9093c..0a24cfc
Fast-forward
 .gitmodules         | 2 +-
 DbConnector         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

$ 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:   DbConnector (new commits)

Submodules changed but not updated:

* DbConnector c87d55d...c3f01dc (4):
  < catch non-null terminated lines
  < more robust error handling
  < more efficient db routine
  < better connection routine

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

Varsayılan olaraq, git pull əmri yuxarıda göstərilən ilk əmrin nəticələrində gördüyümüz kimi, submodulları rekursiv şəkildə dəyişir. Ancaq submodulları yeniləməz. Bu, submodulun “modified” olduğunu və “new commits” verdiyini göstərən git status əmrinin çıxışı ilə göstərilir. Üstəlik, yeni commit nöqtəsini göstərən mötərizələr (<), bu tapşırıqların MainProject-də qeydə alındığını, lakin local DbConnector yoxlanışında olmadığını göstərir. Yeniləməni yekunlaşdırmaq üçün git submodule update işləməlisiniz:

$ git submodule update --init --recursive
Submodule path 'vendor/plugins/demo': checked out '48679c6302815f6c76f1fe30625d795d9e55fc56'

$ git status
 On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

Etibarlı tərəfdə olmaq üçün MainProject yeni submodulları əlavə etməyi commit etdiyi submodullar varsa, --init--recursive flag-larından istifadə edərək git submodule update-ni işə salmalısınız.

Bu prosesi avtomatlaşdırmaq istəyirsinizsə,  --recurse-submodules flag-nı git pull əmrinə əlavə edə bilərsiniz (Git 2.14-dən bəri). Bu, Git pull etməkdən dərhal sonra git submodule update-ni işə salacaq və submodulları düzgün vəziyyətə gətirəcəkdir. Üstəlik, Git’i həmişə --recurse-submodules ilə pull etmək istəsəniz, submodule.recurse konfiqurasiya seçimini "true" olaraq təyin edə bilərsiniz (Git 2.15-dən bəri git pull üçün işləyir). Bu seçim Git-in onu dəstəkləyən bütün əmrlər üçün --recurse-submodules flag-ından istifadə etməsini təmin edəcəkdir (clone xaricində).

Super layihə yeniləmələrini pulling edərkən baş verə biləcək xüsusi bir vəziyyət var: ola bilər ki, yuxarıdakı depo, pull etdiyiniz commit-lərin birində .gitmodules faylındakı submodulun URL-sini dəyiştirmiş ola bilər. Bu, məsələn submodul layihəsi hosting platformasını dəyişdirərsə, baş verə bilər. Bu vəziyyətdə, super layihə deposunda local olaraq tənzimlənmiş submodulun uzaq yerində tapılmayan bir submodul əməlinə istinad edərsə, git pull --recurse-submodules və ya git submodule update üçün uğursuz ola bilər. Bu vəziyyəti düzəltmək üçün git submodule sync əmri tələb olunur:

# copy the new URL to your local config
$ git submodule sync --recursive
# update the submodule from the new URL
$ git submodule update --init --recursive

Submodul üzərində İşləmək

Çox güman ki, submodullardan istifadə edirsinizsə, bunu əsas layihədə (və ya bir neçə submodulda) kod üzərində işləyərkən eyni zamanda submoduldakı kod üzərində işləmək istədiyiniz üçün edirsiniz. Əks təqdirdə bunun əvəzinə daha sadə bir asılılıq idarəetmə sistemindən istifadə edərdiniz (məsələn Maven və ya Rubygems).

Beləliklə, indi əsas layihə ilə eyni vaxtda submodulda dəyişiklik etmək və eyni zamanda bu dəyişiklikləri commit etmək və yayımlamaq nümunəsinə baxaq.

İndiyə qədər, submodul depolarından dəyişiklik almaq üçün git submodule update əmrini işlətdikdə Git dəyişiklikləri əldə edib subdirectory-da faylları yeniləyəcək, lakin sub-repository-i “detached HEAD” vəziyyəti adlanacaq. Bu o deməkdir ki, dəyişiklikləri izləyən local (məsələn, master kimi) branch yoxdur. Heç bir işləyən branch izləmə dəyişiklikləri olmadan o deməkdir ki, submodula dəyişiklik etsəniz də, növbəti dəfə git submodule update işlədikdə bu dəyişikliklər tamamilə itiriləcəkdir. Bir submoduldakı dəyişikliklərin izlənməsini istəyirsinizsə, əlavə addımlar atmalısınız.

Giriş və hack etmək daha asan olması üçün alt modulunuzu qurmaq üçün iki şey etməlisiniz. Hər bir submodula daxil olmalı və işləmək üçün bir branch-ı yoxlamalısınız. Sonra dəyişikliklər etdikdən sonra Git’ə nə etməli olduğunu izah etməlisiniz və sonra git submodule update --remote yuxarıdan yeni işə pull ediləcəkdir. Seçimlər bunlardır ki, onları local işinizə birləşdirə bilərsiniz və ya local işinizi yeni dəyişikliklərin başına qaytarmağa cəhd edə bilərsiniz.

Əvvəlcə submodul qovluğumuza daxil olub, bir branch-ı yoxlayaq.

$ cd DbConnector/
$ git checkout stable
Switched to branch 'stable'

Submodulumuzu “merge” seçimi ilə yeniləməyə çalışaq. Manual müəyyənləşdirmək üçün update çağırışımıza --merge seçimini əlavə edə bilərik. Burada bu submodul üçün serverdə bir dəyişiklik olduğunu və birləşdirildiyini görərik.

$ cd ..
$ 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 qovluğuna daxil olsaq, dəyişikliklər local stable branch-da göstərilmişdir. İndi gəlin görək kitabxanaya öz local dəyişikliklərimizi edərik və başqası eyni zamanda başqa bir dəyişikliyi yuxarıya push edəndə nə olacağına baxaq.

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

İndi submodulumuzu yeniləsək, local bir dəyişiklik etdikdə və yuxarıda əlavə etməli olduğumuz bir dəyişikliyə sahib olduğumuzu görə bilərik.

$ cd ..
$ 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'

--rebase və ya --merge unudarsanız, Git yalnız submodulu serverdəki hər hansı bir şeyə yeniləyəcək və layihənizi ayrı bir HEAD vəziyyətinə qaytaracaqdır.

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

Əgər bu baş verərsə, narahat olmayın, sadəcə qovluğuna qayıda bilər və yenidən branch-nızı yoxlaya bilərsiniz (bu hələ də işinizi özündə saxlayacaq) origin/stable (və ya istədiyiniz hər hansı bir branch-ı) manual olaraq birləşdirə və ya dəyişdirə bilərsiniz.

Submoduldakı dəyişikliklərinizi etməmisinizsə və problem yarada biləcək bir submodul yeniləməsini işlədirsinizsə, Git dəyişiklikləri alır, ancaq submodul qovluğunuzda qeyd olunmamış işləri yazmır.

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

Yuxarıda dəyişdirilmiş bir şey ilə ziddiyyətli dəyişikliklər etmisinizsə, Git yeniləməni işlədiyiniz zaman sizə xəbər verəcəkdir.

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

Submodul qovluğuna girib konflikti normal qaydada istədiyiniz kimi düzəldə bilərsiniz.

Submodul Dəyişikliklərini Yayımlamaq

İndi submodul qovluğunda bəzi dəyişikliklər var. Bunlardan bəziləri yeniləmələrimiz tərəfindən yuxarıdan gətirilib, digərləri local olaraq hazırlanıb və hələ də onları açmamışıq.

$ git diff
Submodule DbConnector c87d55d..82d2ad3:
  > Merge from origin/stable
  > Update setup script
  > Unicode support
  > Remove unnecessary method
  > Add new option for conn pooling

Əsas layihədə iştirak etsək və submodula dəyişikliklərini də pushing etmədən push etsək, dəyişikliklərimizi yoxlamağa çalışan digər insanlara problemə yarnacaq, çünki asılı olan submodul dəyişikliklərini əldə etmək üçün bir yol qalmayacaqlar. Bu dəyişikliklər yalnız local nüsxəmizdə olacaq.

Bunun baş verməməsinə əmin olmaq üçün Git-dən əsas layihəni push etmədən əvvəl bütün submodullarınızın düzgün şəkildə push edildiyini yoxlamağı xahiş edə bilərsiniz. git push əmri, “check” və ya “on-demand” olaraq təyin edilə bilən --recurse-submodules argumentini alır. “check” seçimi, submodul dəyişikliklərindən hər hansı birinə push edilmədiyi təqdirdə, push etməyi asanlaşdıracaq.

$ 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üyünüz kimi, bundan sonra da nə etmək istədiyimiz barədə bəzi faydalı məsləhətlər verir. Sadə seçim odur ki, hər bir submodula daxil olub uzaqdan mövcud olduqlarına əmin olmaq üçün manual olaraq uzaqdan idarə etməkdir və sonra bu push etməyi yenidən sınamaqdır. Bütün push etməklər üçün yoxlama davranışının olmasını istəyirsinizsə,git config push.recurseSubmodules check əməliyyatı edərək bu davranışı standart hala gətirə bilərsiniz.

Digər seçim, “on-demand” dəyərini istifadə etməkdir, bu sizin üçün bunu etməyə çalışacaqdı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ördüyünüz kimi, Git DbConnector moduluna girdi və əsas layihəyə keçməmişdən əvvəl push etdi. Bu submodul push etmək nədənsə uğursuz olarsa, əsas layihə push etmə də uğursuz olacaq. Bu davranışı standart olaraq git config push.recurseSubmodules on-demand edərək edə bilərsiniz.

Submodul Dəyişikliklərini Birləşdirmək

Bir submodul arayışını başqası ilə eyni vaxtda dəyişdirsəniz, bəzi problemlərlə qarşılaşa bilərsiniz. Yəni, submodul tarixləri bir-birindən ayrılıbsa və bir super layihədə branch-ları ayırmaq öhdəliyindədirsə, düzəltmək üçün bir az iş tələb oluna bilər.

Təqdim olunanlardan biri digərinin birbaşa əcdadıdırsa (sürətli birləşmə), onda Git birləşmə üçün sadəcə sonuncunu seçəcəkdir ki, beləcə yaxşı işləyəcək.

Git sizin üçün mənasız birləşməyə belə cəhd etməyəcək. Submodul bir-birindən fərqli commit edirsə və birləşdirilməlidirsə, buna bənzər bir şey alacaqsı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.

Beləliklə, əsasən burada nə baş verən budur ki, Git iki branch-ın submodulun tarixində bir-birindən fərqli olan və birləşdirilməli olduğunu nöqtələri qeyd etdi. Bunu çaşdırıcı olan “merge following commits not found” kimi izah edir, amma bunun niyə olduğunu birazdan izah edəcəyik.

Problemi həll etmək üçün submodulun hansı vəziyyətdə olduğunu müəyyənləşdirməlisiniz. Qəribədir ki, Git buraya kömək etmək üçün çox məlumat vermir, hətta tarixin hər iki tərəfinin verdiyi SHA-1-lərdən də məlumat vermir. Xoşbəxtlikdən, bunu anlamaq çox sadədir. git diff işlədirsinizsə, birləşdirməyə çalışdığınız hər iki branch-da qeyd olunan əmsalların SHA-1-lərini əldə edə bilərsiniz.

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

Beləliklə, bu vəziyyətdə, eb41d76 submodulumuzda biz sahip olduğumuz commit və c771610 upstream-in sahib olduğu commitdir. Submodule qovluğumuza daxil olsaq, birləşmə ona toxunmayacağı üçün artıq eb41d76 üzərində olmalıdır. Hansı səbəbdən olursa olsun, sadəcə ona işarə edən bir branch yarada və nəzarət edə bilərsiniz.

Önəmli olan qarşı tərəfin commit-nin SHA-1 olmasıdır. Bu birləşdirib və həll etmək məcburiyyətində olduğunuz bir şeydir. Sadəcə birbaşa SHA-1 ilə birləşməyə cəhd edə bilərsiniz və ya bunun üçün bir branch yarada və sonra birləşməyə cəhd edə bilərsiniz. Sonuncunu, daha yaxşı birləşmə commiti verəcəyi üçün tövsiyə edirik.

Beləliklə, submodul qovluğumuza daxil olacağıq, git diff-dən ikinci SHA-1 əsasında “try-merge” adlı bir branch yaradacağıq və manual olaraq birləşdirəcəyik.

$ cd DbConnector

$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135

$ git branch try-merge c771610

$ 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 faktiki olaraq birləşmə konflikti var, buna görə də bu problemi həll etsək və öhdəsinə götürsək, əsas layihəni nəticə ilə yeniləyə bilərik.

$ 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. Əvvəlcə konflikti həll edirik.

  2. Sonra əsas layihə qovluğuna qayıdırıq.

  3. SHA-1-ləri yenidən yoxlaya bilərik.

  4. Konflikt submodul girişini həll edin.

  5. Birləşməyimizi commit edin.

Bir az qarışıq ola bilər, amma həqiqətən çox çətin deyil.

Maraqlıdır ki, Git-in ələ keçirdiyi başqa bir hadisə də var. Birləşdirmə əməliyyatı tarixində hər ikisini ehtiva edən submodule qovluğunda varsa, Git bunu sizə mümkün bir həll yolu kimi təklif edəcəkdir. Submodule layihəsinin bir nöqtəsində kimsə bu iki əmrdən ibarət branch-ları birləşdirdiyini görür, bəlkə buna görə bunlardan birini istəyərsən.

Buna görə əvvəllər edilən xəta mesajı “merge following commits not found” idi, çünki bunu edə bilmədi. Çaşdırıcıdır, çünki bunu etmək üçün kimin cəhd etməsini gözləyəcək ki?

Bir məqbul birləşmə commiti taparsa, bu kimi bir şey görəcəksiniz:

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

Git-in təklif etdiyi əmr indeksin yenilənməsini təmin edir, sanki git add (münaqişəni təmizləyən) kimi işlədin, sonra commit edin. Yəqin ki, bunu etməməlisiniz. Asanlıqla submodul qovluğuna girib fərqin nə olduğunu görə bilərsiniz, bu tapşırığa sürətlə irəliləyin, düzgün sınaqdan keçirin və sonra commit edin.

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

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

Bu eyni şeyi yerinə yetirir, amma heç olmasa bu şəkildə bunun işlədiyini və tamamlandığınızda submodul qovluğunda kodunuz olduğunu yoxlaya bilərsiniz.

Submodul Göstərişləri

Submodullarla işləməyi bir az asanlaşdırmaq üçün edə biləcəyiniz bir neçə iş var.

Submodule Foreach

Hər submodulda bəzi ixtiyari əmrləri işlətmək üçün foreach submodul əmr var. Eyni layihədə bir neçə submodul varsa, bu həqiqətən faydalı ola bilər.

Məsələn, deyək ki, yeni bir xüsusiyyətə başlamaq və ya səhv düzəltmək istəyirik və bir neçə submodulda işimiz var. Bütün işləri bütün submodullarımızda asanlıqla yığa bilərik.

$ 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

Sonra yeni bir branch yarada və bütün alt modullarımızda ona keçə bilərik.

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

Fikir aldın. Həqiqətən edə biləcəyiniz bir şey, əsas layihənizdə və bütün alt layihələrinizdə dəyişdirilənlərdən gözəl birləşmiş fərq yaratmaqdı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 submodulda bir funksiya təyin etdiyimizi və əsas layihədə adlandırdığımızı görə bilərik. Bu açıqca sadələşdirilmiş bir nümunədir, amma ümid edirik bunun necə faydalı ola biləcəyi barədə sizə bir fikir verəcək.

Faydalı Alias-lar

Bu əmrlərdən bəziləri üçün bəzi alias-lar qurmaq istəyə bilərsiniz, çünki onlar kifayət qədər uzun ola bilər və əksəriyyətini defolt halına gətirmək üçün konfiqurasiya seçimlərini təyin edə bilməzsiniz. Git ləqəblərinin qurulmasını Git Alias’lar -də qbaa bilərsiniz, ancaq Git-də submodullarla çox işləməyi planlaşdırırsınızsa, qurmaq istədiyiniz bir nümunəni burada tapa bilərsiniz.

$ 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 yolla submodullarıınızı yeniləmək istədikdə git supdate və ya submodula asılılığını yoxlamaqla basmaq üçün git spush işlədə bilərsiniz.

Submodullarla Bağlı Məsələlər

Submodullardan istifadə hiccups olmadan da mümkün deyil.

Branch-ları Dəyişdirmək

Məsələn, branch-larında submodulları olan branch-ları dəyişdirmək Git 2.13-dən daha köhnə Git versiyaları ilə də çətin ola bilər. Yeni bir branch yaradırsınızsa, orada bir submodul əlavə edin və sonra bu submodul olmadan yenidən bir branch-a keçin, submodul qovluğu hələ də yığılmamış bir qovluq olaraq qalır:

$ git --version
git version 2.12.2

$ 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 'Add crypto library'
[add-crypto 4445836] Add 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)

Qovluqda silmək çətin deyil, amma orada olması bir az çaşdırıcı ola bilər. Əgər onu çıxarıb sonra yenidən həmin submodulu olan branch-a qayıtsanız, onu təkrarlamaq üçün submodule update --init işlətməlisiniz.

$ 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

Yenə də həqiqətən çox çətin deyil, ancaq bir az qarışıq ola bilər.

Yeni Git versiyaları (Git >= 2.13), keçid etdiyimiz branch üçün submodulları düzgün vəziyyətdə yerləşdirməyin qayğısına qalan git checkout əmrinə --recurse-submodules flagı əlavə etməklə bütün bunları asanlaşdırır.

$ git --version
git version 2.13.3

$ 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 'Add crypto library'
[add-crypto 4445836] Add crypto library
 2 files changed, 4 insertions(+)
 create mode 160000 CryptoLibrary

$ git checkout --recurse-submodules master
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'.

nothing to commit, working tree clean

git checkout-nın --recurse-submodules flag-dan istifadə, super layihədə bir neçə branch-da işləyərkən faydalı ola bilər. Həqiqətən, submodulunu fərqli commit-lərdə qeyd edən branch-lar arasında keçsəniz, git status yerinə yetirildikdə, alt modul ‘modified’' olaraq görünəcək və “new commits” göstəriləcədir. Bu, submodule vəziyyətinin branch-ları dəyişdirərkən bir qayda olaraq aparılmamasıdır.

Bu, həqiqətən çaşdırıcı ola bilər, buna görə də layihənizdə submodullar olduqda həmişə git checkout --recurse-submodules etmək yaxşı bir fikirdir. --recurse-submodules flag-ı olmayan köhnə Git versiyaları üçün, yoxlanışdan sonra submodulları düzgün vəziyyətə gətirmək üçün git submodule update --init --recursive istifadə edə bilərsiniz.

Xoşbəxtlikdən, Git (>= 2.14) konfiqurasiya seçimini submodule.recurse qoyaraq həmişə --recurse-submodules flag-ından istifadə etməyi söyləyə bilərsiniz: git config submodule.recurse true. Yuxarıda qeyd edildiyi kimi, bu da Git-in --recurse-submodules seçimi olan hər bir əmr üçün submodullara təkrarlanmasını təmin edəcəkdir (git clone xaricində).

Subdirectory-lərdən submodul-lara keçid

Bir çox insanın işlətdiyi digər əsas xəbərdarlıq subdirectory-dən submodullara keçməkdir. Layihənizdəki faylları izləmisinizsə və onları submodula köçürmək istəyirsinizsə, diqqətli olmalısınız və ya Git sizə qəzəblənəcəkdir. Layihənizin alt alt bölməsində fayllarınız olduğunu düşünün və onu bir subdirectory-ə keçirmək istəyirsiniz. Əgər subdirectory-ni silib sonra submodule add işlədirsinizsə, Git sizə qışqırır:

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

Əvvəlcə CryptoLibrary qovluğunu çıxartmalısınız. Sonra submodulu əlavə edə bilərsiniz:

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

İndi bir branch-da etdiyinizi düşünün. Bu sənədlərin submodula deyil, həqiqi ağacda olduğu bir branch-a geri dönməyə çalışarsanız, bu səhvinizi 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

Onu checkout -f ilə dəyişdirməyə məcbur edə bilərsiniz, ancaq bu əmrlə yenidən yazıla biləcəyiniz üçün orada saxlanmamış dəyişikliklərin olmadığından ehtiyatlı olun.

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

Sonra geri dönəndə nədənsə boş bir CryptoLibrary qovluğu alırsınız və git submodule update onu da düzəldə bilməz. Bütün sənədlərinizi geri qaytarmaq üçün submodul qovluğuna daxil olmalı və git checkout . başlatmalısınız. Bunu bir neçə submodul üçün işlətmək üçün submodule foreach skriptində işlədə bilərsiniz.

Qeyd etmək vacibdir ki, submodullar bu gün bütün Git məlumatlarını ən yaxşı layihənin .git qovluğunda saxlayırlar, buna görə Git-in çox köhnə versiyasından fərqli olaraq, submodule qovluğunu məhv etmək heç bir commiti və ya branch-nı itirməyəcəkdir.

Bu vasitələrlə submodullar eyni vaxtda bir neçə əlaqəli, lakin ayrıca layihələr üzərində inkişaf etdirmək üçün olduqca sadə və təsirli bir metod ola bilər.