Git
Chapters ▾ 2nd Edition

7.11 Mga Git na Kasangkapan - Mga Submodule

Mga Submodule

Madalas na nangyayari na habang nagtatrabaho sa isang proyekto, kailangan mong gumamit ng isa pang proyekto mula sa loob nito. Marahil ito ay isang library na binuo ng isang third party o ikaw ay hiwalay lumilikha at ginagamit sa maraming mga proyektong magulang. Ang isang karaniwang isyu ay lumitaw sa mga sitwasyong ito: gusto mong tratuhin ang dalawang proyekto na hiwalay ngunit maaari pa ring magamit ang isa mula sa loob ng isa pa.

Narito ang isang halimbawa. Ipagpalagay na ikaw ay bumubuo ng isang website at naglilikha ng Atom feed. Sa halip na nagsusulat ng iyong sariling code na binubuo ng Atom, napagdesisyunan mo na gumamit ng isang library. Malamang na kailangang isama mo ang code na ito mula sa isang nakabahaging library tulad ng pag-install ng CPAN o Ruby gem, o kopyahin ang source code sa iyong sariling tree ng proyekto. Ang isyu sa pagsasali sa library ay mahirap na ipasadya ang library sa anumang paraan at kadalasang mas mahirap na i-deploy ito, dahil kailangan mong siguraduhin na ang bawat kliyente ay may magagamit na library. Ang isyu sa pagkokopya ng code sa iyong sariling proyekto ay mahirap na isama ang anumang mga pasadyang pagbabago na iyong ginawa kapag ang mga upstream na pagbabago ay nagiging nagagamit.

Tinatalakay ng Git ang mga isyung ito gamit ang mga submodule. Ang mga submodule ay nagpapahintulot sa iyo na panatilihin ang isang repositoryo ng Git bilang isang subdirectory ng ibang repositoryo ng Git. Pinapayagan na nito na i-clone ang iba pang repositoryo sa iyong proyekto at panatilihing hiwalay ang iyong mga commit.

Pagsisimula sa mga Submodule

Ating tatalakayin ang paggawa ng isang simpleng proyekto na hinati sa isang pangunahing proyekto at ilang mga sub-project.

Ating simulan sa pamamagitan ng pagdagdag ng isang umiiral na repositoryo ng Git bilang isang submodule na repositoryo na ating tinatrabaho. Upang magdagdag ng isang bagong submodule, gumamit ng utos na git submodule add na may ganap o kaugnay na URL ng pryekto na nais mong simulang subaybayan. Sa halimbawang ito, magdadagdag tayo ng isang library na tinatawag na “DbConnector”.

