\documentclass[a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[slovene]{babel}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{longtable}
\usepackage{booktabs}
\usepackage{array}
\usepackage{calc}
\usepackage{hyperref}
\usepackage{geometry}
\geometry{a4paper, margin=2.5cm}
\usepackage{pmboxdraw}
\usepackage{newunicodechar}
\newunicodechar{├}{\textSFviii}
\newunicodechar{─}{\textSFx}
\newunicodechar{└}{\textSFii}
\newunicodechar{│}{\textSFxi}
\newtheorem{opomba}{Opomba}
\newcommand{\gitobject}[2]{\texttt{.git/objects/#1/#2}}
\title{Matematični pogled na Git}
\author{Martin Vuk}
\date{\today}
\begin{document}

\maketitle
\begin{abstract}
Git je program, ki omogoča vodenje zgodovine različic datotek v neki
mapi(direktoriju). V glavnem se uporablja za upravljanje z izvorno kodo
pri razvoju računalniških programov. Mnogi med nami pa ga uporabljajo
tudi pri pisanju besedil v \emph{LaTeX}-u. Poleg tega, da Git hrani
zgodovino sprememb, tudi omogoča da več ljudi hkrati sodeluje pri
urejanju istih datotek. Ogledali si bomo, kako Git deluje. Opisali bomo,
kako Git uporabi \emph{zgoščevalne funkcije}, \emph{Merklejeva drevesa}
in \emph{usmerjene aciklične grafe}, da shrani zgodovino različic in
omogoči hkratno urejanje vsebine. Matematični model, ki ga Git uporablja
je v resnici zelo preprost in njegovo razumevanje nas lahko reši
marsikatere zagate, ki nastane med njegovo uporabo.
\end{abstract}

\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}.

Git hrani vsebino mape z datotekami in celotno zgodovino različic
datotek iz preteklosti. Za vsako različico hrani Git zapis o avtorju,
datumu in opis sprememb, ki so nastale v primerjavi s predhodno
različico. Vse te imformacije dajejo podroben pregled nad zgodovino
sprememb.

Sisteme, ki omogočajo hranjenje preteklih različic datotek, imenujemo
\href{https://en.wikipedia.org/wiki/Version_control}{sistemi za nadzor
različic} (angl. version control system (VCS)) ali \emph{sistemi za
upravljanje z izvorno kodo} (angl. Source Code Management (SCM)). 
Poleg nadzora različic Git omogoča hkratno spreminjanje datotek več
uporabnikov na različnih računalnikih. Zato je Git
\href{https://en.wikipedia.org/wiki/Distributed_version_control}{distribuiran
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.
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
javno spletišče, ki je namenjeno skladiščenju Git repozitorijev.
\end{opomba}

V nadaljevanju bomo obravnavali nasledjne teme:

\begin{itemize}
\item
  \emph{Podatkovno skladišče:} Kako Git uporablja
  \href{https://sl.wikipedia.org/wiki/Zgo\%C5\%A1\%C4\%8Devalna_funkcija}{zgoščevalno
  funkcijo} (angl. hash function)? in
  \href{https://en.wikipedia.org/wiki/Merkle_tree}{Merklejeva drevesa}
  za hranjenje posnetkov vsebine mape.
\item
  \emph{Zgodovina sprememb:} Kako zgodovino predstavimo z
  \href{https://en.wikipedia.org/wiki/Directed_acyclic_graph}{usmerjenim
  acikličnim grafom}, v katerem so vozlišča različice, povezave pa
  povežejo različice z njihovimi neposrednimi predhodniki?
\item
  \emph{Reference}: Kako preproste reference (kazalci) na vsebino
  omogočajo bliskovito preklaplanje med različicami in preprečijo
  popolno zmešnjavo, ko več ljudi hkrati spreminja iste datoteke?
\end{itemize}

\section{Podatkovno skladišče}

Ko ustvarimo nov Git repozitorij, Git ustvari podmapo z imenom
\texttt{.git} z vsemi podatki, ki jih Git potrebuje. Git v mapi
\texttt{.git} hrani različne stvari:

\begin{itemize}
\item
  vsebino datotek, ki smo jih dodali v repozitorij,
\item
  drevesno strukturo korenske mape, ki jo hranimo v repozitoriju,
\item
  posnetke stanja v različnih trenutkih s podatki o avtoju, datumu in
  opisu sprememb,
\item
  kazalce na posamezne posnetke stanja.
\end{itemize}

Git repozitorij je vsaka mapa, ki vsebuje podmapo \texttt{.git} z zgoraj
navedenimi podatki. Podrobnosti o tem, kako Git hrani podatke, si lahko
preberete v knjigi Pro Git \cite{chacon_102_nodate}.

\section{Zgoščevalna funkcija}

Git ne shranjuje datotek z običajnimi imeni, ampak za ime uporabi 160
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
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
\(H\) ni injektivna, saj je množica nizov, bistveno večja od množice
zgostitev. To pomeni, da imata lahko dve različni datoteki enako
zgostitev. Če se to zgodi, rečemo, da pride do \emph{trka
zgostitve}. V primeru trka zgostitve bi Git shranil le eno datoteko,
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
je izjemno majhna, zato Git lahko predpostavi, da je niz \(b\) enolično
določen z njegovo zgostitvijo \(H(b)\).

Git uporablja \(160\) bitno zgoščevalno funkcijo \emph{SHA1}. Funkcija
SHA1 je posebna implementacija zgoščevalne funkcije, ki se je
uporabljala v kriptografiji\footnote{Leta 2017 so raziskovalci iz CWI
  Amsterdam in Google Research našli prvi praktični primer dveh
  različnih pdf datotek, ki imata isto SHA1
  zgostitev\cite{stevens_first_2017}. Opisan napad so poimenovali
  \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.}.

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
  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
  \texttt{8dd6d4bdaeff93016bd49474b54a911131759648} bo shranjena v
  \texttt{.git/objects/8d/d6d4bdaeff93016bd49474b54a911131759648}}.
Vsebina \(b\) je tako vedno dostopna pod imenom, ki je enako njeni
zgostitvi \(H(b)\). Tako dobimo
\href{https://en.wikipedia.org/wiki/Content-addressable_storage}{vsebinsko
naslovljivo shrambo objektov}, ki je ena od bistvenih značilnosti Gita.
Ta način shranjevanja omogoča, da lahko vedno preverimo, če ima
shranjenjena vsebina isto zgostitev, kot je njeno ime. Lahko tudi
shranimo več različic iste datoteke, saj ima vsaka različica drugačno
zgostitev. Zgostitev služi tudi kot kontrola, če je prišlo do kvaritve
podatkov, ki so shranjeni v Git repozitoriju.

\section{Datotečna drevesa}

V vsebinsko naslovljivo shrambo objektov lahko shranimo vsebino datotek
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.
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
in posledično zgostitev za drevo. Poleg posameznih datotek, lahko drevo
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}
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 naslednjo strukturo datotek in
podmap

\begin{figure}
\centering
\begin{verbatim}
├── bla.txt (vsebina: bla)
├── blabla.txt (vsebina: blabla)
└── podmapa
    └── bla.txt (vsebina: bla)
\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}
\item
  vsebino datoteke \texttt{bla.txt}
\begin{verbatim}
bla
\end{verbatim}
v \gitobject{bc}{c1382241e267cf790ca6b3afe9fde6dcf1072f}
\item
  vsebino datoteke \texttt{blabal.txt}
\begin{verbatim}
blabla
\end{verbatim}
  v \gitobject{2c}{e22b4dc77442103f095503f1205937c1b0fcfc}
\item
  seznam datotek v mapi \texttt{podmapa}

\begin{verbatim}
100644 blob bcc1382241e267cf790ca6b3afe9fde6dcf1072f    bla.txt
\end{verbatim}
  v \gitobject{ae}{247f2a35aadade5863aec2475cf13020304b06}
\item
  seznam datotek v korenski mapi
\begin{verbatim}
100644 blob 33476f4951afc28d5ac2dc0d42d82f17ac817de2   bla.txt
100644 blob 2ce22b4dc77442103f095503f1205937c1b0fcfc    blabla.txt
040000 tree ae247f2a35aadade5863aec2475cf13020304b06    podmapa
\end{verbatim}
  v \gitobject{47}{3e0bbfc9de64fdca00e611e5666788ddf664ca}
\end{itemize}

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}. 
Zato datotečno drevo v Gitu ni nujno predstavljeno kot drevo, ampak kot
\emph{usmerjen aciklični graf}.

