Înțelegerea mașinilor pentru a reduce incertitudinea

Cum sa nu te mai temi de GIT
Ai mai fost aici? (benzi desenate web de XKCD)

Ce este oricum Git?

„Este un sistem de control al versiunilor.”

De ce am nevoie de ea?

„Pentru controlul versiunilor, prostie.”

Bine, bine, încă nu sunt prea de ajutor. Iată ideea de bază: pe măsură ce proiectele devin prea mari, cu prea mulți colaboratori, devine imposibil să se urmărească cine a făcut ce și când. A introdus cineva o schimbare care a spart întregul sistem? Cum îți dai seama care a fost acea schimbare? Cum te întorci la cum erau lucrurile înainte? Înapoi în țara minunilor neîntreruptă?

Voi face un pas mai departe – să nu spunem un proiect cu mulți colaboratori, ci doar un proiect mic cu tine ca creator, întreținător și distribuitor: creezi o nouă caracteristică pentru acest proiect, care introduce bug-uri subtile pe care le vei afla mai târziu. Nu vă amintiți ce modificări ați făcut la baza de coduri existente pentru a crea această nouă caracteristică. Problemă?

Răspunsul la toate aceste probleme este versiunea! Având versiuni pentru tot ceea ce ați codat, vă asigură că știți cine a făcut modificările, ce modificări și exact unde, de la începutul proiectului!

Și acum, vă invit să nu vă mai gândiți la (g) ca o cutie neagră, să o deschideți și să aflați ce comori așteaptă. Aflați cum funcționează Git și nu veți mai avea niciodată o problemă în a face lucrurile să funcționeze. Odată ce ai trecut prin asta, îți promit că îți vei da seama de nebunia de a face ceea ce spune banda desenată XKCD de mai sus. Exact asta încearcă să prevină versiunea.

ad-banner

Cum să Git?

Presupun că știi comenzile de bază din Git sau ai auzit despre ele și le-ai folosit măcar o dată. Dacă nu, iată un vocabular de bază pentru a vă ajuta să începeți.

Repertoriu: un loc pentru depozitarea lucrurilor. Cu Git, aceasta înseamnă folderul de coduri

cap: Un „pointer” la cel mai recent cod la care lucrați

adăuga: O acțiune pentru a cere lui Git să urmărească un fișier

comite: O acțiune de salvare a stării actuale – astfel încât să puteți revedea această stare dacă este necesar

la distanta: Un depozit care nu este local. Poate fi în alt dosar sau în cloud (de exemplu: Github): ajută alte persoane să colaboreze cu ușurință, deoarece nu trebuie să primească o copie din sistemul dvs. – o pot obține doar din cloud. De asemenea, vă asigură că aveți o copie de rezervă în cazul în care vă rupeți laptopul

Trage: O acțiune pentru a obține codul actualizat de la telecomandă

Apăsați: O acțiune de trimitere a codului actualizat la telecomandă

combina: O acțiune pentru a combina două versiuni diferite de cod

stare: Afișează informații despre starea actuală a depozitului

Unde să Git?

Vă prezentăm magia controlată de un folder ascuns: .git/

În fiecare depozit git, veți vedea așa ceva

$ tree .git/
.git/
├── HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
8 directories, 14 files

Acesta este modul în care Git controlează și gestionează întregul dvs. proiect. Vom intra în toți biții importanți, unul câte unul.

Git este format din 3 părți: depozitul de obiecte, indexul și directorul de lucru.

Magazinul de obiecte

Acesta este modul în care Git stochează totul pe plan intern. Pentru fiecare fișier din proiectul dvs. pe care îl aveți add, Git generează un hash pentru fișier și stochează fișierul sub acel hash. De exemplu, dacă acum creez un helloworld depuneți și faceți git add helloworld (care îi spune lui Git să adauge fișierul numit helloworld la magazinul de obiecte git), primesc așa ceva:

$ tree .git/
.git/
├── HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── objects
│   ├── a0
│   │   └── 423896973644771497bdc03eb99d5281615b51
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags
9 directories, 16 files

Un nou obiect a fost generat! Pentru cei interesați să meargă sub capotă, Git folosește intern obiect hash comandă așa:

$ git hash-object helloworld
a0423896973644771497bdc03eb99d5281615b51

Da, este același hash pe care îl vedem sub folderul obiecte. De ce subdirectorul cu primele două caractere ale hashului? Face căutarea mai rapidă.

Apoi, Git creează un obiect cu numele ca hash de mai sus, comprimă fișierul dat și îl stochează acolo. Prin urmare, puteți vedea, de asemenea, conținutul obiectului!

$ git cat-file a0423896973644771497bdc03eb99d5281615b51 -p
hello world!

