672 lines
26 KiB
TeX
672 lines
26 KiB
TeX
\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}
|