[source,console

$ git submodule add https://github.com/chaconinc/DbConnector
Pagkokopya sa 'DbConnector'...
remote: Pagbibilang ng mga bagay: 11, tapos na.
remote: Pagko-compress ng mga bagay: 100% (10/10), tapos na.
remote: Kabuuan 11 (delta 0), muling nagamit 11 (delta 0)
Pag-aalis ng laman ng mga bagay: 100% (11/11), tapos na.
Pagsusuri ng pagkakakonekta... tapos na.

Bilang default, idadagdag ng mga submodule ang subprject sa isang direktoryo na pinangalanan nang pareho sa repositoryo, sa kasong ito ay “DbConnector”. Maaari kang magdagdag ng iba’t-ibang path sa huli ng utos kung nais mo ito pumunta saanman.

Kung patakbuhin mo ang git status sa puntong ito, mapapansin mo ang ilang mga bagay.

$ git status
Sa branch na master
Ang iyong branch ay napapanahon sa 'origin/master'.

Ang mga pagbabagong dapat i-commit:
  (gumamit ng "git reset HEAD <file>..." upang mag-unstage)

	bagong file:   .gitmodules
	bagong file:   DbConnector

Una dapat mong mapansin ang bagong file na .gitmodules. Ito ay isang file ng pagsasaayos na nag-iimbak sa pagmamapa sa pagitan ng URL ng proyekto at ang lokal na subdirectory na na-pull mo ito:

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

Kung mayroon kang maramihang mga submodule, magkakaroon ka ng maramihang mga entry sa file na ito. Mahalaga na tandaan na ang file na ito ay kontrolado ng bersyon kasama ang iyong ibang mga file, katulad ng iyong file na .gitignore. Ito ay naka-push at naka-pull sa natitirang bahagi ng iyong proyekto. Ito ay kung paano alam ng ibang mga tao na nag-clone ng proyektong ito kung saan makukuha ang mga proyekto ng submodule.

Dahil ang URL sa file na .gitmodules ay kung ano ang unang subukan ng mga tao upang i-clone/i-fetch, siguraduhin na gumamit ng isang URL na maaari nilang ma-access kung posible. Halimbawa, kung gumamit ka ng isang iba’t ibang URL upang mag-push kaysa sa iba na gusto na mag-pull, gumamit ng isa na may access ang iba. Maaari mong sapawan ang halaga na ito nang lokal gamit ang git config submodule.DbConnector.url PRIVATE_URL para sa pansariling paggamit. Kung naaangkop, maaaring makatulong ang isang kaugnay na URL.

Ang ibang paglilista sa git status na output ay ang entry ng folder ng proyekto. Kung papatakbuhin mo ang git diff doon, nakikita mo ang isang kawili-wiling bagay:

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

Kahit na ang DbConnector ay isang subdirectory ng iyong tinatrabahuang direktoryo, nakikita ito ng Git bilang isang submodule at hindi nagsusubaybay sa mga nilalaman nito kapag wala ka sa direktoryo iyon. Sa halip, nakikita ito ng Git bilang isang partikular na commit mula sa repositoryong iyon.

Kung nais mo ng isang mas magandang diff na output, maaaring kang magpasa ng opsyon na --submodule sa git diff.

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

Kapag ikaw ay nag-commit, nakikita mo ang isang bagay tulad nito:

$ git commit -am 'added DbConnector module'
[master fb9093c] nagdagdag ng DbConnector na module
 2 mga file ang nabago, 4 pagsisingit(+)
 lumikha ng mode na 100644 .gitmodules
 lumikha ng mode na 160000 DbConnector

Pansinin ang mode na 160000 para sa entry ng DbConnector. Iyon ay isang espesyal na mode sa Git na nangangahulugan na ikaw ay nagtatala ng isang commit bilang isang entry ng direktoryo sa halip na isang subdirectory o isang file.

Panghuli, i-push ang mga pagbabagong ito:

$ git push origin master

Pagkokopya ng isang Proyekto na may mga Submodule

Dito, ating ikokopya ang isang proyekto na may isang submodule sa loob nito. Kapag ikaw ay nagkokopya tulad ng proyektong ito, bilang default, nakakakuha ka ng mga direktoryo na naglalaman ng mga submodule, ngunit wala pa sa mga file na ito ang nasa loob nito:

$ git clone https://github.com/chaconinc/MainProject
Pagkokopya sa 'MainProject'...
remote: Pagbibilang ng mga bagay: 14, tapos na.
remote: Pagko-compress ng mga bagay: 100% (13/13), tapos na.
remote: Kabuuan 14 (delta 1), muling nagamit 13 (delta 0)
Pag-aalis ng laman ng mga bagay: 100% (14/14), tapos na.
Pagsusuri ng pagkakakonekta... tapos na.
$ cd MainProject
$ ls -la
kabuuan 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
$

Nandoon ang direktoryo ng DbConnector, ngunit walang laman. Kinakailangan mong patakbuhin ang dalawang utos: git submodule init upang masimulan ang iyong lokal na file ng pagsasaayos, at git submodule update upang kunin ang lahat ng datos mula sa proyektong iyon at suriin ang angkop na commit na nakalista sa iyong superproject:

$ git submodule init
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) nakarehistro para sa path na 'DbConnector'
$ git submodule update
Pagkokopya sa 'DbConnector'...
remote: Pagbibilang ng mga bagay: 11, tapos na.
remote: Pagko-compress ng mga bagay: 100% (10/10), tapos na.
remote: Kabuuan 11 (delta 0), muling nagamit 11 (delta 0)
Pag-aalis ng laman ng mga bagay: 100% (11/11), tapos na.
Pagsusuri ng pagkakakonekta... tapos na.
Submodule path 'DbConnector': sinuri ang 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

Ngayon na ang iyong subdirectory na DbConnector ay nasa eksaktong estado kung nasaan ito kumakailan noong na-commit mo kanina.