\begin{figure}
\centering
\includegraphics[width=0.6\linewidth]{slike/file-graph.pdf}
\caption{Primer datotečnega grafa povezanega z zgostitvami. Zaradi
preglednosti bomo v slikah izpisali le prvih 6 znakov zgostitve.}
\end{figure}

Posledično lahko vsebino celotne mape opišemo z eno samo zgostitvijo. Če
spremenimo vsebino, ime ali lokacijo datoteke, bo sprememba vplivala na
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\\
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.

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}.

\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
datoteko in pride do izgubil podatkov. Git se zanaša na to, da je
verjetnost za to izjemno majhna. Kako bi ocenili to verjetnost?

Koliko datotek bi morali shraniti v Git, da bi z znatno verjetnostjo
prišlo do trka? Vprašanje je povezano z rojstnodnevnim problemom.
Kako velika naj bo skupina ljudi, da bo vsaj \(50\%\) verjetnost, da
imata dve osebi na isti dan rojstni dan? Velikost skupine je
presenetljivo majhna(23), zato rojstnodnevnei problem imenujemo tudi
rojstnodnevni paradoks. Vprašanje zastavimo matematično. Naključno
izberemo \(n < d\) števil iz množice \(\left\{ 1,2,\ldots,h \right\}\),
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).\]

