Preguntes freqüents de Git
- Crear un repositori des d’una carpeta existent
git pullvsgit fetch+git mergegit pull: merge o rebase?- Vincular una branca local amb la remota (upstream)
- El push m’ha estat rebutjat (“fetch first”)
- Veure els canvis abans del commit
- Treure un arxiu afegit per error
- El .gitignore no ignora un arxiu
- Descartar tots els canvis locals
- Desfer o corregir l’últim commit
- Tornar a l’estat del remot
- Recuperar commits perduts (reflog)
- M’ha sortit un editor que no sé com tancar
- Referències
Respostes breus als dubtes més habituals quan treballes amb git. Els exemples fan referència als escenaris de Workflow Git.
Crear un repositori des d’una carpeta existent
Si ja tens una carpeta amb codi i vols pujar-la a un repositori remot buit (creat prèviament a github o gitlab):
cd la-meva-carpeta
git init -b main
git remote add origin https://gitlab.com/usuari/repositori.git
# afegir .gitignore abans del primer commit
git add .
git commit -m "commit inicial"
git push -u origin main
Alternativament, pots clonar primer el repositori buit i copiar-hi el contingut:
git clone https://gitlab.com/usuari/repositori.git
cd repositori
git switch -c main
# copiar arxius i afegir .gitignore
git add .
git commit -m "commit inicial"
git push -u origin main
git pull vs git fetch + git merge
git pull és equivalent a fer git fetch seguit de git merge. La diferència pràctica és que amb fetch + merge pots inspeccionar els canvis remots abans d’integrar-los (amb git log o git diff), mentre que pull ho fa tot de cop.
# Amb fetch + merge (més control)
git fetch
git log --oneline main..origin/main # veure què ha canviat
git merge
# Amb pull (més ràpid)
git pull origin main
git pull: merge o rebase?
Quan la teva branca local i la remota han divergit, git pull ha de combinar les dues històries, i pot fer-ho de dues maneres:
- Merge (comportament per defecte): crea un commit de merge que uneix les dues línies. Conserva l’historial exacte, però hi deixa “bombolles” de merge com les que hem vist a l’escenari amb conflicte.
- Rebase: reaplica els teus commits locals a sobre dels remots, deixant un historial lineal i més net. És la mateixa idea que rebase per mantenir la branca al dia, aplicada al
pull.
Pensa en l’usuari 2 de l’escenari amb conflicte. Amb merge, obté el diamant que hem vist en aquell escenari. Amb rebase, el seu commit es reaplica a sobre de segona 1:
gitGraph
commit id: "primer commit"
commit id: "afegim pregunta"
commit id: "segona 1"
commit id: "segona 2'"
L’apòstrof de segona 2' indica que és un commit nou: el mateix canvi, però amb un altre hash. El conflicte s’hauria de resoldre igualment, durant el rebase.
Des de git 2.27, si no has triat estratègia, git pull mostra un avís demanant-te que en configuris una. Les opcions habituals:
git config --global pull.rebase true # pull sempre amb rebase (historial lineal)
git config --global pull.ff only # només permet pull si és fast-forward; si no, atura's
Amb pull.ff only (una opció prudent), si hi ha divergència git no fa res automàticament i decideixes tu si fer git merge o git rebase. Puntualment també pots forçar l’estratègia amb git pull --rebase o git pull --no-rebase.
Compte: no facis rebase de commits que ja hagis pujat i compartit, perquè en reescriu l’historial (com --amend o el push forçat).
Vincular una branca local amb la remota (upstream)
Quan fas git push o git pull sense especificar el remot ni la branca, git necessita saber a quina branca remota correspon la teva branca local. Aquesta associació s’anomena upstream o tracking branch.
Hi ha diverses maneres d’establir-la:
# Opció 1: al fer push per primera vegada, amb -u (o --set-upstream)
git push -u origin main
# Opció 2: explícitament, sense fer push
git branch --set-upstream-to=origin/main main
Un cop establerta l’associació, pots fer servir les comandes curtes:
git push # equivalent a git push origin main
git pull # equivalent a git pull origin main
Quan clones un repositori, git configura automàticament el tracking de la branca principal. Per això git pull funciona directament en un repositori clonat sense haver de fer -u primer.
Per veure quines branques locals tenen upstream configurat:
git branch -vv
La sortida mostra, entre claudàtors, la branca remota associada:
feature d4e5f6a [origin/feature] un altre missatge
* main a1b2c3d [origin/main] últim missatge de commit
El push m’ha estat rebutjat (“fetch first”)
Si en fer git push reps un error com aquest:
! [rejected] main -> main (fetch first)
error: failed to push some refs to '...'
vol dir que algú ha pujat canvis al remot abans que tu, i git no et deixa sobreescriure’ls. No és cap error greu: només cal que integris primer els canvis remots i tornis a pujar.
git pull # baixa i integra els canvis remots (fetch + merge)
# si hi ha conflictes, resol-los, fes git add i git commit
git push # ara sí
És exactament la situació de l’escenari amb conflicte. Recorda la regla d’or: sincronitza (pull) sovint per reduir les vegades que et trobes en aquesta situació.
Veure els canvis abans del commit
git diff # canvis al working directory (no afegits al staging)
git diff --staged # canvis ja afegits al staging area
git diff HEAD # tots els canvis respecte l'últim commit
Treure un arxiu afegit per error
Si encara no has fet push, pots treure l’arxiu de l’últim commit sense perdre els canvis locals:
git reset HEAD~1 --soft # desfà el commit, manté els canvis al staging
git restore --staged arxiu-erroni.txt # treu l'arxiu del staging
git commit -m "el commit correcte"
Si ja has fet push però vols treure l’arxiu del repositori sense esborrar-lo del disc:
git rm --cached arxiu-erroni.txt
# afegir-lo al .gitignore si cal
git commit -m "remove arxiu-erroni del repositori"
git push
El .gitignore no ignora un arxiu
El .gitignore només afecta els arxius que git encara no segueix (untracked). Si un arxiu ja s’havia afegit al repositori abans d’incloure’l al .gitignore, git continuarà seguint-lo i els seus canvis. Per deixar de seguir-lo (sense esborrar-lo del disc), treu-lo de l’índex amb --cached:
git rm --cached arxiu.txt
git commit -m "deixa de seguir arxiu.txt"
A partir d’aquí, amb l’arxiu ja al .gitignore, git l’ignorarà. Per a una carpeta sencera, fes servir git rm -r --cached carpeta.
Descartar tots els canvis locals
git reset --hard HEAD
Compte: això esborra tots els canvis no comesos. Si vols guardar-los temporalment per recuperar-los més tard:
git stash # guarda els canvis temporalment
# ... fas altres coses ...
git stash pop # recupera els canvis guardats
Desfer o corregir l’últim commit
Mentre no hagis fet push, l’últim commit es pot desfer o modificar sense afectar ningú.
Desfer el commit completament, mantenint els canvis al working directory:
git reset --soft HEAD~1 # els canvis queden al staging, llestos per tornar a fer commit
git reset HEAD~1 # els canvis queden al working directory, fora del staging
Desfer el commit i descartar els canvis (irreversible):
git reset --hard HEAD~1
Corregir l’últim commit (afegir arxius oblidats o canviar el missatge):
# Canviar només el missatge
git commit --amend -m "nou missatge corregit"
# Afegir arxius que faltaven al commit
git add arxiu-oblidat.txt
git commit --amend --no-edit # manté el missatge original
--amend reescriu l’últim commit. Si ja has fet push, no és recomanable fer-ho perquè reescriu l’historial i pot causar problemes als companys.
Tornar a l’estat del remot
Si vols descartar tots els canvis locals (commits, staging i working directory) i sincronitzar-te exactament amb el repositori remot:
git fetch origin
git reset --hard origin/main
Això mou el HEAD local al mateix commit que origin/main, descartant qualsevol commit local que no s’hagi pujat i qualsevol canvi pendent. Si tens arxius nous que no estan al repositori (untracked), no s’esborren. Per eliminar-los també:
git clean -fd # esborra arxius i carpetes untracked
Compte: ambdues operacions són irreversibles. Si no estàs segur de voler perdre els canvis, fes primer una còpia o un git stash.
Recuperar commits perduts (reflog)
Si has perdut feina amb un reset o un rebase, sovint la pots recuperar. git guarda un registre de tots els llocs on ha estat el HEAD (cada commit, reset, merge, switch…) durant un temps. Es consulta amb git reflog:
git reflog
8f151d7 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
2be3461 HEAD@{1}: commit: feina que creia perduda
8f151d7 (HEAD -> main) HEAD@{2}: commit (initial): primer commit
Cada línia és un estat anterior del HEAD. Quan localitzis el commit que vols (per exemple 2be3461, el que crèiem perdut), el pots recuperar tornant la branca a aquell punt:
git reset --hard 2be3461 # torna la branca a aquell commit
O, si només vols mirar-lo sense moure la branca, fes git switch --detach 2be3461.
Compte: el reflog és local i no és etern (git el va netejant; per defecte, les entrades caduquen al cap d’uns 90 dies). És una xarxa de seguretat, no un substitut de fer commits i push sovint.
M’ha sortit un editor que no sé com tancar
Si fas un git commit sense -m, o git necessita que escriguis un missatge (per exemple, en completar un merge), obre un editor de text. Per defecte sol ser vim o nano, i és fàcil quedar-s’hi encallat:
- vim: prem
Esc, escriu:wqi premEnterper desar i sortir (o:q!per sortir sense desar). - nano: prem
Ctrl+OiEnterper desar, i desprésCtrl+Xper sortir.
Per evitar-ho, posa el missatge directament a la comanda amb -m:
git commit -m "el missatge"
O configura un editor que et resulti més còmode:
git config --global core.editor "nano" # o "vim"
git config --global core.editor "code --wait" # VS Code