May ibang paraan upang gawin ito kung saan ay mas simple, gayunman. Kung ipapasa mo ang --recurse-submodules sa uto na git clone, ito ay awtomating magsisimula at mag-update ng bawat submodule sa repositoryo.

$ git clone --recurse-submodules https://github.com/chaconinc/MainProject
Pagkokopya sa 'MainProject'...
remote: Pagbibilang ng mga bagay: 14, tapos na.
remote: Pagko-compress ng mga bagay: 100% (13/13), tapos na.
remote: Kabuuan 14 (delta 1), muling nagamit 13 (delta 0)
Pag-aalis ng laman ng mga bagay: 100% (14/14), tapos na.
Pagsusuri ng pagkakakonekta... tapos na.
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) nakarehistro sa path na 'DbConnector'
Pagkokopya sa 'DbConnector'...
remote: Pagbibilang ng mga bagay: 11, tapos na.
remote: Pagko-compress ng mga bagay: 100% (10/10), tapos na.
remote: Kabuuan 11 (delta 0), muling nagamit 11 (delta 0)
Pag-aalis ng laman ng mga bagay: 100% (11/11), tapos na.
Pagsusuri ng pagkakakonekta... tapos na.
Submodule path 'DbConnector': sinuri ang 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

Pagtatrabaho sa isang Proyekto na may mga Submodule

Ngayon na mayroon tayong isang kopya ng isang proyekto na may mga submodule sa loob nito at makikupagtulungan sa ating mga kasamahan sa koponan sa kapwa pangunahing proyekto at submodule na proyekto.

Pag-pull sa mga Pagbabago ng Upstream

Ang pinakasimpleng modelo ng paggamit ng mga submodule sa isang proyekto ay kung ikaw ay nag-uubos lamang ng isang subproject at nais na makakuha ng mga pagbabago mula dito sa pana-panahon ngunit hindi akwtal na nagbabago ng anuman sa iyong pagsusuri. Talakayin natin ang isang simpleng halimbawa doon.

Kung gusto mong magsuri para sa bagong trabaho sa isang submodule, maaari kang pumunta sa direktoryo at patakbuhin ang git fetch at git merge ang upstream na branch upang ma-update ang lokal na code.

$ git fetch
Mula sa https://github.com/chaconinc/DbConnector
   c3f01dc..d0354fc  master     -> origin/master
$ git merge origin/master
Ina-update ang c3f01dc..d0354fc
Fast-forward
 scripts/connect.sh | 1 +
 src/db.c           | 1 +
 2 na mga file na nabago, 2 pagsisingit(+)

Kung gusto mo ngayong bumalik sa pangunahing proyekto at patakbuhin ang git diff --submodule, maaari mong makita na ang submodule ay na-update at makakuha ng isang listahan ng mga commit na naidagdag dito. Kung hindi mo gusto na magtipa ng --submodule sa bawat oras na patakbuhin mo ang git diff, maaari mong itakda ito bilang default na format sa pamamagitan ng pagtatakda sa config na halaga ng diff.submodule sa “log”.

$ git config --global diff.submodule log
$ git diff
Submodule DbConnector c3f01dc..d0354fc:
  > mas mahusay na kalakaran ng db
  > mas mahusay na kalakaran ng koneksyon

Kung nag-commit ka sa puntong ito pagkatapos ay ila-lock mo ang submodule sa pagkakaroon ng bagong code kapag nag-update ang iba pang mga tao.

Mayroong din isang mas madaling paraan upang gawin ito, kung naisin mo na hindi manu-manong kunin at i-merge sa subdirectory. Kung patakbuhin mo ang git submodule update --remote, pupuntahan ng Git ang iyong mga submodule at kunin at i-update para sa iyo.

$ git submodule update --remote DbConnector
remote: Pagbibilang ng mga bagay: 4, tapos na.
remote: Pagko-compress ng mga bagay: 100% (2/2), tapos na.
remote: Kabuuan 4 (delta 2), muling nagamit 4 (delta 2)
Pag-aalis ng laman ng mga bagay: 100% (4/4), tapos na.
Mula sa https://github.com/chaconinc/DbConnector
   3f19983..d0354fc  master     -> origin/master
Submodule path 'DbConnector': sinuri ang 'd0354fc054692d3906c85c3af05ddce39a1c0644'

