--- title: GIT — EITB 2022 subtitle: Erabilera aurreratua license: CC-BY-NC-SA 4.0 author: Ekaitz Zarraga - ElenQ Technology links-as-notes: true lang: basque polyglossia-lang: name: basque how-to: pandoc -f markdown+smart -t beamer % -o pdf/2.pdf --pdf-engine=xelatex --template=./template.tex ... ## Lizentzia - CC-BY-SA 4.0 - Dokumentu honen edukiak eta irudiak [**Pro Git (Scott Chacon, Ben Straub)**](https://git-scm.com/book/en/v2) liburutik atera dira. # Git zerbitzarian ## Git zerbitzarian: sarrera Lankideen artean kodea partekatzeko zerbitzari bat izatea ideia ona da: beti dago eskuragarri, kudeaketa sinplifikatzen du, etab. Zerbitzarietan *bare repository*-ak erabiltzen dira. Hauek ez dute *working directory*-rik. `.git` direktorioan dagoena bakarrik dute. - `git init --bare` *bare repository* bat eratzeko. ## Protokoloak Protokolo ezberdinak erabili daitezke repositorioen arteko komunikazioa egiteko: - **Local**: beste fitxategi batean dagoen repositorioa. `git clone /path/to/file`-en bitartez klonatu daiteke. - **HTTP**: *smart* edo *dumb* moduak ditu. *Smart*-ek baimenak kudeatu ditzake. *Dumb*-ek fitxategiak eman baino ez du egiten. - **SSH**: Askotan erabiltzen da. Erabiltzaile bat behar du.\ `git clone ssh://[@]/.git` erabiliz. SCP-ren modu laburra erabili daiteke: `[@]:.git`. - **Git**: SSH-ren antzekoa da baina autentikazio barik. ## Protokoloak — Adibide bat Nire [zerbitzaria](https://git.elenq.tech/): - Web bitartez proiektuak aztertu daitezke. `cgit` erabiltzen du. Beste aukera batzuk daude: `gitweb` adibidez, Git-ekin datorrena. - Proiektu publikoak Git-en bitartez zerbitzatzen dira edonork kopiatu ahal izateko. - Idazteko baimena SSH-ren bitartez zerbitzatzen da, baimena kontrolatu ahal izateko. *Liburuan azaltzen da gauza hauek nola muntatu.* # Git workflowak ## Workflow zentralizatua Git-en izaera banatuak *workflow* ezberdin asko ahalbidetzen ditu baina gaur egun *workflow zentralizatua* erabiltzen da gehien, Web zerbitzu integratuen arrakastagatik (Github, Gitlab, Gitea...) non tiketak eta kodea kudeatzeko aukera dagoen. ![Bezero guztiak zerbitzariarekin sinkronizatzen dira](img/centralized_workflow.png){ height=140px } ## Email-en bitartez Software libre proiektu asko oraindik emailean oinarritzen dira aldaketak proposatzeko. - `git format-patch ` aukeratutako commitak patch fitxategietan idazteko - `git send-email ` patch-ak emailez bidaltzeko (konfigurazioa behar du) - `git am ` jasotako patch-ak proiektuan commiteatzeko - `git apply ` jasotako patch-ak aldaketa moduan aplikatzeko # Git tresna aurreratuak ## Commit selektoreak Giten komando asko commit askotan aplikatu daitezke. Gitek commit selektore ezberdinak aplikatzea ahalbidetzen du commitak aukeratzeko. - `git show ` aukeratutako objektuak erakusteko *Ikusi `git show`-n laguntza. Objetu ezberdinak erakutsi ditzake* *Ikusi `git rev-parse`-n laguntza, selektoreen sintaxia azaltzen baitu.* ## Commit selektoreak — I - **SHA-1 hasha**: commit identifikadorea bera erabili daiteke selektore moduan. Oso luzeak direnez, hasierako karaktereak bakarrik erabili daitezke anbiguoak ez badira. `git log --abbrev-commit` commit id-aren laburpena egiten du. - **Adarren burua**: adarraren izena erabiltzean, adarraren buruaren commita aukeratzen da: `git show `. Adarraren buruaren commit id-a ateratzeko: `git rev-parse `. - **Reflog-a**: Reflog-a repositorioaren egoera gordetzen du historiko baten modura. Reflog-aren sarreren izenak erabili daitezke commit selektore moduan. Gainera denbora erabili daiteke: `HEAD@{yesterday}`. - `git reflog` reflog-aren sarrerak erakusteko ## Commit selektoreak — II - **Gurasoak**: aukeratutako commiten gurasoak eta aitzindariak lortu daitezke: - Txapela (`^`) erabiliz aukeratutako commitaren gurasoak lortu daitezke. Adibidez: `git show HEAD^`. Merge commitetan (guraso asko dituztenak) gurasoen artean aukeratzeko `^` eta gero gurasoaren zenbakia jarri daiteke guraso konkretua lortzeko. - Tildea (`~`) erabiliz aitzindariak lortu daitezke. Zenbaki bat gehituta aurreko aitzindaria lortu daiteke. Adibidez `git show HEAD~2`: aurreko commitaren aurrekoa lortu. ## Commit selektoreak — III - **Commit tartea**: bi commiten arteko tartea lortzeko: `..`\ Adarrekin ere egin daiteke, adar batetik besteraren burura ailegatzeko behar diren commitak lortzeko:\ ![](img/double-dot.png){width=350px}\ - `git log master..experiment` => D C\ - `git log experiment..master` => F E ## Commit selektoreak — IV - **Puntu tripleak**: bi adarretatik ailegatu ahal diren commitak, baina amankomunean ez daudenak: ![](img/double-dot.png){width=350px}\ - `git log master...experiment` => F E D C\ - Zein adarretik datozen ikusteko `--left-right`: ``` git log master...experiment --left-right < F < E > D > C ``` ## Commit selektoreak — V - **Puntu multipleak**: eskaera konplexuagoak egiteko `^` (aurretik) edo `--not` erabili daiteke, adar horren commitak baztertzeko: - Hurrengo komandoak baliokideak dira: ``` git log refA..refB git log ^refA refB git log refB --not refA ``` - Hurrengo komandoek `refA`tik eta `refB`tik lortu daitezkeen commitak hartzen dituzte, `refC`tik lortu daitezkeenak baztertuz: ``` git log refA refB ^refC git log refA refB --not refC ``` ## Staging interaktiboa - `git add --interactive|-i` stage-a modu interaktiboan kudeatzeko - `git add --patch|-p` gehituko diren aldaketak modu interaktiboan aukeratzeko. Blokez bloke *Komando askotan erabili daitezken aukerak dira hauek.\ Oso erabilgarriak dira.* ## Stash-a Aldaketak gordetzeko zaku bat da *stash*a. Trakeatu gabeko aldaketak bertan sartu daitezke repositoria aldatu/berriztu/mergeatu eta gero aldaketak berriro adar berdinean edo beste batean ateratzeko. - `git stash [push]` direktorioa garbitu eta aldaketak *stash*era sartzeko - `git stash list` *stash*aren sarrerak ikusteko - `git stash apply []`-k aukeratutako sarrera (defektuz azkena) aplikatzen dio *working directory*ari. - `git stash drop []` aukeratutako *stash* sarrera ezabatzeko - `git stash pop` = `git stash apply` + `git stash drop` *Push* eta *pop* izenak erabiltzen dira *stash*a LIFO ilara edo stack baten moduan kudeatzen delako. *Gainetik gehitu eta gainetik atera* ## Stash-a — Aukera interesgarriak - `git stash apply --index` egitean *staging area*ra bidaltzen dira *stash*ean *staging area*an zeuden fitxategiak. Defektuz ez dira *stage*era bidaltzen. - `git stash --keep-index` indexatutako (*stage*a) fitxategiak ez dira *stash*era gehitzen, eta *stage*ean mantentzen dira. - `git stash --include-untracked|-u` trackeatuta ez dauden fitxategiak ere *stash*eatzeratzeko. - `git stash --all|-a` baztertutako fitxategiak (`.gitignore`) ere *stash*era gehitzeko - `git stash --patch|-p` *stash*eratzeratzeko aldaketak modu interaktiboan aukeratzeko - `git stash branch []` adar berri bat sortu eta bertan aukeratutako *stash*a aplikatu. Dena ondo badoa *stash*a garbitzen du bukatzerakoan. ## Direktorioa garbitzeko - `git stash --all` egitea direktorio osoa garbitzen du, hau da, trackeatuta ez dauden fitxategiak eta aldaketak *stash*earatzen ditu. - `git clean` egiten antzeko efektu bat lortu daiteke *stash*a erabili gabe, zuzenean aldaketak eta fitxategiak ezabatuz. - `-f` (*force*) beharrezkoa da (defektuz, aukera bat dago desaktibatzeko) nahi gabe ez egiteko > KONTUZ: `git clean --dry-run|-n` erabili badaezpada, zer egingo duen ikusteko > benetan exekutatu baino lehen. ## Sinadurak Commit faltsuak ekiditeko, commitak sinatu daitezke GPG-ren bitartez. > GPG (Gnu Privacy Guard), PGP-ren (Pretty Good Privacy) inplementazio bat da. `user.signkey` konfiguratu behar da. - `git commit -S` commitak sinatzeko - `git tag -s` tagak sinatzeko - `git merge --verify-signatures` sinadurak baieztatzeko mergetan - `git tag -v` tag-en sinadurak baieztatzeko Sinadurak erabiltzen badira, proiektu kide guztiek commitak sinatu behar dituzte. ## Bilaketak - `git grep` espresio erregularren bitartez bilaketak egiteko. `grep` komandoarekin alderatuta azkarragoa da eta historikoan eta indexean bilatzen du. - `git log` logetan bilatzeko funtzio oso interesgarriak daude: - `-S` *pickaxe* funtzioa - `-L` lerroen eboluzioa edo funtzioaren eboluzioa erakusten du ## Historia berridazten - `git commit --amend` mini-rebase baten eragina dauka - `git rebase -i|--interactive ` aukeratutako commitera ailegatzeko behar diren commitak aldatu.\ Editorearen bitartez commit bakoitzarekin zer egin behar duen adierazi ahal zaio: commitak batu, aukeratu, mezua berridatzi, editatu... Aukera batzuek prozesua gelditzen dute. `git rebase --continue` egiten aurrera jarraitzen da, edo `--abort` egiten prozesua ezeztatzen da. Komandoak berak oso ondo azaltzen du nola egin. > KONTUZ: N+1 commit aldatu emaitza gutxienez commit bat behar duelako - `git filter-branch` eta antzeko tresnek aldaketa sakonak egin ditzakete. Adibidez, fitxategi bat commit *guztietatik* kendu. ## Reset eta checkout sakonki — I Reset eta Checkout ulertzeko Giten fitxategien egoerak eta haien arteko trantsizioak ondo ulertu behar dira. Hiru egoera posible daude: 1. HEAD: repositorioaren azkeneko commitaren snapshota 2. Index: *staging area*, hurrengo commitaren proposamena 3. Working Directory: Aldaketak egiten ditugun lekua ![Egoren arteko trantsizioak](img/reset-workflow.png){height=150px} ## Reset eta checkout sakonki — II - `git reset`-ek hiru modu ditu: - `--soft`: HEAD-ak apuntatzen duen adarra mugitu - `--mixed`: `--soft` + egoera indexera pasatu - `--hard`: `--mixed` + egoera *working directory*ra pasatu, bertako aldaketak zapalduz ## Reset eta checkout sakonki — III Grafikoki aztertuz. Repositorio hutsarekin hasita: ![ ](img/reset-ex1.png) ## Reset eta checkout sakonki — IV Fitxategi bat *indexera* gehitu: ![ ](img/reset-ex2.png) ## Reset eta checkout sakonki — V Fitxategia repositorioan idatzi: ![ ](img/reset-ex3.png) ## Reset eta checkout sakonki — VI Fitxategia aldatu: ![ ](img/reset-ex4.png) ## Reset eta checkout sakonki — VII Egoera *indexera* bidali: ![ ](img/reset-ex5.png) ## Reset eta checkout sakonki — VIII Commit berria gehitu: ![ ](img/reset-ex6.png) ## Reset eta checkout sakonki — IX Beste commit bat gehituta: ![ ](img/reset-start.png) ## Reset eta checkout sakonki — IX `--soft`: ![ ](img/reset-soft.png) ## Reset eta checkout sakonki — X `--mixed` (defektuz egiten da): ![ ](img/reset-mixed.png) ## Reset eta checkout sakonki — XI `--hard`: ![ ](img/reset-hard.png) ## Reset eta checkout sakonki — XII Hiru aukeraz aparte, `git reset`-i fitxategi bat sartu ahal zaio. Kasu horretan, lehenengo pausua (HEAD-a mugitzea) ezin da burutu[^head] baina hurrengo pausuak arazo barik egin daitezke. Horrek funtzionamendu interesgarriak ahalbidetzen ditu. - `git reset ` egiten denean, benetan\ `git reset --mixed HEAD ` egiten da.\ 1. ~~HEAD-a mugitu~~ 2. Egoera indexean jarri \ Hau da: **Fitxategia indexetik atera** [^head]: HEADa ezin da erdizka mugitu, edo repositorio osorako mugitzen da edo ez da mugitzen. ## Reset eta checkout sakonki — XIII Grafikoki: ![ ](img/reset-path1.png) ## Reset eta checkout sakonki — XIV Adibide konplexuago bat: `git reset -- ` 1. HEAD-a ezin da mugitu 2. ``-k ``-en daukan egoera indexera sartzen da (`--mixed`). ![ ](img/reset-path3.png){height=220px} ## Reset eta checkout sakonki — XV Ikusitakoa *squash* (commit batzuk bakarrean batu) egiteko erabili daiteke[^rebase-interactive]. - `git reset --soft HEAD~` egitean HEADa atzera eraman daiteke, indexean aldaketak mantenduz. `git commit` eginda, aldaketak repositorioan idatzi daitezke, denak **commit bakarrean**. [^rebase-interactive]: Beste aukera `git rebase --interactive|-i` erabiltzea da ## Reset eta checkout sakonki — XVI Grafikoki: ![ ](img/reset-squash-r1.png) ## Reset eta checkout sakonki — XVII HEADa mugitu aldaketak indexean mantenduz: ![ ](img/reset-squash-r2.png) ## Reset eta checkout sakonki — XIX Commit berria sartu aldaketa guztiekin batera eginda ![ ](img/reset-squash-r3.png) ## Reset eta checkout sakonki — XX `checkout` eta `reset` antzekoak dira, baina ez dira berdinak: - `git checkout`-ek ez du HEADa eraldatzen. HEAD erreferentzia non apuntatzen duen aldatzen du, ez du azpian dagoen adarra aldatzen. - `git checkout ` eta `git reset --hard ` ia berdinak dira baina `checkout`ek ez ditu aldaketak zuzenean zapaltzen. - `git checkout ` fitxategian zeuden aldaketak **zapaltzen ditu** *working directory*an, `git reset --hard`en antzera. KONTUZ Bietan `--patch` erabili daiteke zatika egiteko. ## Reset eta checkout sakonki — XXI `git checkout` vs `git reset`en efektua HEADan: ![ ](img/reset-checkout.png) ## Merge aurreratuak, konfliktoen kudeaketa — I Mergeak egin baino lehen, ideia ona da *working directory*a garbitzea (`git stash`), horrela, zeozer txarto badabil atzera buelta eman daiteke aldaketak galdu barik. - `git merge --abort` mergea txarto doanean (konfliktoak) mergea ezeztatzen du. > KONTUZ: aldaketak badaude *working directory*-an ezin izango da abortatu. ## Merge aurreratuak, konfliktoen kudeaketa — II Konfliktoetan Gitek 3 fitxategi ematen ditu: 1. Stage 1: *common ancestor*-a (*base*), bi adarretatik iritsi daitekeen lehengo commita: `git merge-base`-ekin lortu daiteke puntu hau 2. Stage 2: Gure (*ours*) bertsioa, gure adarrean dagoena 3. Stage 3: Haien (*theirs*) bertsioa, mergeatzen dugun adarrean dagoena - `git show ::` stage fitxategiak ikusteko\ - `::`-k blob horren hasha lortzen du. - `git diff`-en bitartez ikusi daitezke, stage-a aukeratuz - `git diff -1|--base` - `git diff -2|--ours` - `git diff -3|--theirs` ## Merge aurreratuak, konfliktoen kudeaketa — III - `git show ::` eginez, stage bakoitzaren fitxategiak fitxategi ezberdinetara idatzi daitezke eskuz prozesatzeko - `git merge-file` erabili daiteke hiru fitxategiak eskuz mergeatzeko. Merge algoritmoak aplikatuko ditu ## Merge aurreratuak, konfliktoen kudeaketa — IV Konfliktoak hobeto aztertzeko tresna batzuk: - `git checkout --conflict` konfliktoa daukan fitxategiaren konflikto markadoreak berriro idazten ditu. Eskuz aldatzean nahi gabe ezabatu ditugunean interesgarria da. - `git checkout --ours/--theirs` egin daiteke alde bat edo bestea ikusteko. - `git log --oneline --left-right --merge` interesgarria da testuingurua ulertzeko. - `git diff` egitean konfliktoetan *combined diff* bat agertzen da, bi zutabeetan ikusten dira aldaketak. - `git show -p|--patch` egitean `--cc` gehitu daiteke *combined diff*a ikusteko ## Merge aurreratuak, konfliktoen kudeaketa — V *Combined diff*-aren itxura, konflikto bat konpontzean: ``` diff --cc hello.rb index 0399cd5,59727f0..0000000 --- a/hello.rb +++ b/hello.rb @@@ -1,7 -1,7 +1,7 @@@ #! /usr/bin/env ruby def hello - puts 'hola world' - puts 'hello mundo' ++ puts 'hola mundo' end ``` Bi zutabe daude `-` eta `+` sinboloekin, alde bakoitzean zer gertatu den ikusteko. ## Merge aurreratuak, konfliktoen kudeaketa — VI Konfliktoetan `diff3` erabiltzea interesgarria izan daiteke, defektuz `merge` erabiltzen da. Atal berri bat gehitzen dio konfliktoari, *base*a bistaratzeko. ``` <<<<<<< ours puts 'hola world' ||||||| base puts 'hello world' ======= puts 'hello mundo' >>>>>>> theirs ``` - `git checkout --conflict=diff3` konfliktoa 3 stagetan bistaratzeko. - `git config --global merge.conflictstyle diff3` konfliktoak 3 bidetan ikusteko ## Mergeak desegiten — I Bi aukera ezberdin dago mergea desegiteko: 1. `git reset --hard HEAD~` (historikoa berridazten du) 2. `git revert -m 1 HEAD` commit bat gehitu aldaketak desegiten dituena. Arazoak datoz adarra berriro mergeatu behar denean. ## Mergeak desegiten — II `git revert -m 1 HEAD` egitean, mergea desegin da beste commit batekin. `^M` eta `C6` eduki berdinak dituzte. Baina `topic` adarraren commitak `master`etik iritsi daitezke. Gitentzat mergeatuta daudela dirudite. KONTUZ ![](img/undomerge-revert.png) ## Mergeak desegiten — III Txarragoa izan daitekeena, aldaketak gehitzen badira, Gitek commit berriak baino ez ditu hartzen, bestea mergeatuta dagoela uste duelako. KONTUZ ![](img/undomerge-revert2.png) ## Mergeak desegiten — IV Arazoa saihesteko, aurreko mergea, desegin duguna, aplikatu behar da. Horretarako, `revert` commita desegin daiteke beste `revert` bat eginez. ![](img/undomerge-revert3.png) Orain, `topic`-en aldaketa guztiak `master`-en daude. ## Mergeen preferentziak `git merge` aukera eta estrategia ezberdinak daude: - `-X` aukerak gehitzeko erabiltzen da. Adibidez: `-Xours` konfliktoak gure aldera ebazteko - `-s` estrategiak gehitzeko. Adibidez: `-s ours` mergea egin beharrean, gure aldeko aldaketak bakarrik aukeratu (Giti ziria sartzeko interesgarria da) Aukerak eta estrategiak ez dira gauza bera. Estrategiak merge algoritmoa definitzen dute. Aukerek algoritmoa konfiguratzen dute. ## Subtree-ak Subtree-ak beste proiektu bat gure proiektuaren barruan sartzeko mekanismo bat dira. Azpiproiektua adar independente batean kudeatzen da: beste remote bat moduan hasierazten da, baina adar berri batean sartu, beste adarrekin zerikusirik ez duena. - `git read-tree` azpiproiektua irakurri eta beste adar baten azpidirektorio moduan hasiarazteko - `git merge -Xsubtree` erabiltzen da *subtree*aren adarra proiektuan arazo barik mergeatzeko - `git diff-tree` azpiproiektuaren aldaketak aztertzeko, `diff` normal bat ez dabil *Ez da asko erabiltzen, *Submodule*ak gehiago erabiltzen dira. Gehiago ikasteko, liburua irakurri.* ## Rerere: Reuse Recorded Resolution — I Konfliktoak nola konpondu ditugun erregistratzeko sistema bat da, gero Gitek konfliktoak automatikoki ebazteko kriterio berdina erabiltzen. Adibide baten bitartez: 1. Testing adarra mergeatzen konfliktoak daude 2. Konfliktoak konpontzen dira 3. Testak txarto doaz 4. Mergea desegin behar da 5. Testak konpondu behar dira 6. Mergea berriro egin => konflikto berdinak berriro konpondu behar dira `rerere`-ren bitartez lehenengo mergearen konfliktoen ebazpena gorde daiteke. Horrela, mergea berriro aplikatzerakoan ez da konfliktoa berriro ebatzi behar, automatikoki aurretik gordetako ebazpena erabiltzen delako. ## Rerere: Reuse Recorded Resolution — II - `git config --global rerere.enabled true` - `git rerere status` *rerere*ren egoera ikusteko - `git rerere diff` *rerere*k aplikatuko lituzken aldaketak ikusteko - `git add` + `git commit` egiterakoan ebazpena gordetzen da: `"Recorded resolution"` - `git reset --hard HEAD^` eta mergea berriro egiterakoan konfliktoak automatikoki konpontzen dira:\ `"Resolved with previous resolution"` - `git checkout --conflict=merge ` eginda konfliktoa berreskuratu daiteke, `rerere`a aktibatu gabe - `git rerere` egiten berriro mergeatu daiteke automatikoki ## Gitekin debuggeatzen - `git blame` fitxategi baten aldaketak zein commitean sartu diren ikusi daiteke. `^`-ekin hasten diren lerroak hasieratik aldatu gabeko lerroak dira. `-C` aukerarekin mugitutako zatiak aurkitu daitezke[^move] [^move]: Gitek ez ditu mugimenduak gordetzen baina fitxategien zatiak aztertu eta mugimenduak kalkulatu ditzake - `git bisect` [bisekzio metodoa][bisect] erabiliz errorea sartu zuen commita aurkitzeko tresna oso interesgarria. Metodoa: - Bi commit hartu: bat erroreduna eta bestea errore gabea - Erdiko puntuan commit bat hartu: errorea badauka, errorea sartu zuen commita commit honen eta errore gabeko commitaren artean dago. Errorea ez badago, commit honen artean eta commit erroredunaren artean dago. - Beste puntu bat hartu eta jarraitu tartea txikitzen commita aurkitzen den arte. [bisect]: https://en.wikipedia.org/wiki/Bisection_method ## Gitekin debuggeatzen: `git bisect` Bisekzio metodoa aplikatzeko workflowa 1. `git bisect start` bisekzio metodoa hasi 2. `git bisect bad` oraingo commita txarra da 3. `git bisect good ` commit hau ona da 4. `git bisect good/bad` oraingo commita ona edo txarra da. Gitek hurrengo commita aukeratzen du:\ `"Bisecting, N revisions left to test"` 5. `git bisect reset` hasierara bueltatu Automatizatu daiteke: `git bisect run ` - Komandoak `0` bueltatu behar du testa ondo badoa eta beste edozer errorea badago ## Submoduluak Gure proiektuaren barruan azpiproiektuak kudeatzeko modu bat da. - `git submodule add ` azpiproiekturako karpeta berri bat gehitzen du eta `.gitmodules` fitxategia hasieratzen du - `.gitmodules` fitxategian azpiproiektuen informazioa gordetzen da: URLa, izena eta path-a. - `git submodule add ...` eta gero `git diff --cached` egitean ikusten da submoduluen path-a ez dela fitxategi bezala kudeatzen:\ `"Subproject commit ---"` - `git diff --submodule --cached`-ekin hobeto ikusten da - `git push` egiterakoan submodule-aren direktorioa erreferentzia moduan pusheatzen da. ## Submoduluak klonatzeko - `git clone`-ek **ez** ditu proiektuen submoduluak deskargatzen - `git submodule init` submoduluen kontrola hasieratzeko - `git submodule update` submoduluen fitxategiak berrizteko - `git clone --recurse-submodule ` zuzenean submodule guztiak deskargatzeko `clone` egiterakoan. Bestela `git submodule update --init` edo `git submodule update --init --recursive` egin daiteke klonatu eta gero ## Submoduluekin lanean - `git diff --submodule` ez egiteko `git config --global diff.submodule log` egin daiteke eta zuzenean submoduluaren diffa ikusi daiteke `git diff` egitean - `git submodule update --remote ` submoduluen edukiak berrizteko edo bestela submoduluaren barruan `fetch`+`merge` egin daiteke. - `git config -f .gitmodules submodule..branch ` adarrez aldatzeko, edo fitxategia zuzenean aldatu - `git config status.submodulesummary 1` egitean `git status`-ek submoduluen egoera bistaratuko du - `git log --submodule` submoduluen aldaketak ikusteko ## Submoduluak dituen proiektua berrizteko - `git pull` ez ditu submoduluak berritzen, informazioa baino ez du deskargatzen. - `git submodule update` aplikatu behar da, `--init`-ekin submodule berriak gehitu badira, hauek gehitzeko eta `--recursive`, submoduluek submoduluak badituzte. - `git pull --recurse-submodules`-k efektu berdina dauka. Konfiguratu daiteke automatikoki egiteko. - `git submodule sync` egin behar da URLak aldatu badira. ## Submoduluetan lanean Normalean ez da submoduluetan idazten, baina egin daiteke. - `git submodule update`-k azpirepositorioa *detached head* egoeran uzten du. Bertak commitak gehitzean galduko lirateke (ez dago adarrik commitetara ailegatzeko). Hau konpontzeko: 1. Adarrak konfiguratu behar dira: `cd && git checkout ` 2. `git submodule update --remote --merge/--rebase` egin behar da *detached head* egoeran ez gelditzeko. Submoduluan aldaketak badaude, konfliktoa egon daiteke. ## Submoduluen lana publikatzen - `git push` egin daiteke submodulu bakoitzean, Git repositorio normalak baitira - `git push --recurse-submodules=check/on-demand` bi estrategia dago. `check` aldaketak pusheatu behar diren begiratzen du, eta `on-demand`-ek pusha egiten du submodulu bakoitzean aldaketak badaude - `git config push.recurseSubmodules check/on-demand` estrategia defektuz konfiguratzeko ## Submoduluen trukoak - `git submodule foreach ` egin daiteke submodule bakoitzean komando bat executatzeko. Adibidez: `git submodule foreach 'git stash'` ## Bundle Bundle-ak commitak edo repositorio osoak fitxategi baten bidez partekatzeko sistema dira. - `git bundle create []` aukeratutako commitak (edo guztiak) `` fitxategian gorde, bundle formatoan. Normalean, `HEAD` sartzea komeni da, gero bundle-a kargatzean repositorioa non zegoen jakiteko. - `git clone ` bundle fitxategitik clone bat egiteko. Bundle-a repositorio osoa izan behar du. - `git bundle verify ` bundle partzial bat repositorio batean aplikatu daitekeen konprobatzeko - `git bundle list-heads ` bundle fitxategiaren adarrak ikusteko - `git fetch/pull ` bundle fitxategitik repositorioa berriztatzeko ## Replace Giten datu basea orokorrean ezin da aldatu, baina `git replace`-en bitartez *replace* objektuak eratu daitezke, datu basea aldatu denaren itxura emango dutenak. - `git replace ` aplikatzetik aurrera, `` erabiltzean `` erabili balitz bezala funtzionatuko du *Erabileren adibide bat ikusteko liburuan ikusi, nahiko arraroa da hau ikustea* ## Kredentzial sistema HTTP kredentziala erabiltzean, zerbitzariarekin egiten diren interakzio guztietan erabiltzailea eta pasahitza eskatzen du Gitek defektuz. Portaera konfiguratu daiteke: - `git config --global credential.helper ` 1. Beti eskatu (defektuz egiten dena) 2. `cache` kredentzialak memorian mantendu 15 minutu 3. `store` kredentzialak gorde Aukera gehiago dago (`--timeout`, neurrira egindako kredentzial sistemak, etab.). *Interesa balego, liburuan begiratu* # Git konfigurazio aurreratua ## Git konfigurazioa Konfigurazioaren dokumentazioa ikusteko `git help git-config`. Atal interesgarrienak: - Editorea eta paginadorea: `core.editor` eta `core.pager` - Commit mezuen txantiloia: `commit.template` - Merge eta diff tresnak: `merge.tool` eta `diff.tool` - Espazio hutsen kontrola: `core.autocrlf` eta `core.whitespace` ## Git attribute-ak — I Proiektuen barruan, fitxategi mailan, Giten portaera aldatzeko erabiltzen dira atributuak (*attribute*). - `.gitattributes` fitxategian edo `.git/info/attributes` fitxategian, repositorioarekin partekatu nahi ezbadira. Atributu fitxategia `.gitignore` fitxategiaren antzekoa da, baina lerro bakoitzean fitxategien izenen txantiloiaz aparte haien atributuak definitu behar dira. Askotan konfigurazio gehigarria behar da, *driver* programak definitzeko. Ikusi `git help attributes`. ## Git attribute-ak — II Atributu mota asko dago, interesgarrienetarikoak: - `binary` fitxategiak binario moduan identifikatzeko - `diff=` diff-a exekutatu baino lehen fitxategiak iragazki batetik pasatzeko. Konfigurazio gehigarria behar da: - `git config diff..textconv ` - Adibidez: `*.png diff=exif` - `git config diff.exif.textconv exiftool`\ png fitxategien estatistikak konparatzen dira, eta ez edukiak - `ident` identifikazioa gehitzeko. `$Id$` testu literala SHA-1era bihurtzen da espantsio baten bitartez ## Git attribute-ak — III - `filter` fitxategiak iragazki batekin prozesatzeko - `smudge` *index*etik *working directory*rako bidean - `clean` *working directory*tik *index*erako bidean - Adibidez: `*.c filter=indent` - `git config filter.indent.clean indent`\ `git config filter.indent.smudge cat`\ fitxategiak automatikoki indentatzeko `git add` egiterakoan - `export-ignore` artxibo (`git archive`) bat egiterakoan fitxategi batzuk baztertzeko. - `export-subst` artxibo bat egiterakoan `git log`-ek aplikatzen dituen espantsioak aplikatzeko - `merge` merge estrategiak aukeratzeko ## Git hooks Hookak gertaera garrantzitsuak gertatzen direnean exekutatzen diren programak dira. Askotan erabiltzen dira. - `.git/hooks` direktorioan idazten dira, izen konkretu batekin eta exekuzio baimenekin Defektuz, `git init` egiterakoan hook batzuk idazten dira, baina desaktibatuta daude (haien izena `.sample`-n bukatzen delako). Erreferentzia moduan hartu daitezke haien funtzionamendua ulertzeko. ## Bezero aldeko hookak Bakoitzarentzako bakarrik. **Ez dira kopiatzen repositorioarekin batera**[^hookak-partekatu]. Interesgarriak dira garatzaileei alertak botatzeko: testak ez direla pasatu, formaturen bat txarto dagoela adierazteko, etab. Interesgarri batzuk: - `pre-commit` commit mezua idatzi baino lehen exekutatzen da, zeozer ahaztu den konprobatzeko erabili ohi da - `prepare-commit-msg` defektuzko commit mezua aldatzeko erabiltzaileak ikusi baino lehen. Informazio gehigarriak sartu ahal zaio horrela, edo txantiloia aldatu... - `commit-msg` commit mezua aztertzen duen hooka. Txarto badago commita atzera bota dezake. Normalean commitaren mezua txantiloi konkretu bat jarraitzeko erabiltzen da. [^hookak-partekatu]: Tresna batzuek mekanismoak dauzkate hook hauek partekatzeko, baina hau ez da Giten defektuzko funtzionamendua. Kontuz workflow guztia honetan oinarritzearekin. ## Zerbitzari aldeko hookak Politikak inposatzeko aproposak dira, txarto badoaz `push` egiteko eskaerak atzera bota ditzaketelako. Interesgarri batzuk: - `pre-receive` push bat jasotzean erreferentzia guztiak jasotzen dituen programa, errorea ematen badu ez da ezer berriztuko. - `update` aurrekoaren antzekoa da, baina behin exekutatzen da adar bakoitzeko - `post-receive` dena ondo jaso eta gero exekutatzen da, notifikazio moduan erabili daiteke # Git barrutik ## Git barrutik: Sarrera Git maila baxuko tresna bat da, eta maila horretan lan egiteko komandoak ditu, *plumbing* deiturikoak. Orain arte erabilitakoak *porcelain* deitzen dira, maila altuan lan egiten dutelako. Git *content-addressing* aplikatzen duen datu base bat da, hau da, *Key-Value* moduan gordetzen dira elementuak, non Key-a Valuaren edukiaren araberakoa den. Orain arte ikusi da: elementuak haien edukiaren SHA-1 hasharen bitartez identifikatzen dira. Eduki guzti hau `.git` karpetan gordetzen da. Baina elementu mota ezberdinak daude. ## Giten gordetzen diren elementu motak Bi elementu mota nagusiak daude Git datu basean: - Objektuak: edukiak dauzkaten elementuak dira: commitak, blobak, zuhaitzak, etab - Erreferentziak: beste elementuei apuntatzen duten elementuak dira: adarrak, lightweight tagak, etab ## Objektuak `.git/objects`-en gordetzen dira, haien hasharen arabera. Lehengo bi karaktereak direktorio moduan eta hurrengoak fitxategi moduan.\ Adibidez: `.git/objects/cf/6cbb8a400c7cad0f7f93610366c3672f598cdd` - `git hash-object -w`-rekin idatzi daitezke (bueltan hash-a ematen du) - `git cat-file`-ekin irakurri daitezke. `-p` egin emaitza **p**olita ikusteko. ## Objektu motak `git cat-file -t`-rekin mota ikusi daiteke. - `blob`-ak fitxategien edukiak eta bestelako datuak gordetzeko - `tree` fitxategien izenak eta direktorioak gordetzeko\ Sarrera bat edo gehiago dute beste `tree` edo `blob`etara haien izena eta hasha identifikatuz. - `commit` commit datuak gordetzeko: `tree` bat snapshotarekin, mezua, autorea, etab. ## Objektu motak: tree ![](img/data-model-1.png) ## Objektu motak: commit ![](img/data-model-3.png) ## Erreferentziak Haien barruan identifikadore bat baino ez dago. - `git update-ref` edukia aldatzeko. Eskuz ere egin daiteke. Erreferentzia mota ezberdinak daude: - Tag-ak: `.git/refs/tag` karpetan. Edozein objetu tageatu daiteke (normalean commitak). Bi mota dago: - Lightweight tag: erreferentzia bat baino ez dira - Annotated tag: tag *objektu* bat eratzen dute eta erreferentzia bat idazten dute bertara - Adarrak: `.git/refs/heads` karpetan. Commit-id batera erreferentzia egiten dute. - Remote-ak: `.git/refs/remotes` karpetan. Adarren antzerakoak baina ezin dira aldatu. - HEAD-a: `.git/HEAD`-en dago. Adar batera apuntatzen du, baina batzutan commit batera apuntatu dezake (*Detached HEAD*). ## Erreferentziak: adarrak ![](img/data-model-4.png) ## Ariketa: Notak - `git notes` komandoarekin kudeatzen dira. *Aztertu zer diren eta nola funtzionatzen duten. Ikusi `git help git-notes`* ## Packfile-ak Datuen egitura efizienteagoa egiteko Gitek fitxategiak konprimatzen ditu, baina hori ez da normalean nahikoa izaten. Git snapshotetan oinarritzen da, baina ez ditu fitxategi osoak behin eta berriro idazten commit bakoitzean, aldaketak baino ez ditu gordetzen. Gainera fitxategiak taldekatu eta *packfile* deituriko fitxategietan gordetzen ditu prozesua arintzeko. - `.git/objects/pack`-en ikusi daitezke - `git gc` (*garbage collector*) fitxategiak taldekatu eta konprimatzeko (berak bakarrik exekutatzen du beharrezkoa ikusten duenean) - `git verify-pack` *packfile* baten edukia aztertzeko eta egiaztatzeko ## Mantenamendua eta datuak berreskuratzea - `git gc --auto`-k packfileak berrorganizatzen ditu. - `git reflog`-en HEAD-ean egindako aldaketak gordetzen dira, beraz commiten bat galdu bada bertara salto egin daiteke, `reflog`a begiratu eta commita berreskuratu daiteke. - `git fsck --full` galdutako elementuak erakusten ditu, bertatik ere datuak berreskuratu daitezke *ARIKETA: Fitxategi handiak nahi gabe gehitzea arazo bat da, `git rm`-k ez dituelako ezabatzen. Nola egiten da? Zergatik?* ## Refspec-a Lokalaren eta remotearen arteko adarren mapeoa kudeatzeko. `.git/config`-en edukia `git remote add origin https://github.com/schacon/simplegit-progit` egin eta gero: ``` [remote "origin"] url = https://github.com/schacon/simplegit-progit fetch = +refs/heads/*:refs/remotes/origin/* ``` `fetch` atalak **refspec** baten bitartez adarrak nola kudeatu behar diren adierazten du. Sarrera asko egon daitezke arau ezberdinekin. Formatua: - Aukerako `+` bat *fast forward* ez direnean ere berrizteko - `:` nola mapeatu behar diren adierazteko. `` eta `` patroiak dira (`*`) ## Refspec-a eskuz erabiltzen `git fetch/push` egitean refspec patroia (1 edo +) gehitu ahal da *"hau hona"* eta *"beste hau hona"* esateko. Refspec-a ikusita, Gitek adarren adierazpenaren espantsioa egiten du: - `git log origin/master`\ => `git log remotes/origin/master`\ => `git log refs/remotes/origin/master` Adarrak ezabatzeko lehen refspec-a erabili behar zen: - `git push :` iturri atala utzik uztean, remotean adarra ezabatzen zen - Orain `git push --delete ` egin daiteke