Toate acestea sunt sub capotă. Nu ai folosi niciodată pisică în zi cu zi adaugă. Vei pur și simplu addși lăsați-l pe Git să se ocupe de restul.

Aceasta este prima noastră comandă Git, terminată și prăfuită.

git add creează un hash, comprimă fișierul și adaugă obiectul comprimat în depozitul de obiecte.

Directorul de lucru

După cum sugerează și numele, aici lucrezi. Toate fișierele pe care le creați și le editați se află în directorul de lucru. Am creat un fișier nou, byeworld și a fugit git status:

$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
new file:   helloworld
Untracked files:
  (use "git add <file>..." to include in what will be committed)
byeworld

Fișierele care nu sunt urmărite sunt fișiere din directorul de lucru pe care nu i-am cerut git să le gestioneze.

Dacă nu am fi făcut nimic în directorul de lucru, am primi următorul mesaj:

$ git status
On branch master
nothing to commit, working tree clean

pe care sunt destul de sigur că o înțelegi acum. Ignorați filiala și angajați-vă pentru moment. Cheia este că arborele de lucru (directorul) este curat.

Indicele

Acesta este nucleul Git. Cunoscută și sub numele de zona de înscenare. Indexul stochează maparea fișierelor cu obiectele din depozitul de obiecte. Acesta este locul unde commits intră. Cel mai bun mod de a vedea acest lucru este să îl testezi!

Să comitem adăugarea noastră a fișierului helloworld

$ git commit -m "Add helloworld"
[master (root-commit) a39b9fd] Add helloworld
 1 file changed, 1 insertion(+)
 create mode 100644 helloworld

Înapoi la arborele nostru:

$ tree .git/
.git/
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── a0
│   │   └── 423896973644771497bdc03eb99d5281615b51
│   ├── a3
│   │   └── 9b9fdd624c35eee08a36077f411e009da68c2f
│   ├── fb
│   │   └── 26ca0289762a454db2ef783c322fedfc566d38
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags
14 directories, 22 files

Ah, interesant! Avem 2 obiecte noi în magazinul nostru de obiecte și unele lucruri pe care nu le înțelegem încă în jurnalele și referințele. Revenind la prietenul nostru cat-file:

$ git cat-file a39b9fdd624c35eee08a36077f411e009da68c2f -p
tree fb26ca0289762a454db2ef783c322fedfc566d38
author = <=> 1537700068 +0100
committer = <=> 1537700068 +0100
Add helloworld
$ git cat-file fb26ca0289762a454db2ef783c322fedfc566d38 -p
100644 blob a0423896973644771497bdc03eb99d5281615b51 helloworld

După cum puteți ghici, primul obiect este metadatele de comitere: cine a făcut ce și de ce, cu un copac. Al doilea obiect este arborele propriu-zis. Dacă înțelegeți sistemul de fișiere Unix, veți ști exact ce este acesta.

tree în Git corespunde sistemului de fișiere Git. Totul este fie un copac (director), fie un blob (fișier) și cu fiecare commit, Git stochează și informațiile arborelui, pentru a-și spune: așa ar trebui să arate directorul de lucru în acest moment. Rețineți că arborele indică un anumit obiect al fiecărui fișier pe care îl conține (hash-ul).

Este timpul să vorbim despre ramuri! Primul nostru commit a adăugat alte lucruri la .git/ de asemenea. Interesul nostru este acum în .git/refs/heads/master:

$ cat .git/refs/heads/master 
a39b9fdd624c35eee08a36077f411e009da68c2f

Iată ce trebuie să știți despre sucursale:

O ramură în Git este un indicator ușor mobil la una dintre aceste comitere. Numele implicit al sucursalei în Git este master.

Eh, ce? Îmi place să mă gândesc la ramuri ca la o furculiță în codul tău. Vrei să faci unele modificări, dar nu vrei să rupi lucrurile. Decizi să ai o delimitare mai puternică decât jurnalul de comitere și acolo intră sucursalele. master este ramura implicită, utilizată și ca ramură de producție de facto. Prin urmare, crearea fișierului de mai sus. După cum puteți ghici după conținutul fișierului, acesta indică primul nostru commit. Prin urmare, este un indicator către un commit.

Să explorăm acest lucru mai departe. Spuneți, creez o ramură nouă:

$ git branch the-ending
$ git branch
* master
  the-ending

Iată-l, o ramură nouă! După cum puteți ghici, trebuie să fi fost adăugată o nouă intrare .git/refs/heads/ și întrucât nu există nici un commit suplimentar, ar trebui să indice și primul nostru commit, la fel ca master.

$ cat .git/refs/heads/the-ending a39b9fdd624c35eee08a36077f411e009da68c2f

