Git
Chapters ▾ 2nd Edition

3.1 Pag-branch ng Git - Mga Branch sa Maikling Salita

Halos bawat VCS ay mayroong ilang porma ng suporta ng pag-branch. Ang pag-branch ay nangangahulugang ikaw ay hihiwalay mula sa pangunahing linya ng pagde-develop at magpapatuloy sa paggawa ng trabaho na hindi nanggugulo sa pangunahing linyang iyon. Sa karamihan ng mga kasangkapan ng VCS, ito ay isang medyo magastos na proseso, na madalas ay kinakailangan mong gumawa ng isang panibagong kopya ng iyong source code na direktoryo, na maaaring tumagal ng mahabang panahon para sa mga malalaking proyekto.

Iilang mga tao ay tumutukoy sa modelo ng pag-branch ng Git bilang “mamamatay na tampok,” nito at tiyak nitong tinatakda ang Git na hiwalay sa VCS na komunidad. Bakit ito sobrang espesyal? Ang paraan ng pag-branch ng Git ay hindi kapani-paniwalang magaan, na ginagawa ang mga operasyon ng pag-branch na halos madalian, at ang pagpapalit pabalik at patungo sa pagitan ng mga branch sa pangkalahatan na kasing bilis. Hindi katulad ng karamihan sa ibang mga VCS, ang Git ay hinihikayat ang mga daloy ng trabaho na madalas na nagba-branch at nagme-merge, kahit maramihan pa sa isang araw. Ang pagkaunawa at pagkadalubhasa ng tampok na ito ay nagbibigay sa iyo ng isang makapangyarihan at natatanging kasangkapan at maaaring buong baguhin ang paraan ng iyong pagde-develop.

Mga Branch sa Maikling Salita

Upang talagang maintindihan ang paraan ng Git sa paggawa ng pag-branch, kailangan nating umatras at suriin kung paano nag-iimbak ng data ang Git.

Kung iyong natatandaan mula sa Pagsisimula, ang Git ay hindi nag-iimbak ng data bilang isang serye ng mga changeset o mga kaibahan, ngunit sa halip ay isang serye ng mga snapshot.

Kapag ikaw ay gumawa ng isang commit, ang Git ay nag-iimbak ng isang commit na object na naglalaman ng isang pointer sa snapshot ng nilalaman na iyong na-stage. Ang object na ito ay naglalaman din ng pangalan at email ng may-akda, ang mensahe na iyong na-type, at mga pointer sa commit o mga commit na direktang dumating bago ang commit na ito (magulang nito o mga magulang): zero na mga magulang para sa paunang commit, isang magulang para sa isang normal na commit, at maramihang mga magulang para sa isang commit na nagreresulta mula sa isang pag-merge ng dalawa o higit pang mga branch.

Upang maisalarawan ito, ipagpalagay natin na ikaw ay may isang direktoryong naglalaman ng tatlong mga file, at na-stage mo lahat ang mga ito at na-commit. Ang pag-stage ng mga file ay nagkakalkula ng isang checksum para sa bawat isa (ang SHA-1 na hash na nabanggit namin sa Pagsisimula), nag-iimbak sa bersyong iyon ng file sa Git na repositoryo (tinutukoy ng Git ang mga ito bilang mga blob), at nagdaragdag ng checksum na iyon sa staging na lawak:

$ git add README test.rb LICENSE
$ git commit -m 'Ang paunang commit ng aking proyekto'

Kapag nagawa mo ang commit sa pamamagitan ng pagpapatakbo ng git commit, ang Git ay ichi-checksum ang bawat subdirectory (sa kasong ito, ang root na direktoryo ng proyekto lamang) at iiimbak ang mga tree object na iyon sa Git na repositoryo. Ang Git ngayon ay gagawa ng isang commit na object na may metadata at isang pointer sa root project ng tree upang ito ay makalikha muli ng snapshot na iyon kapag kinakailangan.

Ang iyong Git na repositoryo ngayon ay naglalaman ng limang mga object: isang blob para sa mga nilalaman ng bawat isa sa iyong tatlong mga file, isang tree na naglilista ng mga nilalaman ng direktoryo at tinutukoy kung anong mga pangalan ng file ang naimbak bilang mga blob, at isang commit na may pointer sa root tree at lahat ng mga metadata ng commit.