Č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}{}
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}{}
Za vrednosti \(1 \ll n \ll h\) je \(1 - e^{- \frac{n^{2}}{2h}}\) tudi
dobra aproksimacija za \(p(n,h)\).

Da bi odgovorili kako odporna je zgoščevalna funkcija na morebitne
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)\):

\[\begin{array}{r}
 - n^{2} \approx \log(1 - p) \Rightarrow \\
n(p,h) \approx \sqrt{2h\log(\frac{1}{1 - p})} \approx \sqrt{2h}.
\end{array}\]

Funkcija \(\sqrt{\log(\frac{1}{1 - p})}\) zelo počasi narašča, ko se
\(p\) približuje \(1\), zato jo lahko zanemarimo. Če je zgoščevalna
funkcija \(160\) bitna, kot na primer SHA1, je
\(n \approx \sqrt{2^{160}} \approx 2^{80}\). Znatna verjetnost, da pride
do trka zgostitev, bi se pojavila, ko bi shranili \(2^{80}\)
različnih verzij datotek v Git. Raziskovalci, ki so razvili napad
\emph{SHAttered}, so se posebej potrudili in so potrebovali ``zgolj''
približno \(2^{63}\) primerov, da so prišli do trka.

\section{Zgodovinski graf sprememb}

V prejšnjem poglavju smo videli, kako Git hrani vsebino celotne mape in
kako je mogoče do vsebine dostopati če poznamo zgostitvijo korenskega
mape. Zgodovinsko drevo sprememb je preprosta razširitev omenjene
podatkovne strukture.

\section{Posnetki stanja}

