forked from tkrajina/uvod-u-git
-
Notifications
You must be signed in to change notification settings - Fork 0
/
git-gc.tex
executable file
·137 lines (92 loc) · 7.83 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
\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 njen zadnji čvor/\emph{commit}.
Obrišemo li granu \verb+eksperiment+, obrisali smo referencu na njen 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 njen č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*{\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 da 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.}.