Isang commit at ang tree nito.
Figure 9. Isang commit at ang tree nito

Kung gagawa ka ng ilang mga pagbabago at magku-commit muli, ang susunod na commit ay mag-iimbak ng isang pointer sa commit na dumating kaagad bago nito.

Mga Commit at mga magulang nila.
Figure 10. Mga Commit at mga magulang nila

Ang isang branch sa Git ay simpleng isang magaan na nagagalaw na pointer sa isa sa mga commit na ito. Ang default na pangalan ng branch sa Git ay master. Habang nagsisimula kang gumawa ng mga commit, ikaw ay bibigyan ng isang master na branch na tumuturo sa huling commit na ginawa mo. Sa bawat pagkakataon na ikaw ay mag-commit, awtomatikong ginagalaw ito nang pasulong.

Ang “master” na branch sa Git ay hindi isang espesyal na branch. Ito ay eksaktong kapareho ng anumang ibang branch. Ang dahilan lamang kung bakit halos bawat repositoryo ay mayroong isa ay ang git init na utos na gumagawa nito bilang default at karamihan sa mga tao ay hindi mag-aabalang baguhin ito.

Isang branch at ang kasaysayan ng commit nito.
Figure 11. Isang branch at ang kasaysayan ng commit nito

Paggawa ng isang Bagong Branch

Ano ang mangyayari kung ikaw ay gumawa ng isang bagong branch? Ang paggawa nito ay lumilikha ng isang bagong pointer para sa iyo upang ilipat kahit saan. Sabihin nating gumawa ka ng isang bagong branch na tinatawag na testing. Gagawin mo ito gamit ang git branch na utos:

$ git branch testing

Gumagawa ito ng isang bagong pointer sa parehong commit kung saan nandoon ka.

Dalawang mga branch na tumuturo sa parehong serye ng mga commit.
Figure 12. Dalawang mga branch na tumuturo sa parehong serye ng mga commit

Paano nalalaman ng Git kung anong branch ka naroroon? Ito ay nagpapanatili ng isang espesyal na pointer na tinatawag na HEAD. Tandaan na ito ay mas kakaiba kaysa sa konsepto ng HEAD sa ibang mga VCS na nakasanayan mo, katulad ng Subversion o CVS. Sa Git, ito ay isang pointer sa lokal na branch kung saan nandoon ka. Sa kasong ito, nasa master ka pa rin. Ang git branch na utos ay gumawa lamang ng isang bagong branch — hindi ito lumipat sa branch na iyon.

Ang HEAD na tumuturo sa isang branch.
Figure 13. Ang HEAD na tumuturo sa isang branch

Madali mong makikita ito sa pamamagitan ng pagpapatakbo ng isang simpleng git log na utos na nagpapakita sa iyo kung saan nakaturo ang mga pointer ng branch. Ang opsyon ay tinatawag na --decorate.

$ git log --oneline --decorate
f30ab (HEAD -> master, testing) add feature #32 - ability to add new formats to the central interface
34ac2 Fixed bug #1328 - stack overflow under certain conditions
98ca9 The initial commit of my project

Maaari mong tingnan ang “master” at “testing” na mga branch na nasa sunod ng f30ab na commit.

Paglilipat ng mga Branch

Upang lumipat sa isang umiiral na branch, patakbuhin mo ang git checkout na utos. Lumipat tayo sa bagong testing na branch:

$ git checkout testing

Nililipat nito ang HEAD upang tumuro sa testing na branch.

Ang HEAD ay tumuturo sa kasalukuyang branch.
Figure 14. Ang HEAD ay tumuturo sa kasalukuyang branch

Ano ang kabuluhan nito? Gumawa tayo ng iba pang commit:

$ vim test.rb
$ git commit -a -m 'gumawa ng isang pagbabago'
Ang HEAD na branch ay ginagalaw nang pasulong kapag may isang commit ay naggawa.
Figure 15. Ang HEAD na branch ay ginagalaw nang pasulong kapag may isang commit ay naggawa