Ang utos na ito ay ipagpalagay bilang defualt na nais mong i-update ang paglabas sa branch ng master ng repositoryo ng submodule. Kaya mo, gayunpaman, itakda ito sa ibang bagay kung gusto mo. Halimbawa, kung gusto mo na subaybayan ng DbConnector na submodule ang “stable” na branch ng repostiryong iyon, maaari mong itakda ito sa alinman sa iyong file na .gitmodules (upang subaybayan din ng iba), o sa iyong lokal na .git/config na file lamang. Itakda natin ito sa .gitmodules na file:

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

$ git submodule update --remote
remote: Pagbibilang ng mga bagay: 4, tapos na.
remote: Pagko-compress ng mga bagay: 100% (2/2), tapos na.
remote: Kabuuan 4 (delta 2), muling nagamit 4 (delta 2)
Pag-aalis ng laman ng mga bagay: 100% (4/4), tapos na.
Mula sa https://github.com/chaconinc/DbConnector
   27cf5d3..c87d55d  stable -> origin/stable
Submodule path 'DbConnector': sinusuri ang 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687'

Kung iiwanan mo ang -f .gitmodules, ito ay gagawa lamang ng pagbabago para sa iyo, ngunit malamang na ito ay mas makatutulong upang masubaybayan ang impormasyong iyon sa repositoryo upang ang iba naman ay makagagawa rin.

Kapag patakbuhin natin ang git status sa puntong ito, ipapakita ng Git sa atin na mayroon tayong “bagong mga commit” sa submodule.

$ git status
Sa branch na master
Ang iyong branch ay napapanahon sa 'origin/master'.

Mga pagbabago na hindi nai-stage para sa commit:
  (gumamit ng "git add <file>..." upang i-update kung ano ang iko-commit)
  (gumamit ng "git checkout -- <file>..." upang iwaksi ang mga pagbabago sa tinatrabahuang direktoryo)

  nabago:   .gitmodules
  nabago:   DbConnector (mga bagong commit)

walang pagbabago naidagdag sa commit (gumamit ng "git add" at/o "git commit -a")

Kung nagtatakda ka ang setting ng pagsasaayos na status.submodulesummary, ipapakita din ng Git sa iyo ang isang maikling buod ng mga pagbabago sa iyong mga submodule:

$ git config status.submodulesummary 1

$ git status
Sa branch na master
Ang iyong branch ay napapanahon sa 'origin/master'.

Mga pagbabago na hindi nai-stage para sa commit:
  (gumamit ng "git add <file>..." upang i-update kung ano ang iko-commit)
  (gumamit ng "git checkout -- <file>..." upang iwaksi ang mga pagbabago sa tinatrabahuang direktoryo)

	nabago:   .gitmodules
	nabago:   DbConnector (mga bagong commit)

Mga submodule na nabago ngunit hindi na-update:

* DbConnector c3f01dc...c87d55d (4):
  > hulihin ang di-null na tinatapos na mga linya

Sa puntong ito kung patakbuhin mo ang git diff maaari nating parehong makita na binago natin ang ating` .gitmodules` na file at na mayroon ding isang bilang ng mga commit na ating na-pull pababa at handa na i-commit sa ating submodule na proyekto.

$ git diff
diff --git a/.gitmodules b/.gitmodules
indeks 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:
  > hulihin ang di-null na tinatapos na mga linya
  > mas matapang na pag-aasikaso ng kamalian
  > mas mahusay na kalakaran ng db
  > mas mahusay na kalakaran ng koneksyon

Ito ay medyo astig bilang maaari nating aktwal na makita ang log ng mga commit na ating iko-commit sa ating submodule. Sa sandaling na-commit, maaari mong makita ang impormasyong ito pagkatapos ng katotohanan pati na rin kapag nagpatakbo ka ng git log -p.

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

    nag-a-update sa DbConnector para sa pag-aayos ng bug

diff --git a/.gitmodules b/.gitmodules
indeks 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:
  > hulihin ang di-null na tinatapos na mga linya
  > mas matapang na pag-aasikaso ng kamalian
  > mas mahusay na kalakaran ng db
  > mas mahusay na kalakaran ng koneksyon

Bilang default, susubukan ng Git na i-update ang lahat ng iyong mga submodule kapag nagpatakbo ka ng git submodule update --remote kaya kung mayroon kang maraming mga ito, maaari mong gustuhin na ipasa ang pangalan ng submodule lamang na gusto mong subukang i-update.