Osnovna enota v Gitu je \textbf{Vnos} (angl. \textbf{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
shranjenih datotek in metapodatkov vnosa.

\begin{gitobject}{8dd6d4bdaeff93016bd49474b54a911131759648}
tree 65c47feec7465e80492620a48206793e078702e0
parent 16f2994757f1213935b8edb9ae7fee3a8e9ec98d
author MV <mv@example.com> 1765235698 +0100
committer MV <mv@example.com> 1765235698 +0100

Dodaj bla
\end{gitobject}

Vsak vnos je povezan s točno določenim posnetekom vsebine korenskega
datotečnega drevesa, ki ga identificira zgostitev. Poleg tega so
posamezni vnosi so povezani v \textbf{usmerjen acikličen graf (DAG)}, ki
predstavlja zgodovino sprememb. Vsak \textbf{vnos} je \textbf{vozlišče}
v grafu. Vsak vnos izhaja iz enega ali več starševskih vnosov. Izjema je
prvi vnos. \textbf{Povezave} v grafu povezujejo vnose z njihovimi
starši.

\begin{figure}
\centering
\includegraphics[width=0.6\linewidth]{slike/commit-history.pdf}
\caption{Vnosi v Gitu kot usmerjen graf. Vsak vnos(razen prvega) ima
povezavo na vnose iz katerih izhaja.}
\end{figure}

Git hrani zgodovino sprememb v \emph{vsebinsko naslovljivi shrambi
objektov}, ki hrani tri vrste objektov:

\begin{itemize}
\item
  \texttt{blob}: vsebina datotek,
\item
  \texttt{tree}: vsebina mape,
\item
  \texttt{commit}: posnetek vsebine v določenem trenutku.
\end{itemize}

Objekti so poevazni v \emph{usmerjen aciklični graf}. Podgraf na vnosih
določa zgodovino sprememb. Naslovi objektov so \emph{zgostitve} vsebine
objekta, zato je zagotovljena verodostojnost shranjenih podatkov.

\begin{figure}
\centering
\includegraphics[width=0.6\linewidth]{slike/object-storage.pdf}
\caption{Vsebinsko naslovljiva shramba objektov v Gitu. Naslovi so
zgostitve vsebine. Shramba vsebuje dva vnosa. V prvem vnosu smo dodali
dve datoteki \protect\texttt{bla.txt} in \protect\texttt{blabla.txt}, v
drugem vnosu pa smo spremenili le vsebino datotoeke
\protect\texttt{bla.txt}.}
\end{figure}

\section{Kazalci: veje in značke}

Poleg objektov kot so \emph{vnosi}, \emph{posnetki map} in
\emph{posnetki datotek} pozna git še reference. Reference so kazalci z
določenim imenom na posamezen vnos.

\begin{figure}
\centering
\includegraphics[width=0.6\linewidth]{slike/branches-tags.pdf}
\caption{Veja (angl. branch) ali značka(angl. tag) je preprost kazalec
na posamezen vnos(angl. commit).}
\end{figure}

Referenc git ne hrani v skladišču objektov, temveč posebej v mapi
\texttt{.git/refs}. Reference vezane na posamezen repozitorij in se
lahko razlikujejo med različnimi kloni določenega repozitorija.

\textbf{Veja} (angl. \textbf{branch}) je posebne vrste referenca, ki se
premika, ko dodajamo nove vnose. Vsakič ko ustvarimo nov vnos, se
trenutno aktivna veja premakne na novo ustvarjeni vnos.

\textbf{Značka} (angl. \textbf{tag}) je referenca, ki je statična in se
ne premika več, ko jo enkrat ustvarimo.

\begin{figure}
\centering
\includegraphics[width=0.6\linewidth]{slike/branch-move.pdf}
\caption{Ko ustvarimo nov vnos, se aktivna veja \protect\texttt{main}
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.

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.

\textbf{HEAD} je posebna referenca, ki kaže na trenutno aktiven vnos.
Vnos, na katerega kaže \emph{HEAD} bo starševski vnos naslednjeg vnosa,
ki ga bomo dodali.

\begin{figure}
\centering
\includegraphics[width=0.6\linewidth]{slike/head-index.pdf}
\caption{\textbf{HEAD} je referenca na trenutno aktiven vnos.
\emph{Index} vsebuje spremembe, ki bodo zabeležene v naslednjem vnosu.}
\end{figure}

\section{Povzetek}

Povzemimo sedaj, kaj smo spoznali o podatkovnem modelu Gita. V vsebinsko
naslovljivi shrambi hrani Git posnetke stanja celotne mape, ki ga vodimo
v repozitoriju skupaj z metapodatki o spremembah. Najpomembnejša pojma
sta:

\begin{itemize}
\item
  \textbf{Vnos} (angl. \textbf{commit}) je posnetek trenutnega stanja
  projekta, shranjen kot vozlišče v zgodovinskem grafu, ki vsebuje
  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
  vnos na podlagi vsebine posnetka in metapodatkov.
\end{itemize}

Izven shrambe objektov hrani Git še reference na posamezne vnose.
Poznamo dve vrsti referenc:

\begin{itemize}
\item
  \textbf{Veja} (angl. \textbf{branch}) je premična reference, ki kaže
  na določen vnos v zgodovini in se samodejno premakne naprej, ko
  dodajamo nove vnose. Veje omogočajo vzporedne razvojne linije ki so
  med sabo neodvisne.
\item
  \textbf{Oznaka} (angl. \textbf{tag}) je statična referenca, ki trajno
  kaže na določen vnos. Za razliko od veje se oznaka, nikoli ne premika
  samodejno, zato se uporablja predvsem za označevanje pomembnih točk v
  zgodovini, kot so izdaje ali stabilne verzije.
\item
  \textbf{HEAD} je posebna oznaka, ki kaže na trenutno aktiven vnos v
  delovni kopiji.
\end{itemize}

Omenimo še dva pojma, ki jih uporabljamo pri delu z Gitom:

\begin{itemize}
\item
  \textbf{Delovna kopija} (angl. \textbf{workout copy}) je mapa v kateri
  urejamo datoteke, ki jih nato vnesemo v Git. V delovni kopiji imajo na
  začetku datoteke isto vsebino kot je vsebina trenutno aktivnega vnosa
  (\texttt{HEAD}). Spremembe, ki jih naaredimo na delovni kopiji lahko
  zabeležimo v nov vnos.
\item
  \textbf{Oddaljen repozitorij} (angl. \textbf{remote}) je povezava(url)
  na isti repozitorij na drugem računalniku(ponavadi strežniku), s
  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
zgodovinsko drevo le dodaja nove povezave in starih se ne briše. Zato
daje delo z Gitom uporabniku samozavest, da brez strahu spreminja
vsebino, saj se lahko vedno vrne v času nazaj, kot da bi imel časovni
stroj.

Nekatere operacije pa tudi brišejo vnose (npr. \texttt{git\ rebase}).
Takim operacijam rečemo, da spreminjajo zgodovino. Uporabniki morajo
biti pri uporabi operacij, ki spreminjajo zgodovino posebej pazljivi, da
česa trajno ne zamočijo.

\section{Git ukazi kot operacije na grafu}

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}
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
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}
git commit -m "Sporočilo za vnos"
\end{verbatim}

ustvari nov vnos, ki kaže na stanje v čakalnici (angl. staging area ali
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}
git add bla.txt
\end{verbatim}

doda vsebino spremenjene datoteke \texttt{bla.txt} v čakalnico. Ukaz ne
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}
git pull
\end{verbatim}

