el nom fa de control de versions, perquè no n’hi ha cap altre
i, abans que tot això: cada u té la seua còpia, i ja no es poden comparar
Un sol fitxer
git log --oneline-- informe.sql # totes les versions: data, autor i motiugit show HEAD~3:informe.sql # com estava fa tres canvisgit diff HEAD~3 -- informe.sql # què ha canviat des d'aleshores
Quatre fitxers passen a ser un, i no es perd cap versió.
Per ací passen les ferramentes: scripts, esquemes, configuració. Una tasca —un canvi fet a mà— no hi passa: usa una ferramenta, i queda al registre.
La integració contínua (CI) vol dir que, cada vegada que algú puja un canvi, s’executa a soles un codi que el comprova. Eixe codi és la pipeline, i viu versionada en el repositori com qualsevol altra cosa.
Per a començar un repositori de zero, git init dins de la carpeta.
Què tinc ara mateix?
$ git status
Changes to be committed: ← triat per al pròxim commit
modified: consulta_tablespaces.sql
Changes not staged for commit: ← tocat, però encara no triat
modified: validacio_cites.sql
És la comanda que més usareu, i anomena ella mateixa les dues zones.
Les tres zones
directori de treball ──git add──> zona de preparació ──git commit──> historial
(el que edites) (el que entrarà) (permanent)
git add no guarda res: tria què entrarà en el pròxim commit
per això es poden fer dos commits separats de dos canvis del mateix fitxer
git status diu sempre en quina zona està cada cosa
Un canvi d’una línia en la comprovació de tablespaces:
-LLINDAR_OCUPACIO=85+LLINDAR_OCUPACIO=90
El commit ho contesta
Què ha canviat
LLINDAR_OCUPACIO, de 85 a 90
Qui ho ha fet
l’autor del commit
Quan
la data i l’hora exactes
Per quin motiu
«Puja el llindar d’avís de tablespaces (INC-0912)»
Com tornar arrere
git revert a183fe2
El commit, per dins
$ git show -s --format=raw a183fe2
commit a183fe2c9b4d7e1a6f3c8b2d5e9a0f4c7b1d6e38
tree 9c1b7a4f2e5d8c0a1b3e6f7d2c4a8b9e0f1d3c5a
parent 4f2a1c8e7b3d9a0f6c5e2d4b8a1f7c3e9d0b6a2f
author Raúl Salinas <raulsa8@uv.es> 1788249600 +0200
committer Raúl Salinas <raulsa8@uv.es> 1788249600 +0200
Puja el llindar d'avís de tablespaces de 85 a 90 (INC-0912)
commit
el hash: no és un número de sèrie, ix del contingut
tree
la fotografia completa del projecte, no el canvi
parent
el commit anterior: ací es forma la cadena
author / committer
qui el va escriure i qui el va aplicar: difereixen si el commit s’ha reaplicat (rebase, cherry-pick)
el missatge
l’única part que no es pot deduir mirant el codi
Què ha canviat un company?
git diff # el que he tocat jogit diff main...validacio-cites # tot el que aporta una brancagit show a183fe2 # un canvi concret, amb el seu motiu
La vista d’una pull request és això mateix, en el navegador i amb un lloc per a comentar cada línia.
Funcionava fa una setmana
git log --oneline-- validacio_cites.sqlgit show a183fe2git blame create_user.sql
Git no sols permet tornar arrere: permet reconstruir com hem arribat ací.
Mirar, restaurar, revertir
Vull…
Mirar com estava
git show HEAD~3:informe.sql
Tornar el fitxer enrere
git restore --source=HEAD~3 informe.sql
Desfer un canvi ja integrat
git revert a183fe2
En una branca compartida, sempre git revert.
El repositori remot
git remote -v# a on apuntegit fetch # baixa el que hi ha, sense tocar-me resgit pull # fetch + integrar-ho en la meua brancagit push # pujar el que he consolidat
El teu clon i el servidor són dos repositoris complets. fetch només mira; pull a més integra.
I el git add? git commit -a se salta la preparació. Però amb quatre passes ja no és un cartell.
Les tres que es confonen
Què toca
Quan
git restore fitxer
el teu directori
m’he equivocat i encara no he fet commit
git reset --soft HEAD~1
l’historial local
el commit era prematur; els canvis es queden
git reset --hard HEAD~1
historial i fitxers
no vull res del que he fet ⚠️
git revert a183fe2
afig un commit nou
ja està integrat i ho vol desfer tothom
reset reescriu; revert afig. En allò que ja han baixat altres, sempre revert.
M’han interromput
git stash # guarda el que tinc a mitges i deixa el directori netgit switch main # atenc la urgènciagit stash pop # torne a on estava
Per a quan et criden a mitjan canvi i has de mirar una altra cosa ara.
Revertir un commit recupera el codi, no les dades.
Un UPDATE o un DDL ja executat no es desfà amb Git.
D’ací ix una decisió: els processos amb dades reals acaben amb una persona que decidix.
Reescriure l’historial
commit --amend, rebase -i i filter-repo no modifiquen commits: en creen d’altres i abandonen els vells.
Branca
Reescriure
main i les compartides
Mai. Per a desfer, git revert
La teua, encara sense fusionar
Endavant, i sovint convé
publicar-ho exigix git push --force: «descarta el que hi ha al servidor»
les branques principals es protegixen, i el servidor el rebutja
si has de forçar: --force-with-lease, que falla si algú havia pujat res
Què forma realment el projecte
git ls-files # l'inventari: el que algú va decidir que hi forma partgit ls-files '*.sql'# només el SQLgit grep DBMS_SCHEDULER # buscar només dins d'eixe inventari
ls mostra el que hi ha. git ls-files mostra el que s’ha decidit tindre.
El .gitignore és l’altra cara: deixa fora generats, logs i temporals.
El missatge diu per què
Canvia timeout a 60
Evita timeout durant la càrrega de matrícula (INC-1234)
el què ja el diu el diff
el per què no està en cap altre lloc
un commit, una cosa: barrejar-ho tot encarix llegir-lo, revisar-lo i desfer-lo
en present: «Afig…», que en valencià val alhora d’imperatiu. Mai infinitiu ni passat
Conventional Commits (feat:, fix:) calcula versions d’un producte que es publica: ací no compra res
Dos canvis en un mateix fitxer
Has afegit una funcionalitat i has arreglat el fallo que t’ho impedia. Són dos commits.
git add -p consulta_tablespaces.sql
Git va mostrant els canvis tros a tros i pregunta per cada u:
Stage this hunk [y,n,q,a,d,s,e,?]?
y sí · n no · s parteix el tros en trossos més menuts
Practiquem: exercici 1
Entendre el ciclemodificar → revisar → commit.
exportUVLOGIN=raulsa8 # ← el vostregit config --global core.editor nano
L’editor es posa amb git config: és de Git, no del terminal, i no cal repetir-ho.
L’enunciat dona per fet un terminal tipus Unix —Git Bash, WSL, MobaXterm—. Si no, hi ha l’alternativa escrita.
Enunciat: el document Exercicis del material del curs
Branques i revisió
L’historial és una cadena
graph RL
classDef commit fill:#002e52,stroke:#002e52,color:#fff;
classDef punter fill:#e4ebe9,stroke:#002e52,color:#002e52,stroke-width:2px;
c3(( )) --> c2(( )) --> c1(( ))
main["main"] -.-> c3
class c3 commit;
class c2 commit;
class c1 commit;
class main punter;
Cada cercle és un commit, i apunta al que tenia darrere: és la línia parent.
main no és una carpeta ni una còpia: és un punter a l’últim commit.
Una branca és un punter nou
git switch -c validacio-cites
graph RL
classDef commit fill:#002e52,stroke:#002e52,color:#fff;
classDef punter fill:#e4ebe9,stroke:#002e52,color:#002e52,stroke-width:2px;
c3(( )) --> c2(( )) --> c1(( ))
main["main"] -.-> c3
vc["validacio-cites"] -.-> c3
inf["informes"] -.-> c2
class c3 commit;
class c2 commit;
class c1 commit;
class main punter;
class vc punter;
class inf punter;
Tres branques, tres punters. validacio-cites i main assenyalen el mateix commit; informes es va quedar arrere.
No s’ha copiat cap fitxer: només hi ha un nom nou.
Els teus commits van a la teua branca
graph RL
classDef commit fill:#002e52,stroke:#002e52,color:#fff;
classDef punter fill:#e4ebe9,stroke:#002e52,color:#002e52,stroke-width:2px;
c4(( )) --> c3(( )) --> c2(( )) --> c1(( ))
main["main"] -.-> c3
vc["validacio-cites"] -.-> c4
inf["informes"] -.-> c2
class c4 commit;
class c3 commit;
class c2 commit;
class c1 commit;
class main punter;
class vc punter;
class inf punter;
El mateix dibuix, amb un commit més. S’ha mogut un sol punter: el teu.
main i informes estan exactament on estaven. Ningú més veu encara els teus canvis.
M és un commit amb dos pares. Per això no es perd res de cap de les dues bandes.
Moure’s entre branques
git branch # quines tincgit branch -a# també les del servidorgit switch validacio-cites # canviar-megit switch -c nova # crear-la i canviar-megit switch -# tornar a l'anterior
git checkout fa això mateix i moltes coses més: switch i restore el partiren en dos, precisament perquè confonia.
Dues maneres d’integrar
El mateix punt de partida: la teua branca i main han divergit.
%%{init: {'theme':'base','themeVariables':{'git0':'#002e52','git1':'#0082c5','gitBranchLabel0':'#ffffff','gitBranchLabel1':'#ffffff','commitLabelColor':'#002e52','commitLabelBackground':'#e4ebe9','commitLabelFontSize':'15px'},'gitGraph':{'showCommitLabel':false,'showBranches':true}}}%%
gitGraph
commit id: "o"
commit id: "o"
branch branca
checkout branca
commit id: "A"
commit id: "B"
checkout main
commit id: "X"
commit id: "Y"
Per a incorporar X i Y al teu treball hi ha dos camins, i no fan el mateix.
Opció A: git merge main
%%{init: {'theme':'base','themeVariables':{'git0':'#002e52','git1':'#0082c5','gitBranchLabel0':'#ffffff','gitBranchLabel1':'#ffffff','commitLabelColor':'#002e52','commitLabelBackground':'#e4ebe9','commitLabelFontSize':'15px'},'gitGraph':{'showCommitLabel':false,'showBranches':true}}}%%
gitGraph
commit id: "o"
commit id: "o"
branch branca
checkout branca
commit id: "A"
commit id: "B"
checkout main
commit id: "X"
commit id: "Y"
checkout branca
merge main
no reescriu res: A i B es queden com van passar
M té dos pares, i per això l’historial mostra que hi va haver dues línies
els conflictes es resolen una vegada, en el moment de fusionar
Opció B: git rebase main
Després de git rebase main, la teua branca ix d’on acaba main:
WHENEVER SQLERROR EXIT SQL.SQLCODEWHENEVER OSERROR EXIT FAILURE-- Exemple docent. No executar en producció sense adaptar-lo.-- Ús esperat:-- @create_user.sql APP_ORDENAMAT PROFILE_APPLICATION USERSDEFINE username ='&1'DEFINE profile_name ='&2'DEFINE default_ts ='&3'CREATEUSER&&usernameIDENTIFIEDEXTERNALLYDEFAULTTABLESPACE&&default_tsTEMPORARYTABLESPACE TEMPPROFILE&&profile_name;GRANTCREATESESSIONTO&&username;
Important
Les contrasenyes, wallets, claus i tokens no van al repositori.
Sí que pot anar:
àlies
serveis
entorns
paràmetres no secrets
plantilles
validacions
Entorns reproduïbles
“En la meua màquina funciona”
versió d’Oracle i del client
joc de caràcters i variables NLS_
zona horària
paquets del sistema
rutes lligades a un ordinador: X:\…, \\servidor\…
passos manuals que ningú va escriure
L’entorn també és codi
# Base de dades de proves efímera: la mateixa que arranca la CI.# docker compose up -d # crea l'esquema i carrega el joc de dades# docker compose down -v # no deixa rastre## La contrasenya és local i d'usar i tirar: esta base no conté dades reals.services:oracle:image: gvenzl/oracle-free:23-slim-faststartenvironment:ORACLE_PASSWORD: local_throwawayAPP_USER: matriculaAPP_USER_PASSWORD: local_throwaway # Fixat perquè el resultat no depenga de la màquina que ho execute.TZ: Europe/MadridORACLE_CHARACTERSET: AL32UTF8NLS_DATE_FORMAT:"YYYY-MM-DD HH24:MI:SS"ports:-"1521:1521"volumes:- ./01_schema_matricula.sql:/container-entrypoint-initdb.d/01_schema.sql:ro- ./02_seed_matricula.sql:/container-entrypoint-initdb.d/02_seed.sql:rohealthcheck:test:["CMD","healthcheck.sh"]interval: 10sretries:30
I la màquina que ja tens
El compose val per al que naix nou. Per a un servidor que fa anys que corre:
b3293df committing changes in /etc after apt run
e1edf17 Estat inicial
5c38ef2 Initial commit
No ix d’eixa màquina: dins de /etc hi ha shadow i claus privades.
Quan està acabada una tasca?
Quan passa la integració contínua.
no quan funciona en el meu portàtil
no quan “a mi em va bé”
qualsevol company pot reproduir el resultat
la mateixa imatge en local i en la CI
Dades de prova
esquema buit amb el DDL de producció
càrrega sintètica menuda
casos límit inclosos expressament
destruir-ho tot en acabar
Mai una còpia de dades reals d’estudiants. I tampoc una còpia completa: per a provar una regla no fan falta totes les dades.
Càrrega sintètica
WHENEVER SQLERROR EXIT SQL.SQLCODE-- Els scripts d'inici del contenidor s'executen com a SYSDBA contra el CDB,-- i l'usuari de l'aplicació viu dins del PDB: cal dir a quin contenidor va.ALTERSESSIONSETCONTAINER= FREEPDB1;-- Càrrega de prova sintètica: noms inventats, volum menut i casos límit.-- Mai s'han de copiar dades reals d'estudiants a un entorn de proves.-- La base de dades és efímera, per això el joc de dades no esborra res.INSERTINTO matricula.estudiant (id, nom, centre) VALUES (1, 'Aitana Ferrer Bonet', 'FIS');INSERTINTO matricula.estudiant (id, nom, centre) VALUES (2, 'Joan Marí Escrivà', 'FIS');INSERTINTO matricula.estudiant (id, nom, centre) VALUES (3, 'Neus Bellver Tormo', 'ETSE');-- Cas normal: cada estudiant, una franja distinta.-- TO_DATE explícit i no la cadena a seques: així la càrrega no depén del-- NLS_DATE_FORMAT de qui l'execute i dona el mateix resultat.INSERTINTO matricula.centre_franja VALUES (1, 'FIS', 'G1100', TO_DATE('2026-09-01 09:00', 'YYYY-MM-DD HH24:MI'));INSERTINTO matricula.centre_franja VALUES (2, 'FIS', 'G1100', TO_DATE('2026-09-01 09:30', 'YYYY-MM-DD HH24:MI'));INSERTINTO matricula.centre_franja VALUES (3, 'ETSE', 'G1400', TO_DATE('2026-09-01 09:00', 'YYYY-MM-DD HH24:MI'));COMMIT;
Pensar les condicions de prova
De què depén realment este procés?
quines taules necessita
quines dades mínimes fan que el cas tinga sentit
quins casos límit hem de cobrir
què hauria de passar si les dades són incorrectes
El registre l’escriu la màquina
A mà, el registre es fa després i el fa una persona cansada:
es copia la comanda al tiquet
s’apega un tros d’eixida
s’anota l’hora aproximada
Automatitzat, el registre és un subproducte: comanda exacta, commit, hora real, eixida completa, resultat.
commit=a183fe2
A les 08:00 es detecta que la càrrega de la nit ha deixat dades incorrectes. El log de les 03:00 diu commit=a183fe2.
git show a183fe2git log --oneline a183fe2..HEAD
Quin codi va córrer, qui el va escriure, per què, i què s’ha tocat després.
Amb la data com a paràmetre, el canvi d’any s’assaja en juliol.
Assajar la matrícula
Amb base efímera i dades sintètiques, el procés es pot executar tantes vegades com faça falta:
cada canvi es valida contra el mateix joc de proves
una regressió es detecta el dia que s’introdueix
no cal reservar cap entorn compartit
el dia de la matrícula és una execució més
☕
Pausa
Tornem a les 12:55
Integració contínua
Què pot comprovar una pipeline?
secrets accidentals
DELETE sense WHERE
GRANT DBA
scripts sense WHENEVER SQLERROR
errors de Bash
estructura del repositori
documentació mínima
validacions sobre Oracle de proves
Una validació de negoci
-- Ha de retornar zero files si no hi ha cites duplicades.SELECT centre, titulacion, franja,COUNT(*) AS repetitionsFROM matricula.centre_franjaGROUPBY centre, titulacion, franjaHAVINGCOUNT(*) >1;
La pipeline més menuda
# Comprova que tot el Python del repositori compila: no és# cap prova, però atrapa l'error d'edició que arriba a# producció a les 3 de la matinada.# Es pot posar el primer dia, sense proves i sense entorn.image: python:3-slimpipelines:default:-step:name: El Python compilamax-time:5script: # compileall torna un codi distint de zero si un # fitxer té un error de sintaxi, i això ja posa la # pipeline en roig. No fa falta res més.- python -m compileall -q .
Cap prova, cap entorn. Un error de sintaxi ja no arriba a producció.
Exemple de pipeline
commit
↓
detecció de secrets
↓
validació d'estructura
↓
anàlisi estàtica
↓
execució en proves
↓
informe
Abans de pujar
pip install pre-commitpre-commit install # una vegada per repositori
A partir d’ahí, cada git commit passa les regles en la teua màquina.
segons, en compte d’esperar la pipeline
no és un control: --no-verify se’l salta
la CI és la que talla; el hook és la comoditat
Tres nivells
Què vol dir
Qui ho decideix
Integració contínua
cada canvi es valida
ningú, passa sempre
Entrega contínua
queda llest per a desplegar
una persona
Desplegament continu
es publica sol
ningú, passa sempre
Per a publicar sol fan falta
una validació de fiar
un desplegament reproduïble
tornar arrere barat
Si la pipeline es posa verda amb coses trencades, publicar sol només serveix per a difondre errors més ràpid.
Si tornar arrere no és fàcil, ràpid i segur, la gent té por d’anar cap avant.
On sí i on no
Documentació, informes, entorns de proves: desplegament continu.
Processos amb dades reals: entrega contínua i botó humà.
L’objectiu no és desplegar sol, és que quan es decidisca desplegar no faça falta cap passa manual.
Estes diapositives
push a main
↓
Bitbucket Pipelines
↓
[1] comprovacions estàtiques ← segons; si falla, s'acaba ací
↓
[2] construir en un contenidor Debian net ← si falla, s'acaba ací
↓
[3] empaquetar el web en un zip ← artefacte descarregable
↓
web publicat
Ningú “puja les transparències”.
Un node Oracle des de zero
# Assaig de construcció d'un node Oracle des de zero. No prova que la# instal·lació siga correcta: prova que el procediment escrit està complet.pipelines:custom: # s'engega a mà, no per commitnode-oracle-des-de-zero:-step:runs-on:[self.hosted, linux] # cal un runner que puga crear VMscript:- IP=$(provisio/vm.sh create "assaig-$BITBUCKET_BUILD_NUMBER")- ssh oracle@$IP 'sudo provisio/00-preparar-so.sh' # La mateixa passa, una segona vegada: si no és idempotent, el # procediment no es pot repetir i encara no val com a document.- ssh oracle@$IP 'sudo provisio/00-preparar-so.sh'- ssh oracle@$IP 'provisio/10-comprovacions-previes.sh' # cluvfy- ssh oracle@$IP 'provisio/20-instalar.sh' # runInstaller -silent- ssh oracle@$IP 'provisio/30-fum.sh' # la base respon?after-script: # s'executa també si ha fallat- provisio/vm.sh destroy "assaig-$BITBUCKET_BUILD_NUMBER"
De les diapositives que es publiquen soles a un node sencer: el mecanisme és el mateix.
Practiquem: exercici 5
Provocar una fallada de la pipeline i corregir-la.
Idees: una cadena que parega una contrasenya, un DELETE sense WHERE, un marcador de conflicte oblidat.
Enunciat: el document Exercicis del material del curs
Exercici complet
Flux de pràctica
Crear branca
Modificar SQL
Actualitzar documentació
Fer commit
Publicar branca
Obrir pull request
Veure fallada de CI
Corregir
Revisar
Resoldre un conflicte, si n’hi ha
Fusionar
Per on començar
Un repositori nou d’operació Oracle
No migrar un procés que ja funciona, sinó crear un repositori nou per al que es faça d’ara endavant:
Tot script operatiu nou naix versionat, revisable i amb comprovacions.
comprovacions de tablespaces, sessions, objectes invàlids