-
Notifications
You must be signed in to change notification settings - Fork 30
/
cesta-pitanja.tex
157 lines (96 loc) · 8.55 KB
/
cesta-pitanja.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
\tocChapter{Česta pitanja}
Jedno je razumjeti naredbe i terminologiju gita, a potpuno drugo je imati iskustvo u radu s gitom.
Da bi nekako došli do iskustva, trebamo imati osjećaj o tome koji su problemi koji se pojavljuju u radu i trebamo automatizirati postupak njihovog riješavanja.
U ovom poglavlju ćemo proći nekoliko takvih "situacija".
\tocSection{Jesmo li $push$ali svoje izmjene na udaljeni repozitorij?}
S klasičnim sustavima za verzioniranje, kod kojeg smo izmijenili može biti ili lokalno ne$commit$an ili $commit$an na centralnom repozitoriju.
Kao što sad već znamo, s gitom je stvar za nijansu složenija.
Naš kod može biti lokalno ne$commit$an, može biti $commit$an na našem lokalnom repozitoriju, a može biti i $push$an na udaljeni repozitorij.
Više puta mi se desilo da netko od kolega (tko tek uči git) pita "Kako to da moje izmjene nisu završile na produkciji\footnote{\dots{}ili produkcijskom $build$u.}, \textbf{iako sam ih $commit$ao}?".
Odgovor je jednostavan -- $commit$ao ih je lokalno, ali nije $push$ao na naš glavni repozitorij.
Problem kojeg on ima je u tome što nije nigdje jasno vidljivo jesu li izmjene iz njegove \verb+master+ grane $push$ane na udaljeni repozitorij.
Jednostavan način da to provjerimo je da provjerimo odnos između \verb+master+ i \verb+origin/master+.
Za svaki slučaj, prvo ćemo osvježiti stanje udaljenog repozitorija s:
\gitoutput{git fetch}
\dots{}i sad idemo \textbf{vizualno} proučiti odnos između naše dvije grane:
\gitoutput{gitk master origin/master}
Sad pogledajte na grafu je li:
\begin{itemize}
\item \verb+master+ \textbf{ispred} \verb+origin/master+, u tom slučaju vi imate više $commit$ova od udaljenog repozitorija i možete ih $push$ati,
\item \verb+master+ \textbf{iza} \verb+origin/master+, u tom slučaju vi imate manje $commit$ova od udaljenog repozitorija i trebate ih pokupiti s udaljenog repozitorija ($pull$ ili $rebase$),
\item \verb+master+ i \verb+origin/master+ se nalaze na dvije grane koje su međusobno divergirale (u tom slučaju vi imate izmjene koje niste još $push$ali, ali trebate prije toga napraviti $pull$).
\item \verb+master+ i \verb+origin/master+ pokazuju na isti čvor, tada je lokalno stanje potpuno isto ko i stanje udaljenog repozitorija.
\end{itemize}
Za primjere grafova, pogledajte poglavlje o prikazu grafova.
\tocSection{$Commit$ali smo u krivu granu}
Na primjer, slučajno smo commitali u \verb+master+, a trebali smo u \verb+unicode-fix+.
Pretpostavimo da su zadnja dva $commit$a iz \verb+master+ ona koja želimo prebaciti u ovu drugu granu.
Rješenje je jednostavno, prvo ćemo se prebaciti u tu drugu granu:
\gitoutput{git checkout unicode-fix}
Zatim ćemo preuzeti jedan po jedan ta dva $commit$a u trenutnu granu:
\gitoutput{git cherry-pick master\textasciitilde{}1\\
git cherry-pick master}
Podsjetimo se da je \verb+master+ naziv grane, ali i pokazivač na njegov zadnji $commit$, tako da \verb+git cherry-pick master+ preuzima samo taj zadnji $commit$.
Commit \verb+master~1+ se odnosi na pretposljednji u toj grani.
Umjesto \verb+master+ i \verb+master~1+ smo mogli koristiti i SHA1 identifikatore $commit$ova, koje možemo dobiti s \verb+git log master+.
Sad, kad smo te $commit$ove prebacili (i) u željenu granu, trebamo ih maknuti iz one u kojoj su neželjeni.
Idemo se prvo prešaltati na nju:
\gitoutput{git checkout master}
I, idemo ih obrisati:
\gitoutput{git reset --hard master\textasciitilde{}2}
\dots{}što tu granu resetira na stanje u \verb+master~2+ (a to je pred-predzadnji $commit$).
\tocSection{$Commit$ali smo u granu X, ali te commitove želimo prebaciti u novu granu}
$Commit$ali smo u \verb+master+, ali u jednom trenutku smo zaključili da te izmjene ne želimo tu.
Želimo stvoriti novu granu koja će nam sačuvati te $commit$ove, a \verb+master+ resetirati na isto stanje kao i u udaljenom repozitoriju.
Pa, idemo redom, s\dots
\gitoutput{git branch nova-grana}
\dots{}ćemo kreirati novu granu iz \verb+master+.
Te dvije grane su trenutno potpuno iste, dakle, upravo smo riješili prvi dio zadatka -- sačuvali smo $commit$ove iz \verb+master+ u drugoj grani.
S obzirom da nam stanje u \verb+master+ treba biti isto kao u \verb+origin/master+, prvo ćemo se potruditi da lokalno imamo ažurno stanje udaljenog repozitorija:
\gitoutput{git fetch}
\dots{}i sad idemo izjednačiti \verb+master+ i \verb+origin/master+:
\gitoutput{git reset --hard origin/master}
\tocSection{Imamo ne$commitane$ izmjene i git nam ne da prebacivanje na drugu granu}
Imamo li ne$commit$anih izmjena, git ponekad neće dopustiti prebacivanje ($checkout$anje) s grane na granu.
Ukoliko te izmjene predstavljaju neku logičnu cjelinu -- onda ćemo ih jednostavno $commit$ati i to nije problem.
No, ako se nalazimo na pola posla i to ne želimo\dots
To se može zaobići na dva načina.
Jedan je da koristimo naredbu \verb+git stash+\footnote{Detaljnije u poglavlju o manje korištenim naredbama.}, a drugi je da ipak -- $commit$ate.
Problem s ovim drugim pristupom je što ćemo imati djelomični $commit$ s poluzavršenim kodom.
Međutim, kad se naknadno vratimo na ovu granu (nakon što obavimo posao na nekoj drugoj) -- možemo posao završiti i commitati ga s:
\gitoutput{git commit --amend -m "Novi komentar...}
I, osvježili smo prethodni polovični $commit$.
Ukoliko to činimo, treba samo pripaziti da svoj "privremeni" $commit$ ne $push$ate na udaljeni repozitorij dok nije gotov\footnote{To općenito vrijedi za $commit$ove, nemojte koristiti "commit --amend" ukoliko ste već $push$ali na udaljeni repozitorij.}.
\tocSection{Zadnjih $n$ $commit$ova treba "stisnuti" u jedan $commit$}
S \verb+git log+ ili \verb+gitk+ nađimo SHA1 identifikator zadnjeg $commit$a kojeg \textbf{želimo ostaviti netaknutog} (tj. sve $commitove$ \textbf{nakon} njega želimo "stisnuti" u jedan $commit$).
Neka je to, na primjer, \verb+15694d32935f07cc66dbc98fdd7b3b248d885492+.
Treba pripaziti se da lokalno u repozitoriju nemamo nikakvih ne$commit$anih izmjena i da se nalazimo u pravoj grani, a onda:
\gitoutput{git reset --soft 15694d32935f07cc66dbc98fdd7b3b248d885492}
Git će nas sad vratiti u povijest, ali datoteke će ostaviti u istom stanju u kakvom su bile snimljene.
Sad ih možemo $commit$ati iznova i dobiti ćemo ono što smo tražili.
\tocSection{$Push$ali smo u remote repozitorij izmjenu koju nismo htjeli}
Kad smo \textbf{lokalno} napravili izmjenu koju nismo htjeli -- možemo koristiti \verb+git reset --hard ...+.
Međutim, ako smo našu izmjenu $push$ali, onda je najbolje napraviti:
\gitoutput{git revert <nas\_commit>}
Podsjetimo se, \verb+revert+ stvara \textbf{novi} $commit$ koji \textbf{miče} izmjene koje smo prethodno napravili.
Nakon toga $push$ajmo još jednom na udaljeni repozitorij, i to je to.
Alternativa je da napravimo:
\gitoutput{git reset --hard <commit>\\
git push -f origin <grana>}
\dots{}međutim, to može biti problem ako je naše neželjene izmjene na udaljenom repozitoriju netko već preuzeo ($fetch$ao) kod sebe lokalno.
\tocSection{$Merge$ali smo, a nismo htjeli}
Ukoliko svoje izmjene niste $push$ali na udaljeni repozitorij, jednostavno nađite $commit$ prije vašeg $merge$a i napravite $reset$ na to mjesto.
Najčešće će biti dovoljno:
\gitoutputcommand{git reset --hard HEAD\^{}1}
Naravno, provjerite za svaki slučaj je li \verb+HEAD^1+ upravo onaj $commit$ na kojeg želite vratiti vaš $branch$.
\tocSection{Ne znamo gdje smo $commit$ali}
Napravili smo $commit$, prebacili se na neku drugu granu i malo se izgubili tako da sad više ne znamo gdje su te izmjene završile.
Jednu stvar koju možemo napraviti je pokrenuti \verb+gitk --all+ i pogledati možemo li naći taj $commit$ u nekoj od postojećih grana.
Ukoliko ga nađemo -- možemo ga $cherry-pick$ati u našu granu i revertati u grani u kojoj je greškom završio.
No, ima jedan poseban slučaj na kojeg treba pripaziti.
Ukoliko smo se $checkout$ali na neku $remote$ granu (npr. \verb+origin/master+) git nam neće javiti grešku ukoliko tamo i $commit$amo iako takve grane nisu zamišljene da na njima radimo, nego samo da preuzimamo izmjene iz njih u naše lokalne grane.
Na sreću, novije verzije gita će vas upozoriti kad se s takve grane želite vratiti na neku postojeću lokalnu i odmah vam sugerira što da učinite.
Konkretno on vam predlaže da taj $commit$ sačuvate kao novu granu koju ćete napraviti iz trenutnog stanja:
\input{git_output/git_checkout_iz_origin_master}
No, ako ste zanemarili tu poruku (ili koristite IDE koji vam neće dati tu istu poruku) prisjetite se naredbe $reflog$.
S njome možete naći SHA1 "izgubljenog $commit$a" i napraviti \verb+cherry-pick+ u tekuću granu ili novi branch.