Pagtatrabaho sa isang Submodule

Malamang na kung gumagamit ka ng mga submodule, ginagawa mo ito dahil gusto mo talagang magtrabaho sa code sa submodule sa parehong oras habang nagtatrabaho ka sa code sa pangunahing proyekto (o sa ilang mga submodule). Kung hindi man ay malamang na ikaw ay gumamit ng isang mas simple na sistema ng pamamahala ng dependensya (tulad ng Maven o Rubygems).

Kaya ngayon ay talakayin natin ang isang halimbawa ng paggawa ng mga pagbabago sa submodule sa parehong oras sa pangunahing proyekto at pag-commit at paglalathala sa mga pagbabago iyon sa parehong oras.

Sa ngayon, kapag pinatakbo natin ang utos na git submodule update upang makuha ang mga pagbabago mula sa mga repositoryo ng submodule, makakakuha ang Git ng mga pagbabago at i-update ang mga file sa subdirectory ngunit iiwan ang sub-repository sa tinatawag na estado na `nakahiwalay na HEAD''. Ito ay nangangahulugan na walang lokal na tinatrabahuang branch (tulad ng ``master'', halimbawa) na nagsusubaybay ng mga pagbabago. Ng walang tinatrabahuang branch na nagsusubaybay ng mga pagbabago, iyon ay nangangahulugan na kahit na ikaw ay mag-commit ng mga pagbabago sa submodule, ang mga pagbabagong iyon ay posibleng mawala sa susunod na patakbuhin mo ang `git submodule update. Kailangan mong gawin ang ilang mga dagdag na hakbang kung nais mong baguhin ang mga submodule na susubaybayan.

Para ma-set up ang iyong submodule na maging mas madaling pasukin at i-hack, kailangan mong gawin ang dalawang bagay. Kailangan mong puntahan ang bawat submodule at suriin ang isang branch na tatrabahuin. Pagkatapos ay kailangan mong sabihan ang Git kung ano ang gagawin kung may ginawa kang mga pagbabago at pagkatapos ay ipu-pull ng git submodule update --remote sa bagong trabaho mula sa upstream. Ang mga pagpipilian ay maaari kang mag-merge ng mg ito sa iyong lokal na trabaho, o subukang i-rebase ang iyong lokal na trabaho sa itaas ng mga bagong pagbabago.

Una sa lahat, puntahan natin ang ating submodule na direktoryo at suriin ang isang branch.

$ git checkout stable
Pinalitan sa branch na 'stable'

Subukan natin ito sa opsyon na “merge”. Upang itakda ito nang mano-mano, maaari lamang nating idagdag ang pagpipilian na --merge sa ating update na pagtawag. Dito, makikita natin na may isang pagbabago sa server para sa submodule na ito at ito ay nai-merge.

$ git submodule update --remote --merge
remote: Pagbibilang ng mga bagay: 4, tapos na.\
remote: Pagko-compress ng mga bagay: 100% (2/2), tapos na.
remote: Kabuuan 4 (delta 2), muling nagamit 4 (delta 2)
Pag-aalis ng laman ng mga bagay: 100% (4/4), tapos na.
Mula sa https://github.com/chaconinc/DbConnector
   c87d55d..92c7337  stable     -> origin/stable
Nag-a-update sa c87d55d..92c7337
Fast-forward
 src/main.c | 1 +
 1 file na nabago, 1 pagsisingit(+)
Submodule path 'DbConnector': na-merge sa '92c7337b30ef9e0893e758dac2459d07362ab5ea'

Kung pupuntahan natin ang DbConnector na direktoryo, mayroon na tayong mga bagong pagbabago na na-merge sa lokal stable na branch. Ngayon, tingnan natin kung ano ang nangyayari kapag gagawa tayo ng sariling lokal na pagbabago sa library at may ibang tao na mag-push ng iba pang pagbabago na upstream sa parehong oras.

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

Ngayon kung ating ia-update ang ating submodule, makikita natin kung ano ang nangyayari kapag gumawa tayo ng isang lokal na pagbabago at ang upstream din ay may isang pagbabago na kailangan nating isama.

$ git submodule update --remote --rebase
Una, nagri-rewind sa head upang ulitin ang iyong trabaho sa itaas nito...
Naglalapat: unicode na suporta
Submodule path 'DbConnector': ni-rebase sa '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