Da, exact! Acum, ține minte byeworld? Acel fișier nu era încă urmărit, așa că, indiferent de ramura în care te-ai deplasa, acel fișier ar fi întotdeauna acolo. Spune, vreau să trec la această ramură acum, așa că o voi face checkout ramura, ca o afumare.

$ git checkout the-ending
Switched to branch 'the-ending'
$ git branch
  master
* the-ending

Acum, sub capotă, Git ar schimba tot conținutul directorului de lucru pentru a se potrivi cu conținutul indicat de commit-ul ramurii. Deocamdată, deoarece acesta este exact la fel ca master, arată la fel.

Eu add și commit byeworld fişier.

Ce vă așteptați să schimbați în objects pliant?

Ce vă așteptați să schimbați în refs/heads pliant?

Gândiți-vă la acest lucru înainte de a merge mai departe.

$ tree .git/
.git/
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           ├── master
│           └── the-ending
├── objects
│   ├── 0b
│   │   └── 17be9dbc34c5a5fbb0b94d57680968efd035ca
│   ├── a0
│   │   └── 423896973644771497bdc03eb99d5281615b51
│   ├── a3
│   │   └── 9b9fdd624c35eee08a36077f411e009da68c2f
│   ├── b3
│   │   └── 00387d818adbbd6e7cc14945fdf4c895de6376
│   ├── d1
│   │   └── 8affe001488123b496ceb34d8b13b120ab4cb6
│   ├── fb
│   │   └── 26ca0289762a454db2ef783c322fedfc566d38
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   ├── master
    │   └── the-ending
    └── tags
17 directories, 27 files

3 obiecte noi – 1 pentru adăugare, 2 pentru comitere! Are sens? Ce crezi că conțin obiectele?

  • Confirmați metadatele
  • add conținutul obiectului
  • Descrierea arborelui

Ultima parte a imaginii este: cum funcționează această metadate de validare cu metadatele de validare anterioare. Bine, cat-file!

$ git cat-file 0b17be9dbc34c5a5fbb0b94d57680968efd035ca -p
100644 blob d18affe001488123b496ceb34d8b13b120ab4cb6 byeworld
100644 blob a0423896973644771497bdc03eb99d5281615b51 helloworld
$ git cat-file b300387d818adbbd6e7cc14945fdf4c895de6376 -p
tree 0b17be9dbc34c5a5fbb0b94d57680968efd035ca
parent a39b9fdd624c35eee08a36077f411e009da68c2f
author = <=> 1537770989 +0100
committer = <=> 1537770989 +0100
add byeworld
$ git cat-file d18affe001488123b496ceb34d8b13b120ab4cb6 -p
Bye world!
$ cat .git/refs/heads/the-ending 
b300387d818adbbd6e7cc14945fdf4c895de6376

O vezi cu bold? Indicatorul părinte! Și exact așa te-ai gândit la asta – o listă legată, care leagă comitetele!

Și vedeți implementarea sucursalei? Acesta indică un commit, cel mai recent pe care l-am făcut după ce am verificat! Bineînțeles, stăpânul ar trebui să arate în continuare spre angajamentul din lumea hellow, nu?

$ cat .git/refs/heads/master a39b9fdd624c35eee08a36077f411e009da68c2f

Bine, am trecut prin multe, să le rezumăm până aici.

TL; DR

Git funcționează cu obiecte – versiuni comprimate ale fișierelor pe care le cereți Git să le urmărească.

Fiecare obiect are un ID (un hash generat de Git pe baza conținutului fișierului).

De fiecare dată când add un fișier, Git adaugă un obiect nou în depozitul de obiecte. Acesta este exact motivul pentru care nu vă puteți ocupa de fișiere foarte mari în Git – stochează întregul fișier de fiecare dată add schimbări, nu diferența (contrar credinței populare).

Fiecare comitere creează 2 obiecte:

  1. Copacul: Un ID pentru arbore, care acționează exact ca un director Unix: arată către alți arbori (directoare) sau blob-uri (fișiere): Aceasta construiește întreaga structură a directorului pe baza obiectelor prezente în acel moment. Bloburile sunt reprezentate de obiectele curente create de add.
  2. Metadatele de comitere: Un ID pentru commit, care a făcut commit-ul, un arbore care reprezintă commit-ul, mesajul commit și commit părintele. Formează o structură de listă legată care face legătura între ele.

Ramurile sunt indicii pentru a comite obiecte de metadate, toate stocate în .git/refs/heads

Atât pentru înțelegerea din culise! În partea următoare, vom parcurge câteva dintre acțiunile Git care oferă oamenilor coșmaruri:

reset, merge, pull, push, fetch și modul în care modifică structura internă în .git/.

Alte povești din această serie:

Ti-a placut asta? Nu ratați din nou o postare – abonați-vă la lista mea de e-mail!