--- title: GIT — EITB 2022 subtitle: Erabilera aurreratua license: CC-BY-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 askok commit askotan aplikatu daitezke. Gitek commit selektore ezberdinak aplikatzea ahalbidetzen du commitak aukeratzeko. - `git show ` aukeratutako objektuak erakusteko ## 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 []` aukeratutako *stash* 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` *pikeaxe* 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 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 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 dirudi. 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