Kung nalimutan mo ang --rebase o --merge, ia-update lamang ng Git ang submodule sa kung anuman ang nasa server at ilagay muli ang iyong proyekto sa isang tinanggal na HEAD na estado.

$ git submodule update --remote
Submodule path 'DbConnector': nag-check out sa '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

Kung mangyayari ito, huwag mag-alala, maaari kang bumalik sa direktoryo at suriing muli ang iyong branch (kung saan ay naglalaman pa rin ng iyong trabaho) at i-merge o i-rebase nang mano-mano ang origin/stable (o kung anuman ang gustong mong remote na branch).

Kung hindi ka pa naka-commit ng iyong mga pagbabago sa iyong submodule at nagpatakbo ka ng isang update ng submodule na magiging sanhi ng mga isyu, kukunin ng Git ang mgs pagbabago ngunit hindi nito sasapawan ang mga hindi na-save na trabaho sa iyong submodule na direktoryo.

$ git submodule update --remote
remote: Pagbibilang ng mga bagay: 4, tapos na.
remote: Pagko-compress ng mga bagay: 100% (3/3), tapos na.
remote: Kabuuan 4 (delta 0), muling nagamit 4 (delta 0)
Pag-aalis ng laman ng mga bagay: 100% (4/4), tapos na.
Mula sa https://github.com/chaconinc/DbConnector
   5d60ef9..c75e92a  stable     -> origin/stable
kamalian: Ang lokal na mga pagbabago sa mga sumusunod na mga file ay masasapawan ng checkout:
	scripts/setup.sh
Pakiusap, i-commit ang iyong mga pagbabago o i-stash ang mga ito bago makapagpalit ng mga branch.
Nag-uurong
Hindi ma-checkout ang 'c75e92a2b3855c9e5b66f915308390d9db204aca' sa path ng submodule na 'DbConnector'

Kung gumawa ka ng mga pagbabago na sumasalungat sa ilang nabago na upstream, ipapaalam sa iyo ng Git kapag pinatakbo mo ang update.

$ git submodule update --remote --merge
Awtomatikong pagsasama sa scripts/setup.sh
SALUNGATAN (nilalaman): Kasalungatan sa pag-merge sa scripts/setup.sh
Nagtala ng preimage para sa 'scripts/setup.sh'
Nabigo ang awtomatikong pag-merge; ayusin ang mga kasalungtan at pagkatapos i-commit ang resulta.
Hindi ma-merge ang 'c75e92a2b3855c9e5b66f915308390d9db204aca' sa path ng submodule na 'DbConnector'

Maaari kang magpunta sa submodule na direktoryo at ayusin ang salungatan tulad ng karaniwan mong ginagawa.

Paglalathala sa mga Pagbabago ng Submodule

Ngayon ay mayroon na tayong iilang mga pagbabago sa ating submodule na direktoryo. Ilan sa mga ito ay dinala mula sa upstream sa pamamagitan ng ating mga update at ang iba ay ginawa sa lokal at hindi pa magagamit ng sinuman dahil hindi pa natin na-push ang mga ito.

$ git diff
Submodule DbConnector c87d55d..82d2ad3:
  > Nag-merge mula sa origin/stable
  > ina-update ang setup script
  > unicode na suporta
  > tinanggal ang hindi kinakailangang pamamaraan
  > nagdagdag ng bagong opsyon para sa conn pooling

Kung tayo ay nag-commit sa pangunahing proyekto at pi-nush ito, nang wala ring pag-push sa mga pagbabago ng submodule, ang ibang tao na nagsusubok na mag-check out ng mga pagbabago ay magkakaroon ng problema dahil wala silang paraan upang makuha ang mga naka-dependeng pagbabago sa submodule. Ang mga pagbabagong iyon ay umiiral lamang sa ating lokal na kopya.

Para makasiguro na hindi na ito mangyayari, maaari mong hilingin sa Git na suriin na ang lahat ng iyong mga submodile ay na-push nang maayos bago mag-push sa pangunahing proyekto. Ang utos git push ay tumatanggap ng argumento na --recurse-submodules na kung saan maaari itakda ang alinman sa “check” o “on-demand”. Ang opsyon na “check” ay simpleng bibigo sa push kung anuman sa mga na-commit na pagbabago ng submodule ay hindi na-push.

