forked from tkrajina/uvod-u-git
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit-gc.tex
executable file
·167 lines (110 loc) · 9.18 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
162
163
164
165
166
167
\chapter*{"Higijena" repozitorija}
\addcontentsline{toc}{chapter}{"Higijena" repozitorija}
Za programere -- repozitorij je životni prostor i s njime se živi dio dana.
Kao što se trudimo držati stan urednim, tako bi trebalo i s našim virtualnim prostorima.
Preko tjedna, kad rano ujutro odlazimo na posao i vraćamo se kasno popodne, se ponekad desi da nam se u stanu nagomila robe za pranje.
Zato nekoliko puta tjedno treba uzeti pola sata vremena i počistiti nered koji je zaostao, inače će entropija zavladati, a to nikako ne želim(o).
Tako i s repozitorijem -- treba ga redovito održavati i čistiti nered koji ostavljamo za sobom.
\section*{Grane}
\addcontentsline{toc}{section}{Grane}
Iako nam git omogućuje da imamo neograničen broj grana, ljudski um nije sposoban vizualizirati si više od 5-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 -- dešava se da imamo one u kojima smo počeli neki posao i kasnije odlučili da nam ne treba.
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.
Ukoliko nismo sigurni je li nam u nekoj grani ostala možda još kakva izmjena koju treba vratiti u \verb+master+, postupak je sljedeći:
\gitoutputcommand{git checkout neka-grana\\git merge master}
\dots{}da bismo preuzeli sve izmjene iz master, tako da stanje bude ažurno.
I sad\dots
\gitoutputcommand{git diff master}
\dots{}za provjeru koje su točno izmjene ostale.
Ako je prazno -- nema razlika i možemo brisati.
Ako nije -- \textbf{ima} izmjena i sad se sami odlučujemo jesu li te izmjene nešto važno što ćemo nastaviti razvijati ili nešto što možemo zanemariti i obrisati tu granu.
Ukoliko baš morate imati puno grana, onda se potrudite dogovoriti neki logični način kako ih imenujete.
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 možete grane 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.
I sad, kad dobijete spisak svih grana -- odmah ćete znati koja grana čemu služi.
\section*{Git gc}
\addcontentsline{toc}{section}{Git gc}
Druga stvar koju 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.
\section*{Povijest i brisanje grana}
\addcontentsline{toc}{section}{Povijest i brisanje grane}
Spomenuti ćemo još nešto što bi logički pripadalo u poglavlja o granama i povijesti, ali tada nismo imali dovoljno znanja.
Što se dešava s \emph{commit}ovima iz neke grane nakon što ju 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 je jednostavno:
\textbf{Svi čvorovi koji su dio povijesti projekta će ostati u repozitoriju i neće biti obrisati s} \verb+git gc+.
Kako znamo koji čvorovi su dio \textbf{povijesti projekta}?
Po tome što postoji nešto (grana, čvor ili \emph{tag}) tko ima referencu na njih.
Krenimo sad primijeniti to pravilo na naš primjer\dots
Podsjetimo se da su strelice redosljed 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 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}
Ukoliko u ovom primjeru obrišemo \verb+eksperiment+, postoji samo jedan čvor koji će biti izgubljen, a to je $g$.
Bez referencu na granu, niti jedan čvor niti \emph{tag} ne pokazuje na $g$, dakle on prestaje biti dio povijesti našeg projekta.
$z$ ima referencu na $f$, a s $f$ nam garantira da i $e$ i $d$ ostaju dio povijesti projekta.
\section*{Digresija o brisanju grana}
\addcontentsline{toc}{section}{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 se tiče brisanja grane.
Postoje dva načina, prvog 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 odbiti obrisati granu samo ako \textbf{jest} potpuno \emph{merge}ana.
Dakle, ako imate strah da ne biste slučajno obrisali granu čije izmjene još niste preuzeli u neku drugu granu -- koristiti \verb+-d+ umjesto \verb+-D+ kod brisanja.
\section*{\emph{Squash merge} i brisanje grana}
\addcontentsline{toc}{section}{\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 ju kasnije ne \emph{merge}amo klasičnim putem.}.