pobere vsebino(objekte in reference) iz oddaljenega repozitorija in
uskladi lokalno vejo z oddaljeno. Shrambi objektov se preprosto doda
nove objekte, ki so v oddaljeni veji. Če je lokalna veja prednik
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}
git push
\end{verbatim}

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}
git fetch
\end{verbatim}

pobere novo vsebino (vnose, veje in oznake) iz oddaljenega repozitorija.
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
drevesa, ampak le to, na kateri vnos kaže trenutno izbrana veja.

\section{Merge}

Ukaz

\begin{verbatim}
git merge referenca
\end{verbatim}

ustvari nov vnos, ki združi dve ločeni veji v eno (trenutno izbrano in
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}
git rebase referenca
\end{verbatim}

prestavi vnose v trenutno izbrani veji tako, da so potomci vnosa, na
katerega kaže referenca. Med ukazi, ki smo jih spoznali, je ta ukaz
edini, ki lahko povzroči izgubo podatkov. Običajno ukazi le dodajajo
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}.}.

\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:

{\url{https://git-scm.com/cheat-sheet}}

Pri pisanju tega članka sem sevada uporabljal Git. V
\href{https://git.fri.uni-lj.si/martin.vuk/git-intro}{javno dostopnem
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
in uredil po svoje.

\bibliographystyle{plain}
\bibliography{reference}
\end{document}