Ito ay kawili-wili, dahil ngayon ang iyong testing na branch ay nilipat nang pasulong, ngunit ang iyong master na branch ay nakaturo pa rin sa commit kung saan nandoon ka noong pinatakbo mo ang git checkout upang maglipat ng mga branch. Lumipat tayo pabalik sa master na branch:

$ git checkout master
Ang HEAD ay lilipat kapag ikaw nag-checkout.
Figure 16. Ang HEAD ay lilipat kapag ikaw nag-checkout

Ang utos na iyon ay gumawa ng dalawang bagay. Inilipat nito ang HEAD na pointer pabalik upang tumuro sa master na branch, at ibinalik nito ang mga file sa iyong tinatrabaho na repositoryo pabalik sa snapshot na tinuturo ng master. Ito ay nangangahulugan ding ang mga pagbabago na ginawa mo mula sa puntong ito nang pasulong ay magkaiba mula sa isang mas matandang bersyon ng proyekto. Mahalagang iri-rewind nito ang trabahong nagawa mo sa iyong testing na branch upang maaaring kang pumunta sa iba pang direksyon.

Example 4. Ang paglilipat ng mga branch ay nagbabago ng mga file sa iyong tinatrabahong direktoryo

Importanteng tandaan na kapag ikaw ay lumilipat ng mga branch sa Git, ang mga file sa iyong tinatrabaho na direktoryo ay mababago. Kung ikaw ay maglilipat sa isang mas matandang branch, ang iyong tinatrabaho na direktoryo ay maibabalik upang magmukha ito katulad sa huling pagkakataong ikaw ay nag-commit sa branch na iyon. Kung ang Git ay hindi maaaring malinis na gumawa nito, hindi ka hahayaan nitong maglipat.

Gumawa tayo ng ilang kaunting mga pagbabago at mag-commit muli:

$ vim test.rb
$ git commit -a -m 'gumawa ng iba pang mga pagbabago'

Ngayon ang kasaysayan ng iyong proyekto ay humiwalay na (tingnan ang Divergent na kasaysayan). Ikaw ay lumikha at lumipat sa isang branch, gumawa ng ilang trabaho dito, at pagkatapos ay lumipat pabalik sa iyong pangunahing branch at gumawa ng iba pang trabaho. Parehong ang mga pagbabagong iyon ay magkaiba sa hiwalay na mga branch: maaari kang lumipat pabalik at patungo sa pagitan ng mga branch at pagsamahin sila kapag handa ka na. At ginawa mo lahat iyon gamit ang simpleng branch, checkout, at commit na mga utos.

Divergent na kasaysayan.
Figure 17. Divergent na kasaysayan

Maaari mo ring madaling tingnan ito gamit ang git log na utos. Kung patatakbuhin mo ang git log --oneline --decorate --graph --all ito ay maglilimbag ng kasaysayan ng iyong mga commmit, na nagpapakita kung nasaan ang iyong mga branch pointer at kung paano humiwalay ang iyong kasaysayan.

$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

Dahil ang isang branch sa Git ay talagang isang simpleng file na naglalaman ng 40 na karakter na SHA-1 na checksum ng commit na itinuturo nito, ang mga branch ay mura lamang gawin at sirain. Ang paggawa ng isang bagong branch ay kasing dali at kasing simple katulad ng pagsulat ng 41 bytes sa isang file (40 na mga karakter at isang newline).

Ito ay nasa matingkad na kaibahan sa paraan ng karamihan sa mas matandang mga kasangkapan ng VCS na branch, na nagsasangkot ng pagkopya sa lahat ng mga file ng proyekto sa isang pangalawang direktoryo. Ito ay maaaring magtagal ng ilang segundo o kahit mga minuto, depende sa laki ng proyekto, samantalang sa Git ang proseso ay palaging madalian. Gayundin, dahil nagtatala tayo sa mga magulang kapag tayo ay magku-commit, ang paghahanap ng isang nararapat na merge base para sa pag-merge ay awtomatikong nagagawa para sa atin at kadalasang sobrang madaling gawin. Ang mga tampok na ito ay nakakatulong maghikayat sa mga developer upang madalas gumawa at gumamit ng mga branch.

Tingnan natin kung bakit dapat mong gawin ito.