10 Probleme frecvente ale git-ului și cum să le rezolvi

Am scris inițial acest articol pentru Codementor în octombrie 2014. Ar trebui să aibă ceva pentru toată lumea, de la utilizatori destul de noi de git la dezvoltatori cu experiență.

1. Renunțați la modificările fișierelor locale

Uneori, cel mai bun mod de a înțelege o problemă este să vă scufundați și să vă jucați cu codul. Din păcate, modificările aduse în proces se dovedesc uneori mai puțin decât optime, caz în care revenirea fișierului la starea inițială poate fi cea mai rapidă și ușoară soluție:

git checkout - Gemfile # resetează calea specificată
git checkout - lib bin # funcționează de asemenea cu mai multe argumente

În cazul în care vă întrebați, liniuța dublă (-) este o modalitate comună pentru utilitățile liniei de comandă de a semnifica sfârșitul opțiunilor de comandă.

2. Anulează angajamentele locale

Din păcate, uneori este nevoie de ceva mai mult timp pentru a ne da seama că suntem pe calea greșită și, până la acel moment, una sau mai multe schimbări ar fi fost deja comise la nivel local. Acest lucru este atunci când resetarea git vine la îndemână:

git reset HEAD ~ 2 # anulează ultimele două angajamente, păstrează modificările
resetare git --hard HEAD ~ 2 # anulează ultimele două angajamente, elimină modificările

Fii atent cu opțiunea --hard! Vă resetează arborele de lucru, precum și indexul, astfel încât toate modificările dvs. se vor pierde în bine.

3. Eliminați un fișier din git fără să îl eliminați din sistemul dvs. de fișiere

Dacă nu sunteți atent în timpul unei adăugări de git, puteți ajunge să adăugați fișiere pe care nu ați dorit să le comite. Cu toate acestea, git rm îl va elimina atât din zona dvs. de înscenare, cât și din sistemul dvs. de fișiere, care poate nu este ceea ce doriți. În acest caz, asigurați-vă că eliminați doar versiunea etapizată și adăugați fișierul în .gitignore pentru a evita să faceți aceeași greșeală a doua oară:

git resetează numele fișierului # sau git rm - numele fișierului în cache
ecou nume de fișier >> .gitignore # adăugați-l la .gitignore pentru a evita re-adăugarea lui

4. Editați un mesaj de angajare

Tipurile se întâmplă, dar, din fericire, în cazul mesajelor de angajare, este foarte ușor să le repari:

git commit --amend # start $ EDITOR pentru a edita mesajul
git commit --amend -m "Mesaj nou" # setați noul mesaj direct

Dar nu toate acestea pot face pentru tine. Ați uitat să adăugați un fișier? Doar adăugați-o și modificați angajamentul anterior!

git adaugă uitat_file git commit --amend

Vă rugăm să rețineți că --amend va crea de fapt un nou angajament care îl înlocuiește pe cel precedent, deci nu îl folosiți pentru a modifica comisioane care au fost deja trimise într-un depozit central. O excepție de la această regulă poate fi făcută dacă sunteți absolut sigur că niciun alt dezvoltator nu a verificat deja versiunea anterioară și și-a bazat propria activitate pe ea, caz în care o apăsare forțată (git push - force) poate fi încă ok. Opțiunea - force este necesară aici, deoarece istoricul arborelui a fost modificat local, ceea ce înseamnă că apăsarea va fi respinsă de serverul de la distanță, deoarece nu este posibilă îmbinarea rapidă.

5. Curățați angajamentele locale înainte de a împinge

Deși --amend este foarte util, nu ajută dacă angajamentul pe care doriți să îl refaceți nu este ultimul. În acest caz, o rambursare interactivă vine la îndemână:

git rebase --interactiv
# dacă nu ați specificat nicio informație de urmărire pentru această sucursală
# va trebui să adăugați informații despre ramură în amonte și la distanță:
git rebase - ramură de origine interactivă

Acest lucru vă va deschide editorul configurat și vă va prezenta următorul meniu:

alegeți versiunea 8a20121 Upgrade Ruby la 2.1.3
alege 22dcc45 Adăugați niște biblioteci fanteziste
# Rebase fcb7d7c..22dcc45 pe fcb7d7c
#
# Comenzi: # p, pick = use commit
# r, reword = folosiți commit, dar editați mesajul de comitere
# e, edit = folosiți commit, dar opriți-vă pentru a modifica
# s, squash = folosiți commit, dar s-au topit în angajarea anterioară
# f, fixup = ca "squash", dar aruncați mesajul de jurnal al acestui angajament
# x, exec = comanda rulare (restul liniei) folosind shell
#
# Aceste linii pot fi re-comandate; acestea sunt executate de sus în jos.
#
# Dacă eliminați o linie aici ACEASTA COMISIE va fi pierdută.
#
# Cu toate acestea, dacă eliminați totul, rambursarea va fi anulată.
#
# Rețineți că comiterile goale sunt comentate

În partea de sus, veți vedea o listă de comitete locale, urmată de o explicație a comenzilor disponibile. Nu trebuie decât să alegeți angajamentele pe care doriți să le actualizați, schimbați alegerea pentru refacere (sau r pentru scurt), și veți fi dus la o nouă vizualizare, unde puteți edita mesajul.

Cu toate acestea, așa cum se poate observa din lista de mai sus, rambursările interactive oferă mult mai mult decât o simplă editare a mesajelor de angajare: puteți elimina complet angajamentele ștergându-le din listă, precum și editați, reordonați și ghemuindu-le. Squashing vă permite să îmbinați mai multe angajamente într-una, ceea ce îmi place să fac pe ramurile de funcții înainte de a le împinge pe telecomandă. Nu mai sunt angajamente „Adaugă fișier uitat” și „Fix typo” înregistrate pentru eternitate!

6. Revenirea angajamentelor împinse

În ciuda corecțiilor demonstrate în sfaturile anterioare, comisioanele defecte o fac ocazional în depozitul central. Totuși, acesta nu este un motiv de disperare, deoarece git oferă o modalitate ușoară de a reveni asupra unor angajamente simple sau multiple:

git revert c761f5c # readuce comiterea cu id-ul specificat
git revert HEAD ^ # revine al doilea pentru ultima angajare
git revert develop ~ 4..develope ~ 2 # readuce o serie întreagă de comiteri

În cazul în care nu doriți să creați comisioane de returnare suplimentare, dar aplicați doar modificările necesare arborelui dvs. de lucru, puteți utiliza opțiunea --no-commit / -n.

# anulează ultimul angajament, dar nu creează o refacere a angajamentului
git revert -n HEAD

Pagina manuală de la man 1 git-revert listează alte opțiuni și oferă câteva exemple suplimentare.

7. Evitați conflictele de fuziune repetate

După cum știe orice dezvoltator, rezolvarea conflictelor de îmbinare poate fi obositoare, însă rezolvarea exactă a aceluiași conflict în mod repetat (de exemplu, în ramuri de funcții pe termen lung) este în mod enervant. Dacă ați suferit de acest lucru în trecut, veți fi bucuroși să aflați despre funcția de rezoluție înregistrată nefolosită. Adăugați-o la configurația dvs. globală pentru a o activa pentru toate proiectele:

git config --global rerere.enabled true

În mod alternativ, îl puteți activa în fiecare proiect creând manual directorul .git / rr-cache.

Acest lucru este sigur că nu este o caracteristică pentru toată lumea, dar pentru persoanele care au nevoie de acestea, poate fi economisitor în timp real. Imaginează-ți că echipa ta lucrează la diverse ramuri de funcții în același timp. Acum doriți să le îmbinați toate într-o singură ramură testabilă înainte de lansare. Așa cum era de așteptat, există mai multe conflicte de contopire, pe care le rezolvi. Din păcate, se dovedește că una dintre sucursale nu se află încă acolo, așa că decideți să o desfundați din nou. Câteva zile (sau săptămâni) mai târziu, când sucursala este în sfârșit gata, o îmbinați din nou, dar datorită rezoluțiilor înregistrate, nu va trebui să rezolvați din nou aceleași conflicte de fuziune.

