Dodaj slike v tex verzijo
This commit is contained in:
parent
eb034aff97
commit
65af0b39b0
10 changed files with 724 additions and 4 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -1,3 +1,11 @@
|
|||
*.aux
|
||||
*.log
|
||||
*.out
|
||||
*.bbl
|
||||
*.blg
|
||||
*.pdf
|
||||
*.synctex.gz
|
||||
*.fls
|
||||
*.fdb_latexmk
|
||||
*.png
|
||||
*.svg
|
||||
2
diagrams/branch-move-img.typ
Normal file
2
diagrams/branch-move-img.typ
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#set page(width: auto, height: auto, margin: 5mm)
|
||||
#include "branch-move.typ"
|
||||
2
diagrams/branches-tags-img.typ
Normal file
2
diagrams/branches-tags-img.typ
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#set page(width: auto, height: auto, margin: 5mm)
|
||||
#include "branches-tags.typ"
|
||||
2
diagrams/commit-history-img.typ
Normal file
2
diagrams/commit-history-img.typ
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#set page(width: auto, height: auto, margin: 5mm)
|
||||
#include "commit-history.typ"
|
||||
2
diagrams/file-graph-img.typ
Normal file
2
diagrams/file-graph-img.typ
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#set page(width: auto, height: auto, margin: 5mm)
|
||||
#include "file-graph.typ"
|
||||
2
diagrams/head-index-img.typ
Normal file
2
diagrams/head-index-img.typ
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#set page(width: auto, height: auto, margin: 5mm)
|
||||
#include "head-index.typ"
|
||||
2
diagrams/object-storage-img.typ
Normal file
2
diagrams/object-storage-img.typ
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#set page(width: auto, height: auto, margin: 5mm)
|
||||
#include "object-storage.typ"
|
||||
672
git-obzornik.tex
Normal file
672
git-obzornik.tex
Normal file
|
|
@ -0,0 +1,672 @@
|
|||
\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{kolizije
|
||||
zgostitve}. V primeru kolizije 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 kolizije(
|
||||
glej \hyperref[sec_kolizije]{{[}sec\_kolizije{]}}). Verjetnost kolizije
|
||||
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{Kolizije zgostitev in rojstnodnevni
|
||||
paradoks}\label{sec_kolizije}
|
||||
|
||||
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 kolizije? 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
|
||||
kolizije, 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 kolizije 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 kolizije.
|
||||
|
||||
\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}
|
||||
28
pripravi_slike.ps1
Normal file
28
pripravi_slike.ps1
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
$diagramsDir = "diagrams"
|
||||
$outputDir = "slike"
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
if (!(Test-Path -Path $outputDir)) {
|
||||
New-Item -ItemType Directory -Path $outputDir | Out-Null
|
||||
}
|
||||
|
||||
# Get all wrapper .typ files in the diagrams directory (files ending in -img.typ)
|
||||
$files = Get-ChildItem -Path $diagramsDir -Filter "*-img.typ"
|
||||
|
||||
foreach ($file in $files) {
|
||||
# Remove -img from the output filename to match the desired naming convention
|
||||
$baseName = $file.BaseName -replace "-img$", ""
|
||||
$outputFile = Join-Path -Path $outputDir -ChildPath ($baseName + ".pdf")
|
||||
Write-Host "Compiling $($file.Name) to $outputFile..."
|
||||
|
||||
# Run typst compile with necessary flags
|
||||
# --root . : Allow access to files in the project root (like definicije.typ)
|
||||
# --ppi 300 : Increase resolution for better quality
|
||||
typst compile --root . --ppi 300 $file.FullName $outputFile
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Failed to compile $($file.Name)"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "All diagrams compiled."
|
||||
Loading…
Add table
Add a link
Reference in a new issue