This commit is contained in:
Martin Vuk 2026-01-14 21:24:20 +01:00
parent ec325e72cf
commit 4d3b048954

View file

@ -43,13 +43,13 @@ marsikatere zagate, ki nastane med njegovo uporabo.
\section{Kaj je Git?}
\href{https://git-scm.com/}{Git} je kot \textbf{časovni stroj} za
datoteke. Uporabniku omogoča, da vidi \textbf{pretekle različice}
datotek, spreminja datoteke, \textbf{brez skrbi, da bi kaj pokvaril} in
datoteke \textbf{deli z drugimi}. Poleg časovnega stroja je Git
\textbf{razpršeno skladišče datotek}. Omogoča, da datoteke
\textbf{hkrati ureja več uporabnikov} na različnih računalnikih in
kasneje spremembe \textbf{združi}.
\href{https://git-scm.com/}{Git} je kot \emph{časovni stroj} za
datoteke. Uporabniku omogoča, da vidi \emph{pretekle različice}
datotek, spreminja datoteke, brez skrbi, da bi kaj pokvaril in
jih deli z drugimi. Poleg časovnega stroja je Git
tudi \emph{razpršeno skladišče datotek}. Omogoča, da datoteke
hkrati ureja več uporabnikov na različnih računalnikih in
kasneje spremembe združi.
Git hrani vsebino mape z datotekami in celotno zgodovino različic
datotek iz preteklosti. Za vsako različico hrani Git zapis o avtorju,
@ -68,7 +68,7 @@ sistem za nadzor različic} (angl. Distributed Version Control System
(DVCS)).
\begin{opomba}
Git in GitHub nista eno in isto. Ljudje pogosto mešajo Git in GitHub.
Ljudje pogosto mešajo Git in GitHub, ki pa nista eno in isto.
Git je program, ki si ga lahko vsakdo namesti in poganja na svojem
računalniku. Program Git je ustvaril Linus Torvalds, da bi lažje
upravljal z izvorno kodo za jedro operacijskega sistema Linux. GitHub je
@ -124,8 +124,10 @@ bitno število (40 mestno število v 16-tiškem zapisu), ki ga izračuna iz
vsebine datoteke. Git za izračun imena uporabi \emph{zgoščevalno
funkcijo}. Naj bo \(B\) množica vseh možnih podatkovnih nizov(besedil),
\(n\)-bitna zgoščevalna funkcija je funkcija
\[H:B \rightarrow \left\{ 0,1,\ldots,2^{n} - 1 \right\},\] ki vsakemu
besedilu \(b\) priredi \(n\)-bitno vrednost \(H(b)\). Vrednosti
\[
H:B \rightarrow \left\{ 0,1,\ldots,2^{n} - 1 \right\},
\]
ki vsakemu besedilu \(b\) priredi \(n\)-bitno vrednost \(H(b)\). Vrednosti
zgoščevalne funkcije \(H(b)\) pravimo \emph{zgostitev} vsebine \(b\)
(angl. hash). Git hrani datoteke pod imeni, ki so enaka zgostitvi
vsebine. Kaj pa če imata dve različni vsebini isto zgostitev? Funkcija
@ -137,8 +139,8 @@ za drugo pa bi predpostavil da je že shranjena. Zato je funkcija \(H\)
izbrana tako, da sprememba enega samega bita v besedilu \(b \in B\)
spremeni vrednost \(H(b)\) in je porazdelitev vrednosti \(H(b)\) čim
bližje enakomerni porazdelitvi. To pomeni, da so vse vrednosti \(H(b)\)
približno enako verjetne. Na ta način zmanjšamo verjetnost trka(
glej \hyperref[sec_trk]{{[}sec\_trk{]}}). Verjetnost trka
približno enako verjetne. Na ta način zmanjšamo verjetnost trka
(glej poglavje \ref{sec_trk}). Verjetnost trka
je izjemno majhna, zato Git lahko predpostavi, da je niz \(b\) enolično
določen z njegovo zgostitvijo \(H(b)\).
@ -151,11 +153,11 @@ uporabljala v kriptografiji\footnote{Leta 2017 so raziskovalci iz CWI
\emph{SHAttered}. Git je zato z verzijo \texttt{v2.13.0} začel
uporabljati verzijo SHA1, ki je odporna proti napadu \emph{SHAttered}.
Kljub temu razvijalci Gita načrtujejo, da bodo SHA1 postopoma
nadomestili s SHA-256.}.
nadomestili s $256$ bitno zgoščevalno funkcijo SHA-256.}.
Ko datoteko z vsebino \(b\) zabeležimo v Git repozitorij, Git izračuna
zgostitev vsebine \(H(b)\) in jo shrani v datoteko z imenom \(H(b)\) v
\texttt{git/objects}\footnote{V resnici Git shrani vsebino v datoteko z
\texttt{.git/objects}\footnote{V resnici Git shrani vsebino v datoteko z
imenom \(h_{3}h_{4}\ldots h_{40}\) v mapi \(h_{1}h_{2}\), kjer je
\(h_{1}h_{2}h_{3}\ldots h_{40}\) zapis \(H(b)\) v 16-tiškem sistemu.
Datoteka, katere vsebina ima zgostitev \(H(b)\) enako
@ -178,8 +180,20 @@ in njihovih prejšnjih različic. A kako ohranimo informacijo o imenu
datotek in drevesni strukturi mape? Git za to ustvari nov tip objekta
\emph{drevo} (angl. \emph{tree}), ki hrani preprost seznam imen datotek
in naslovov na vsebino datotek v mapi. Naslov na vsebino datoteke \(b\)
je seveda zgostitev vsebine \(H(b)\). Seznam imen datotek in zgostitev
je preprosta tekstovna datoteka, za katero lahko izračunamo zgostitev.
je seveda zgostitev vsebine \(H(b)\).
\begin{figure}[h]
\centering
\begin{Verbatim}[frame=single]
100644 blob 33476f4951afc28d5ac2dc0d42d82f17ac817de2 bla.txt
100644 blob 2ce22b4dc77442103f095503f1205937c1b0fcfc blabla.txt
040000 tree ae247f2a35aadade5863aec2475cf13020304b06 podmapa
\end{Verbatim}
\caption{Vsebina mape v Gitu je preprost seznam datotek in podmap ter
zgostitev njihove vsebine. Številke na začetku določajo dovoljenja za datoteke
po sistemu Posix.}
\end{figure}
Drevo preprost seznam v tekstovni datoteki, za katerega lahko prav tako izračunamo zgostitev.
Zgostitev datotečnega drevesa natanko določa tako imena datotek, kot
tudi vsebino datotek, ki so vsebovane v mapi. Če se katerakoli datoteka
ali ime datoteke v mapi spremeni, se bo spremnila tudi njena zgostitev
@ -188,21 +202,10 @@ vsebuje tudi poddrevesa. Tako lahko rekurzivno ustvarimo drevesno
podatkovno strukturo, ki zajema mapo z datotekami in podmapami v
poljubni globini.
\begin{figure}
\centering
\begin{Verbatim}[frame=single]
100644 blob 33476f4951afc28d5ac2dc0d42d82f17ac817de2 bla.txt
100644 blob 2ce22b4dc77442103f095503f1205937c1b0fcfc blabla.txt
040000 tree ae247f2a35aadade5863aec2475cf13020304b06 podmapa
\end{Verbatim}
\caption{Vsebina mape v Gitu je preprost seznam datotek in podmap ter
zgostitev njihove vsebine}
\end{figure}
Poglejmo si primer. Denimo, da imamo v korenski mapi naslednje datoteke in
podmape.
Poglejmo si primer. Denimo, da imamo v naslednjo strukturo datotek in
podmap
\begin{figure}
\begin{figure}[h]
\centering
\begin{Verbatim}[frame=single]
├── bla.txt (vsebina: bla)
@ -212,7 +215,7 @@ podmap
\end{Verbatim}
\caption{Struktura datotek in podmap, ki jo bomo hranili v Gitu.}
\end{figure}
\pagebreak
Git bo shranil naslednje objekte v vsebinsko naslovljivo shrambo:
\begin{itemize}
@ -249,11 +252,12 @@ Z uporabo zgostitve kot kazalca na vsebino, Git vsebino mape postavi v
podatkovno strukturo, ki jo matematično lahko opišemo z \emph{usmerjenim
grafom}. Če je vsebina datotek enaka(npr. \texttt{bla.txt} in
\texttt{mapa/bla.txt}), Git shrani le eno kopijo, ki je dostopna v
datoteki \linebreak\texttt{.git/objects/bc/c1382241e267cf790ca6b3afe9fde6dcf1072f}.
datoteki z imenom enakim zgostitvi vsebine.
Zato datotečno drevo v Gitu ni nujno predstavljeno kot drevo, ampak kot
\emph{usmerjen aciklični graf}.
\emph{usmerjen (aciklični) graf}\footnote{Teoretično bi lahko dosegli, da bi bili v grafu tudi cikli,
a je to zelo malo verjetno in zato to možnost ignoriramo.}.
\begin{figure}
\begin{figure}[h]
\centering
\includegraphics[width=0.6\linewidth]{slike/file-graph.pdf}
\caption{Primer datotečnega grafa povezanega z zgostitvami. Zaradi
@ -266,22 +270,22 @@ zgostitev spremenjene vsebine in sprememba bo splavala na površje do
zgostitve za korensko mapo. Zgostitev služi tako kot identifikator
vsebine, kot tudi kot kontrolna vsota, ki omogoča detekcijo sprememb.
Opomba\\
\begin{opomba}
Podatkovna struktura objektov v Gitu je podobna Merklejevim drevesom.
Razlika je v tem, da Gita hrani le eno kopijo datotek z identično
vsebino, zato dobimo usmerjen aciklični graf in ne drevesa. Postopek je
podoben veriženju blokov, ki se uporablja v kriptovalutah.
vsebino, zato dobimo usmerjen aciklični graf in ne drevesa. Postopek graditve
datotečnega drevesa v Gitu je soroden veriženju blokov, ki se uporablja v
kriptovalutah.
\end{opomba}
Opomba\\
Dostop do objekta je mogoč, če poznamo \textbf{zgostitev} njegove
vsebine. To pomeni, da je referenca na posamezen objekt v Gitu preprosto
zgostitev(angl. hash) vsebine tega objekta. Po drugi strani je vsebina
objekta določena z njegovo zgostitvijo. To pomeni, da lahko enostavno
preverimo verodostojnost vsebine, ki je shranjena v Gitu. Git hrani
skladišče objektov v mapi \texttt{.git/objects}.
Ponovimo, kar smo spoznali o Gitu. Git hrani vsebino datotek in datotečno strukturo
v \emph{vsebinsko naslovljivi shrambi} (v mapi \texttt{.git/objects}).
To pomeni, da je referenca na posamezen objekt v Gitu preprosto zgostitev njegove vsebine
in da lahko do določene vsebine dostopamo le, če poznamo njeno zgostitev. Po drugi strani
je vsebina za vse praktične primere določena s svojo zgostitvijo. Tako lahko enostavno
preverimo verodostojnost vsebine, ki je shranjena v Gitu.
\section{Trki zgostitev in rojstnodnevni
paradoks}\label{sec_trk}
\section{Trki zgostitev in rojstnodnevni paradoks}\label{sec_trk}
Git hrani datoteke pod imeni, ki so enaka zgostitvi vsebine. Če imata
dve datoteki z različno vsebino isto zgostitev, Git shrani le eno
@ -299,16 +303,26 @@ tako da je vsaka izbira enakomerno porazdeljena. Kolikšna je verjetnost
\(p(n,h)\), da bosta vsaj dve števili enaki? Verjetnost \(p(n,h)\)
izračunamo elementarno z verjetnostjo nasprotnega dogodka:
\[1 - p(n,h) = \frac{h \cdot (h - 1)\cdots(h - n + 1)}{h^{n}} = \prod_{k = 1}^{n - 1}\left( 1 - \frac{k}{h} \right).\]
\[1 - p(n,h) = \frac{h \cdot (h - 1)\cdots(h - n + 1)}{h^{n}} =
\prod_{k = 1}^{n - 1}\left( 1 - \frac{k}{h} \right).\]
Če izraz logaritmiramo, dobimo
\[\log(1 - p(n,h)) = \sum_{k = 1}^{n - 1}\log(1 - \frac{k}{h}) < - \sum_{k = 1}^{n - 1}\frac{k}{h} = \frac{- \left( n(n - 1) \right)}{2h}.\]\protect\phantomsection\label{eq_log_ocena}{}
\begin{eqnarray}
\log(1 - p(n,h)) = \sum_{k = 1}^{n - 1}\log(1 - \frac{k}{h}) <
- \sum_{k = 1}^{n - 1}\frac{k}{h} = \frac{- \left( n(n - 1) \right)}{2h}.
\label{eq_log_ocena}
\end{eqnarray}
Res! Logaritem je konveksna funkcija, zato so vrednosti manjše od
vrednosti na tangenti
\(\log(1 - \frac{k}{h}) = \log(1 - x) < x = \frac{k}{h}\).
Od tod izpeljemo oceno za \(p(n,h)\)
\[p(n,h) > 1 - e^{\frac{- \left( n(n - 1) \right)}{2h}} \approx 1 - e^{- \frac{n^{2}}{2h}}.\]\protect\phantomsection\label{eq_ocena}{}
\begin{eqnarray}
p(n,h) > 1 - e^{\frac{-\left( n(n - 1) \right)}{2h}} \approx 1 - e^{-\frac{n^{2}}{2h}}.
\label{eq_ocena}
\end{eqnarray}
Za vrednosti \(1 \ll n \ll h\) je \(1 - e^{- \frac{n^{2}}{2h}}\) tudi
dobra aproksimacija za \(p(n,h)\).
@ -317,8 +331,7 @@ trka, moramo rešiti obratno nalogo: največ koliko števil \(n(p,d)\)
lahko izberemo, da bo verjetnost pojava dveh enakih števil manjša od
\(p \in \lbrack 0,1\rbrack\)? Natančen odgovor na to vprašanje ni tako
preprost \cite{brink_probably_2012}. Lahko pa uporabimo oceno
(\hyperref[eq_ocena]{{[}eq\_ocena{]}}) in čez palec ocenimo vrednost
\(n(p,h)\):
(\ref{eq_ocena}) in čez palec ocenimo vrednost \(n(p,h)\):
\[\begin{array}{r}
- n^{2} \approx \log(1 - p) \Rightarrow \\
@ -343,12 +356,12 @@ podatkovne strukture.
\section{Posnetki stanja}
Osnovna enota v Gitu je \textbf{vnos} (angl. \textbf{commit}). Vnos je
Osnovna enota v Gitu je \emph{vnos} (angl. \emph{commit}). Vnos je
posnetek stanja zabeleženih datotek v trenutku, ko je bil ustvarjen.
Poleg vsebine datotek vsak vnos vsebuje še metapodatke o avtorju, datumu
vnosa in opisom sprememb. Podobno kot objekt tipa \emph{drevo}, je tudi
vnos objekt v vsebinsko naslovljivi shrambi, ki ima določeno
\textbf{zgostitev vnosa}. Zgostitev vnosa je natanko določena z vsebino
vnos objekt v vsebinsko naslovljivi shrambi in ima določeno
\emph{zgostitev vnosa}. Zgostitev vnosa je natanko določena z vsebino
shranjenih datotek in metapodatkov vnosa.
\begin{table}
@ -437,15 +450,13 @@ premakne naprej, značka \protect\texttt{v-1.0} pa ostane tam, kjer je
bila.}
\end{figure}
Opomba\\
Veje in značke nimajo v Gitu nobenega posebnega pomena, razen tega, da
so reference na vnose. Pomen posamenznih vej je stvar dogovora med
uporabniki. Tako se pogosto uporablja različne veje za različne namene:
\texttt{main} ali \texttt{master} je navadno glavna veja razvoja, veje z
imeni \texttt{stable}, \texttt{production}, \texttt{development} in
podobno označujejo različne stopnje razvoja programske opreme, veje s
predpono \texttt{feature-} označujejo razvoj novih funkcionalnosti.
predpono \texttt{feature-*} označujejo razvoj novih funkcionalnosti.
Vse te pomene damo vejam ljudje, ki sodelujemo v nekem Git repozitoriju.
Za Git so vse veje in značke zgolj preprosti kazalci na določen vnos.
@ -474,7 +485,7 @@ sta:
posnetek stanja datotek ter metapodatke (avtor, čas, sporočilo).
\item
\textbf{zgostitev vnosa} (angl. \textbf{commit hash}) je 40-mestna
heksadecimalna vrednost, izračunana s SHA-1, ki enolično identificira
heksadecimalna vrednost, izračunana s SHA1, ki enolično identificira
vnos na podlagi vsebine posnetka in metapodatkov.
\end{itemize}
@ -512,7 +523,6 @@ Omenimo še dva pojma, ki jih uporabljamo pri delu z Gitom:
katerim lahko izmenjujemo vsebino.
\end{itemize}
Opomba\\
Gitov podatkovni model omogoča, da je večina operacij v Gitu obrnljivih.
To pomeni, da lahko repozitorij povrnemo v prejšnje stanje. Običajne
operacije le dodajajo nove vnose in starih ne brišejo. Prav tako se v
@ -532,8 +542,6 @@ Ko smo opremljeni z razumevanjem podatkovnega modela Gita, razložimo kaj
pomenijo posamezne operacije, ki jih Git omogoča. Ukazov ne bom
prevajal, ampak jih bom navedel kot jih pozna program \texttt{git}.
\section{Checkout}
Ukaz
\begin{verbatim}
@ -541,13 +549,11 @@ git checkout referenca
\end{verbatim}
spremeni datoteke v delovni kopiji tako, da se ujemajo z vnosom, na
katerega kaže referenca. Poleg tega prestavi oznako \texttt{HEAD} na
katerega kaže \texttt{referenca}. Poleg tega prestavi oznako \texttt{HEAD} na
isti vnos. Če je referenca veja, jo nastavi, kot aktivno vejo. Če je
referenca oznaka ali zgostitev vnosa, priedmo v stanje brez aktivne veje
(angl. \emph{deteached HEAD}).
\section{Commit}
Ukaz
\begin{verbatim}
@ -559,8 +565,6 @@ index). V zgodovinskem grafu ustvari novo vozlišče, ki je povezano s
prejšnjim vnosom. Poleg tega prestavi aktivno vejo in oznako
\texttt{HEAD} na novo ustvarjeni vnos.
\section{Add}
Ukaz
\begin{verbatim}
@ -572,8 +576,6 @@ spreminja zgodovinskega grafa, pač pa doda novo vsebino in datotečna
drevesa, ki vsebujejo spremembe v shrambo objektov. Vsebina čakalnice bo
zabeležena v naslednjem vnosu.
\section{Pull}
Ukaz
\begin{verbatim}
@ -587,8 +589,6 @@ oddaljene, se lokalna veja enostavno prestavi, da kaže na isti vnos, kot
oddaljena veja. V nasprotnem primeru, mora uporabnik posredovati in
razrešiti morebitne konflikte.
\section{Push}
Ukaz
\begin{verbatim}
@ -599,8 +599,6 @@ potisne novo vsebino na oddaljeni repozitorij. Push deluje obratno kot
\texttt{pull}. Ukaz je uspešno izveden le, če je oddaljena veja prednica
lokalne veje in ni konflikotov.
\section{Fetch}
Ukaz
\begin{verbatim}
@ -612,19 +610,16 @@ Pri tem ne more priti do konfliktov, ker git preprosto doda nove objekte
v shrambo in obstoječih objektov nikakor ne spreminja. Oddaljenim vejam
in oznakam preprosto doda predpono z imenom oddaljenega repozitorija.
\section{Reset}
Ukaz
\begin{verbatim}
git reset referenca
\end{verbatim}
spremeni kam kaže trenutno izbrana veja. Ukaz ne spremeni zgodovinskega
spremeni kam kaže trenutno izbrana veja. Trenutno izbrano vejo prestavi na isti vnos, na
katerega kaže dana \texttt{referenca}. Ukaz ne spremeni zgodovinskega
drevesa, ampak le to, na kateri vnos kaže trenutno izbrana veja.
\section{Merge}
Ukaz
\begin{verbatim}
@ -636,8 +631,6 @@ referenco). Nov vnos ima dva starša: vnos na katerega kaže trenutna veja
in vnos, na katerega kaže referenca. Če pride do konfliktov, jih mora
uporabnik sam razrešiti, preden se ustvari nov vnos.
\section{Rebase}
Ukaz
\begin{verbatim}
@ -651,16 +644,16 @@ nove vnose in prestavljajo reference. Zato je večina ukazov v Gitu
varna, v smislu, da jih lahko kasneje prekličemo in pridemo nazaj na
prejšnje stanje. Ukaz \texttt{rebase} pa spremeni zgodovino in ga ne
moremo preklicati, saj trenutne vnose nadomesti z novimi in stare vnose
pobriše\footnote{Obstaja enostaven način, da tudi \texttt{rebase} lahko
prekličemo. Na zadnji vnos, ki ga želimo prestaviti preprosto
postavimo novo referenco(vejo ali oznako). To povzroči, da se stari
vnosi ne pobrišejo tudi, ko se izvede ukaz \texttt{rebase}.}.
pobriše\footnote{Obstaja enostaven način, kako \texttt{rebase} izvedemo tako, da ga lahko
kasneje prekličemo. Na vnos, ki ga želimo prestaviti z \texttt{rebase}, preprosto
postavimo novo vejo ali oznako. To povzroči, da se stari
vnosi ne pobrišejo, ko se izvede ukaz \texttt{rebase}.}.
\section{Zaključek}
Spoznali smo, kako deluje Git in s katere matematičnime pojme uporablja
za model. Opis dela z Gitom presega namen tega dokumenta, zato vas raje
usmerim na uradno dokumentacijo:
Spoznali smo, kako deluje Git in s katerimi matematičnimi pojmi lahko opišemo njegov podatkovni model.
Upam, da boste s tem znanjem bolj samozavestno uporabljali Git. Opis dela z Gitom presega namen tega
dokumenta, zato vas raje usmerim na uradno dokumentacijo:
{\url{https://git-scm.com/cheat-sheet}}
@ -669,7 +662,7 @@ Pri pisanju tega članka sem sevada uporabljal Git. V
repozitoriju} \cite{vuk_git-intro_nodate} si lahko ogledate celotno
zgodovino nastajanja tega članka.
Pri pripravi dokumenta sem uporabil Gemini 3. Vse odgovore sem preveril
Pri pripravi dokumenta sem uporabil Gemini 3, a sem vse odgovore njegove odgovore skrbno preveril
in uredil po svoje.
\bibliographystyle{plainurl}