Pagina de man (man git-rerere) are mai multe informații despre cazurile și comenzile de utilizare ulterioară (starea git rerere, git rerere diff, etc).

8. Găsiți angajamentul care a rupt ceva după o fuziune

Urmărirea comisiei care a introdus o eroare după o fuziune mare poate dura destul de mult. Din fericire, git oferă o mare opțiune de căutare binară sub formă de git-bisect. Mai întâi trebuie să efectuați configurația inițială:

git bisect start # începe sesiunea de bisectare
git bisect bad # marchează revizuirea curentă ca fiind proastă
git bisect good revizie # marchează ultima revizuire bună cunoscută

După acest git va verifica automat o revizuire la jumătatea distanței dintre versiunile cunoscute „bune” și „proaste”. Acum puteți rula specificațiile dvs. din nou și marca corespunzător comiterea „bună” sau „rea” în consecință.

git bisect bun # sau git bisec rău

Acest proces continuă până când ajungeți la angajamentul care a introdus eroarea.

9. Evitați greșelile obișnuite cu cârligele git

Unele greșeli se întâmplă în mod repetat, dar ar fi ușor de evitat prin executarea anumitor verificări sau sarcini de curățare la un stadiu definit al fluxului de lucru git. Acesta este exact scenariul pentru care au fost concepute cârligele. Pentru a crea un nou cârlig, adăugați un fișier executabil la .git / hooks. Numele scenariului trebuie să corespundă unuia dintre cârligele disponibile, o listă completă a acestora fiind disponibilă în pagina manuală (manechinele de mână). Puteți defini, de asemenea, cârlige globale pe care să le utilizați în toate proiectele dvs., creând un director de șabloane pe care git îl va folosi la inițializarea unui nou depozit (consultați man git-init pentru informații suplimentare). Iată cum arată intrarea relevantă în ~ / .gitconfig și un exemplu de director de șabloane:

[Init]
    templatedir = ~ / .git_template
  
→ copac .git_template
  .git_template
  └── cârlige
      └── pre-angajare

La inițializarea unui nou depozit, fișierele din directorul șablon vor fi copiate în locația corespunzătoare din directorul .git al proiectului.

Ceea ce urmează este un exemplu ușor contrișat cârligul de comutare-msg, care va asigura că fiecare mesaj de referință face referire la un număr de bilet de genul „# 123”.

#! / usr / bin / env ruby
message = File.read (ARGV [0])

cu excepția cazului în care mesajul = ~ / \ s * # \ d + /
  pune „[POLITICA] Mesajul dvs. nu a făcut referire la un bilet."
  iesirea 1
Sfârșit

10. Când toate celelalte eșuează

Până acum am tratat destul de mult cum să remediați erorile comune atunci când lucrați cu git. Cei mai mulți dintre ei au soluții destul de ușoare, însă există momente în care cineva trebuie să scoată armele mari și să rescrie istoria unei întregi ramuri. Un caz de utilizare obișnuit pentru aceasta este eliminarea datelor sensibile (de exemplu, datele de autentificare de autentificare pentru sistemele de producție) care au fost angajate într-un depozit public:

git filter-branch --force --index-filter \
  'git rm - cached --ignore-unmatch secrets.txt' \
  --prune-vide - pisica -tag-name-filter - - all

Aceasta va elimina fișierul secrets.txt din fiecare ramură și etichetă. De asemenea, va elimina orice comitere care ar fi goală ca urmare a operației de mai sus. Rețineți că acest lucru va rescrie întregul istoric al proiectului dvs., care poate fi foarte perturbator într-un flux de lucru distribuit. De asemenea, în timp ce fișierul în cauză a fost înlăturat, datele de identificare pe care le conține ar trebui totuși considerate compromise!

GitHub are un tutorial foarte bun despre eliminarea datelor sensibile, iar omul git-filter-branch are toate detaliile despre diferitele filtre disponibile și opțiunile lor.

Publicat inițial la gist.github.com.