forked from tkrajina/uvod-u-git
-
Notifications
You must be signed in to change notification settings - Fork 0
/
git-gc.tex
executable file
·162 lines (104 loc) · 8.87 KB
/
git-gc.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
\chapter*{"Higijena" repozitorija}
\addcontentsline{toc}{chapter}{"Higijena" repozitorija}
Za programere je repozitorij životni prostor i s njime se živi dio dana.
Kao što se trudimo držati stan urednim, tako bi trebali i s našim virtualnim prostorima.
Preko tjedna, kad rano ujutro odlazimo na posao i vraćamo se kasno popodne, ponekad se desi da nam se u stanu nagomila robe za pranje.
Zato nekoliko puta tjedno treba odvojiti pola sata i počistiti nered koji je zaostao, inače će entropija zavladati, a to nikako ne želim(o).
Nadam se.
Tako i s repozitorijem; treba ga redovito održavati i čistiti nered koji ostavljamo za sobom.
\tocSection{Grane}
Iako nam git omogućuje da imamo neograničen broj grana, ljudski um nije sposoban vizualizirati si više od 5 do 10 njih\footnote{Barem moj nije, ako je vaš izuzetak, preskočite sljedećih nekoliko rečenica. Ili jednostavno zamislite da je umjesto "5-10" pisalo "500-1000".}.
Kako stvaramo nove grane, događa se da imamo one u kojima smo počeli neki posao i kasnije odlučili da nam to neće trebati.
Ili smo napravili posao, \emph{merge}ali u \verb+master+, ali nismo obrisali granu.
Nađemo li se s više od 10-15 grana \textbf{sigurno} je dio njih tu samo zato što smo ih zaboravili obrisati.
U svakom slučaju, predlažem vam da svakih par dana pogledate malo po lokalnim (a i udaljenim granama) i provjerite one koje više ne koristite.
Ako nismo sigurni je li nam u nekoj grani ostala možda još kakva izmjena koju treba vratiti u \verb+master+, možemo koristiti naredbu:
\gitoutputcommand{git branch --merged master}
To će nam ispisati popis svih grana čije izmjene \textbf{su u potpunosti} \emph{merge}ane u master.
Analogno, postoji i naredba s kojom dobijamo popis svih onih grana koje \textbf{nisu} u potpunosti \emph{merge}ane u neku drugu granu:
\gitoutputcommand{git branch --no-merged <naziv\_grane>}
Ako baš morate imati puno grana, onda dogovorite s drugim ljudima u projektu neki logičan način kako ćete grane imenovati.
Na primjer, ako koristite neki sustav za prijavu i praćenje grešaka, onda svaka greška ima neki svoj broj.
Možete se odlučiti svaku grešku ispravljati u posebnoj grani.
Imate li veliku i kompleksnu aplikaciju, biti će i puno prijavljenih grešaka, a posljedično i puno grana.
Tada grane možete imenovati prema broju prijavljene greške zajedno s kratkim opisom.
Na primjer, \verb+123-unicode-problem+ bi bila grana u kojoj ispravljate problem prijavljen pod brojim 123, a radi se o (tko bi rekao?) nekom problemu s \emph{unicode} enkodiranjem.
Sad, kad dobijete spisak svih grana, odmah ćete znati koja grana čemu služi.
\tocSection{Git gc}
Druga stvar koja se preporuča ima veze s onim našim \verb+.git/objects+ direktorijem kojeg smo spominjali u "Ispod haube" poglavlju.
Kao što znamo, svaki \emph{commit} ima svoju referencu i svoj objekt (datoteku) u tom direktoriju.
Kad napravimo \verb+git commit --amend+ -- git stvara \textbf{novi} \emph{commit}.
Nije da on samo izmijeni stari\footnote{Ne bi ni mogao izmijeniti stari jer ima drukčiji sadržaj i SHA1 bi mu se nužno morao promijeniti.}.
Grafički:
\input{graphs/amend}
Dakle, git interno dodaje \textbf{novi} objekt (\emph{f'}) i na njega pomiče referencu \verb+HEAD+ (koja je do tada gledala na \emph f).
On samo "kaže": Od sada na dalje, zadnji \emph{commit} u ovoj grani više nije \emph f, nego \emph{f'}.
Sad u git repozitoriju imamo i \emph{commit} \emph f, a i \emph{f'}, ali samo jedan od njih se koristi (\emph{f'}).
Commit \emph f je \textbf{i dalje snimljen u} \verb+.git/object+ \textbf{direktoriju}, ali on se više neće koristiti.
Puno tih \verb+git commit --amend+ posljedično ostavlja puno "smeća" u repozitoriju.
To vrijedi i za neke druge operacije kao brisanje grana ili rebase.
Git to čini da bi tekuće operacije bile što je moguće brže.
Čišćenje takvog "smeća" (\emph{garbage collection} iliti \emph{gc}) ostavlja za kasnije, a ta radnja nije automatizirana nego se od nas očekuje da ju pokrenemo\footnote{Nije automatizirana, ali možemo uvijek sami napraviti neki task koji se izvršava na dnevnoj ili tjednoj bazi, a koji "čisti" sve naše git repozitorije.}.
Naredba je \verb+git gc+:
\input{git_output/git_gc}
\dots{}i nju treba izvršavati s vremena na vrijeme.
Osim \verb+gc+, postoji još nekoliko sličnih naredbi kao \verb+git repack+, \verb+git prune+, no one su manje važne za početnika.
Ako vas zanimaju -- \verb+git help+ je uvijek na dohvat ruke.
\tocSection{Povijest i brisanje grana}
Spomenuti ćemo još nešto što bi logički pripadalo u poglavlja o granama i povijesti, ali tada za to nismo imali dovoljno znanja.
Što se događa s \emph{commit}ovima iz neke grane nakon što je obrišemo?
Uzmimo tri primjera.
U sva tri imamo dvije grane: \verb+master+ i \verb+eksperiment+.
Prvi primjer:
\input{graphs/git_merge_i_brisanje_grana_1}
Pravilo po kojem git razlikuje čvorove koje će ostaviti u povijesti od onih koje će obrisati jednostavno je:
\textbf{Svi čvorovi koji su dio povijesti projekta ostat će u repozitoriju i neće biti obrisani s} \verb+git gc+.
Kako znamo koji čvorovi su dio \textbf{povijesti projekta}?
Po tome što postoji nešto (grana, čvor ili \emph{tag}) što ima referencu na njih.
Krenimo sad primijeniti to pravilo na naš primjer\dots
Podsjetimo se da su strelice redoslijed nastajanja, ali reference idu suprotnim smjerom, sljedbenik ima referencu na prethodnika.
Dakle, $g$ ima referencu na $f$, $f$ na $e$, itd.
Što je sa čvorom $g$?
Izgleda kao da nitko nema referencu na njega, ali to nije točno; grana \verb+eksperiment+ je referenca na njega.
Ako obrišemo granu \verb+eksperiment+ -- $g$ više nema nikoga da se njega referencira.
\verb+git gc+ će ga obrisati, ali onda mora obrisati i $f$, $e$ i $d$ ($b$ ne možemo, jer $c$ ima referencu na njega).
Dakle, kad obrišemo granu koja nije \emph{merge}ana u neku drugu granu, onda se svi njeni čvorovi gube iz povijesti projekta.
Drugi primjer:
\input{graphs/git_merge_i_brisanje_grana_2}
Ovaj primjer je isti kao i prvi s jednom razlikom.
Došlo je do \emph{merge}a.
Znamo da grana nije ništa drugo nego referenca na zadnji čvor/\emph{commit}.
Obrišemo li granu \verb+eksperiment+, obrisali smo referencu na zadnji čvor $g$, ali i dalje imamo $q$ koji pokazuje na $g$.
Zbog toga će svi čvorovi grane \verb+eksperiment+ nakon njenog brisanja ostati u repozitoriju.
Treći primjer:
\input{graphs/git_merge_i_brisanje_grana_3}
Ako u ovom primjeru obrišemo \verb+eksperiment+, postoji samo jedan čvor koji će biti izgubljen, a to je $g$.
Bez reference na granu, niti jedan čvor niti \emph{tag} ne pokazuje na $g$. Dakle, on prestaje biti dio povijesti našeg projekta.
Za razliku od njega, $z$ ima referencu na $f$, a s $f$ nam garantira da i $e$ i $d$ ostaju dio povijesti projekta.
\tocSection{Digresija o brisanju grana}
Uzmimo opet iste dvije situacije, onu u kojoj će se svi čvorovi grane sačuvati:
\input{graphs/git_merge_i_brisanje_grana_2}
\dots{}i onu u koju gubimo samo neke čvorove:
\input{graphs/git_merge_i_brisanje_grana_3}
Prvu situaciju zovemo \textbf{potpuno \emph{merge}ana grana}, a drugu \textbf{djelomično \emph{merge}ana grana}.
Znamo sad da postoje situacije u kojima čak i nakon brisanja grane -- njeni \emph{commit}ovi ostaju u povijesti projekta.
Mogli bi si postaviti pitanje: "Zašto uopće brisati grane?"
Odgovor je jednostavan: brisanjem grane nećemo više tu granu imati u listi koji dobijemo s \verb+git branch+.
Kad bi tamo imali sve grane koje smo ikad imali u povijesti projekta (a njih može biti jako puno) bilo bi se teško snaći u podužem ispisu.
Druga digresija koju ćemo ovdje napraviti tiče se brisanja grane.
Postoje dva načina. Prvi kojeg smo već spomenuli:
\gitoutputcommand{git branch -D grana}
\dots{}koji bezuvjetno briše granu \verb+grana+, a drugi je:
\gitoutputcommand{git branch -d grana}
\dots{}koji će obrisati granu samo ako \textbf{jest} potpuno \emph{merge}ana.
Ako nije, odbiti će obrisati.
Dakle, ako vas je strah da biste slučajno obrisali granu čije izmjene još niste preuzeli u neku drugu granu -- koristite \verb+-d+ umjesto \verb+-D+ kod brisanja.
\tocSection{\emph{Squash merge} i brisanje grana}
Uzmimo opet:
\input{graphs/git_merge_i_brisanje_grana_4}
Želimo li u povijesti projekta sačuvati izmjene iz neke grane, ali ne i njenu povijest, to se može s \verb+git merge --squash+.
Podsjetimo se, tom operacijom git \textbf{hoće} preuzeti izmjene iz grane, ali \textbf{neće} u čvoru $q$ napraviti referencu na $g$.
Dakle, rezultat je kao kod klasičnog \emph{merge}a, ali bez reference (u prethodnom grafu crvenom bojom):
\input{graphs/git_merge_i_brisanje_grana_5}
Sad smo preuzeli izmjene iz \verb+grana+ u \verb+master+, ali \verb+git gc+ će prije ili kasnije obrisati $d$, $e$, $f$ i $g$.
S \verb+git merge --squash+ cijelu granu svodimo na jedan \emph{commit} i kasnije gubimo njenu povijest\footnote{\dots{}barem ako je kasnije ne \emph{merge}amo klasičnim putem.}.