$ git push --recurse-submodules=check
Ang mga sumusunod na mga path ng submodule ay naglalaman ng mga pagbabago na maaaring
hindi matagpuan sa anumang remote:
  DbConnector

Pakisubukan

	git push --recurse-submodules=on-demand

o mag-cd sa path at gamitin ang

	git push

upang ma-push ang mga ito sa isang remote.

Tulad ng iyong nakikita, nagbibigay din ito sa atin ng ilang kapaki-pakinabang na payo tungkol sa kung ano ang maaari nating gawin sa susunod. Ang simpleng pagpipilian ay pumunta sa bawat submodule at mano-manong i-push sa mga remote upang matiyak na magagamit ang mga ito sa labas at pagkatapos ay subukang muli ang push na ito. Kung nais mong suriin ang kilos na mangyayari para sa lahat ng push, maaari mong gawing default ang kilos na ito sa pamamagitan ng paggawa ng git config push.recurseSubmodules check.

Ang isa pang opsyon ay ang paggamit ng “on-demand” na halaga, na susubukan na gawin ito para sa iyo.

$ git push --recurse-submodules=on-demand
Pushing submodule 'DbConnector'
Pagbibilang ng mga bagay: 9, tapos na.
Delta compression using up to 8 threads.
Pagko-compress ng mga bagay: 100% (8/8), tapos na.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, tapos na.
Total 9 (delta 3), muling nagamit 0 (delta 0)
Sa https://github.com/chaconinc/DbConnector
   c75e92a..82d2ad3  stable -> stable
   Pagbibilang ng mga bagay: 2, tapos na.
Delta compression na gumagamit ng hanggap 8 mga thread.
Pagko-compress ng mga bagay: 100% (2/2), tapos na.
Pagsusulat ng mga bagao: 100% (2/2), 266 bytes | 0 bytes/s, tapos na.
Total 2 (delta 1), muling nagamit 0 (delta 0)
Sa https://github.com/chaconinc/MainProject
   3d6d338..9a377d1  master -> master

Tulad ng makikita mo doon, nagpunta si Git sa module ng DbConnector at nag-push ito bago mag-push sa pangunahing proyekto. Kung ang submodule push ay nabigo para sa ilang kadahilanan, ang pangunahing push ng proyekto ay mabibigo din. Maaari mong gawing default ang kilos na ito sa pamamagitan ng paggawa ng git config push.recurseSubmodules on-demand.

Pag-merge sa mga Pagbabago ng Submodule

Kung binago mo ang isang reperensya ng submodule sa parehong oras bilang ibang tao, maaari kang makaranas ng ilang mga problema. Iyon ay, kung ang mga kasaysayan ng submodule ay magkaiba at na-commit sa mga magkaibang branch sa isang superproject, maaaring tumagal nang kaunti na maayos ito.

Kung ang isa sa mga commit ay isang direktang ninuno ng iba pan (isang mabilis na pagsasama), pagkatapos ay piliin lamang ng Git ang huli para sa pagsasama, upang ito ay gumana.

Ang Git ay hindi magtatangkang kahit isang maliit na pag-merge para sa iyo, gayunpaman. Kung ang submodule ay gumagawa ng magkaiba at kailangang i-merge, makakakuha ka ng isang bagay na mukhang ganito:

$ git pull
remote: Pagbibilang ng mga bagay: 2, tapos na.
remote: Pagko-compress ng mga bagay: 100% (1/1), tapos na.
remote: Kabuuan 2 (delta 1), muling nagamit 2 (delta 1)
Pag-aalis ng laman ng mga bagay: 100% (2/2), tapos na.
Mula sa https://github.com/chaconinc/MainProject
   9a377d1..eb974f8  master     -> origin/master
Nagkukuha sa submodule DbConnector
babala: Bigong na-merge ang submodule DbConnector (ang pag-merge ng sumusunod na mga commit ay hindi natagpuan)
Awtomatikong pag-merge sa DbConnector
SALUNGATAN (submodule): Kasalungatan sa pag-merge sa DbConnector
Nabigo ang awtomatikong pag-merge; ayusin ang mga salungatan at pagkatapos i-commit ang resulta.

So basically what has happened here is that Git has figured out that the two branches record points in the submodule’s history that are divergent and need to be merged. It explains it as “merge following commits not found”, which is confusing but we’ll explain why that is in a bit.

To solve the problem, you need to figure out what state the submodule should be in. Strangely, Git doesn’t really give you much information to help out here, not even the SHA-1s of the commits of both sides of the history. Fortunately, it’s simple to figure out. If you run git diff you can get the SHA-1s of the commits recorded in both branches you were trying to merge.

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

So, in this case, eb41d76 is the commit in our submodule that we had and c771610 is the commit that upstream had. If we go into our submodule directory, it should already be on eb41d76 as the merge would not have touched it. If for whatever reason it’s not, you can simply create and checkout a branch pointing to it.

What is important is the SHA-1 of the commit from the other side. This is what you’ll have to merge in and resolve. You can either just try the merge with the SHA-1 directly, or you can create a branch for it and then try to merge that in. We would suggest the latter, even if only to make a nicer merge commit message.

So, we will go into our submodule directory, create a branch based on that second SHA-1 from git diff and manually merge.

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

We got an actual merge conflict here, so if we resolve that and commit it, then we can simply update the main project with the result.

$ 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. First we resolve the conflict

  2. Then we go back to the main project directory

  3. We can check the SHA-1s again

  4. Resolve the conflicted submodule entry

  5. Commit our merge

It can be a bit confusing, but it’s really not very hard.

Interestingly, there is another case that Git handles. If a merge commit exists in the submodule directory that contains both commits in its history, Git will suggest it to you as a possible solution. It sees that at some point in the submodule project, someone merged branches containing these two commits, so maybe you’ll want that one.

This is why the error message from before was “merge following commits not found”, because it could not do this. It’s confusing because who would expect it to try to do this?

If it does find a single acceptable merge commit, you’ll see something like this:

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

What it’s suggesting that you do is to update the index like you had run git add, which clears the conflict, then commit. You probably shouldn’t do this though. You can just as easily go into the submodule directory, see what the difference is, fast-forward to this commit, test it properly, and then commit it.

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

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

This accomplishes the same thing, but at least this way you can verify that it works and you have the code in your submodule directory when you’re done.

Submodule Tips

There are a few things you can do to make working with submodules a little easier.

Submodule Foreach

There is a foreach submodule command to run some arbitrary command in each submodule. This can be really helpful if you have a number of submodules in the same project.

For example, let’s say we want to start a new feature or do a bugfix and we have work going on in several submodules. We can easily stash all the work in all our submodules.

$ 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

Then we can create a new branch and switch to it in all our submodules.

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

You get the idea. One really useful thing you can do is produce a nice unified diff of what is changed in your main project and all your subprojects as well.

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

Here we can see that we’re defining a function in a submodule and calling it in the main project. This is obviously a simplified example, but hopefully it gives you an idea of how this may be useful.

Useful Aliases

You may want to set up some aliases for some of these commands as they can be quite long and you can’t set configuration options for most of them to make them defaults. We covered setting up Git aliases in Mga Alyas sa Git, but here is an example of what you may want to set up if you plan on working with submodules in Git a lot.

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

This way you can simply run git supdate when you want to update your submodules, or git spush to push with submodule dependency checking.

Issues with Submodules

Using submodules isn’t without hiccups, however.

For instance switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory:

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

Removing the directory isn’t difficult, but it can be a bit confusing to have that in there. If you do remove it and then switch back to the branch that has that submodule, you will need to run submodule update --init to repopulate it.

$ 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

Again, not really very difficult, but it can be a little confusing.

The other main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run submodule add, Git yells at you:

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

You have to unstage the CryptoLibrary directory first. Then you can add the submodule:

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

Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule – you get this error:

$ 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

You can force it to switch with checkout -f, but be careful that you don’t have unsaved changes in there as they could be overwritten with that command.

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

Then, when you switch back, you get an empty CryptoLibrary directory for some reason and git submodule update may not fix it either. You may need to go into your submodule directory and run a git checkout . to get all your files back. You could run this in a submodule foreach script to run it for multiple submodules.

It’s important to note that submodules these days keep all their Git data in the top project’s .git directory, so unlike much older versions of Git, destroying a submodule directory won’t lose any commits or branches that you had.

With these tools, submodules can be a fairly simple and effective method for developing on several related but still separate projects simultaneously.