-
1. Pierwsze kroki
- 1.1 Wprowadzenie do kontroli wersji
- 1.2 Krótka historia Git
- 1.3 Podstawy Git
- 1.4 Linia poleceÅ
- 1.5 Instalacja Git
- 1.6 WstÄpna konfiguracja Git
- 1.7 Uzyskiwanie pomocy
- 1.8 Podsumowanie
-
2. Podstawy Gita
- 2.1 Pierwsze repozytorium Gita
- 2.2 Rejestrowanie zmian w repozytorium
- 2.3 PodglÄ d historii rewizji
- 2.4 Cofanie zmian
- 2.5 Praca ze zdalnym repozytorium
- 2.6 Tagowanie
- 2.7 Aliasy
- 2.8 Podsumowanie
-
3. GaÅÄzie Gita
-
4. Git na serwerze
- 4.1 ProtokoÅy
- 4.2 Uruchomienie Git na serwerze
- 4.3 Generowanie Twojego publicznego klucza SSH
- 4.4 Konfigurowanie serwera
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Inne opcje hostowania przez podmioty zewnÄtrzne
- 4.10 Podsumowanie
-
5. Rozproszony Git
-
6. GitHub
-
7. NarzÄdzia Gita
- 7.1 Wskazywanie rewizji
- 7.2 Interaktywne używanie przechowali
- 7.3 Schowek i czyszczenie
- 7.4 Signing Your Work
- 7.5 Searching
- 7.6 Przepisywanie historii
- 7.7 Reset Demystified
- 7.8 Advanced Merging
- 7.9 Rerere
- 7.10 Debugowanie z Gitem
- 7.11 ModuÅy zależne
- 7.12 Bundling
- 7.13 Replace
- 7.14 Credential Storage
- 7.15 Podsumowanie
-
8. Dostosowywanie Gita
- 8.1 Konfiguracja Gita
- 8.2 Git Attributes
- 8.3 Git Hooks
- 8.4 An Example Git-Enforced Policy
- 8.5 Summary
-
9. Git i inne systemy
- 9.1 Git jako klient
- 9.2 Migracja do Gita
- 9.3 Podsumowanie
-
10. Mechanizmy wewnÄtrzne w Git
- 10.1 Komendy typu plumbing i porcelain
- 10.2 Obiekty Gita
- 10.3 Referencje w Git
- 10.4 Spakowane pliki (packfiles)
- 10.5 Refspec
- 10.6 ProtokoÅy transferu
- 10.7 Konserwacja i odzyskiwanie danych
- 10.8 Environment Variables
- 10.9 Podsumowanie
-
A1. Appendix A: Git in Other Environments
- A1.1 Graphical Interfaces
- A1.2 Git in Visual Studio
- A1.3 Git in Eclipse
- A1.4 Git in Bash
- A1.5 Git in Zsh
- A1.6 Git in Powershell
- A1.7 Summary
-
A2. Appendix B: Embedding Git in your Applications
- A2.1 Command-line Git
- A2.2 Libgit2
- A2.3 JGit
-
A3. Appendix C: Git Commands
- A3.1 Setup and Config
- A3.2 Getting and Creating Projects
- A3.3 Basic Snapshotting
- A3.4 Branching and Merging
- A3.5 Sharing and Updating Projects
- A3.6 Inspection and Comparison
- A3.7 Debugging
- A3.8 Patching
- A3.9 Email
- A3.10 External Systems
- A3.11 Administration
- A3.12 Plumbing Commands
3.6 GaÅÄzie Gita - Zmiana bazy
Zmiana bazy
W Git istniejÄ
dwa podstawowe sposoby integrowania zmian z jednej gaÅÄzi do drugiej: scalanie (polecenie merge
) oraz zmiana bazy (polecenie rebase
).
W tym rozdziale dowiesz siÄ, czym jest zmiana bazy, jak jÄ
przeprowadziÄ, dlaczego jest to Åwietne narzÄdzie i w jakich przypadkach lepiej siÄ powstrzymaÄ od jego wykorzystania.
Typowa zmiana bazy
JeÅli cofniesz siÄ do poprzedniego przykÅadu z sekcji Podstawy scalania, zobaczysz, że rozszczepiÅeÅ swojÄ pracÄ i wykonywaÅeÅ zmiany w dwóch różnych gaÅÄziach.

Najprostszym sposobem, aby zintegrowaÄ gaÅÄzie - jak już napisaliÅmy - jest polecenie merge
. Przeprowadza ono trójstronne scalanie pomiÄdzy dwoma ostatnimi migawkami gaÅÄzi (C3
i C4
) oraz ich ostatnim wspólnym przodkiem (C2
), tworzÄ
c nowÄ
migawkÄ (oraz rewizjÄ).

Jednakże istnieje inny sposób: możesz stworzyÄ ÅatkÄ ze zmianami wprowadzonymi w C4
i zaaplikowaÄ jÄ
na rewizjÄ C3
. W Gicie nazywa siÄ to zmianÄ
bazy (ang. rebase). DziÄki poleceniu rebase
możesz wziÄ
Ä wszystkie zmiany, które zostaÅy zatwierdzone w jednej gaÅÄzi i zaaplikowaÄ je w innej.
W tym wypadku, mógÅbyÅ uruchomiÄ nastÄpujÄ ce polecenie:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Polecenie to dziaÅa przesuwajÄ c siÄ do ostatniego wspólnego przodka obu gaÅÄzi (tej w której siÄ znajdujesz oraz tej do której robisz zmianÄ bazy), pobierajÄ c różnice opisujÄ ce kolejne zmiany (ang. diffs) wprowadzane przez kolejne rewizje w gaÅÄzi w której siÄ znajdujesz, zapisujÄ c je w tymczasowych plikach, nastÄpnie resetuje bieÅ¼Ä cÄ gaÅÄ Åŗ do tej samej rewizji do której wykonujesz operacjÄ zmiany bazy, po czym aplikuje po kolei zapisane zmiany.

C4
do C3
.W tym momencie możesz wróciÄ do gaÅÄzi master
i scaliÄ zmiany wykonujÄ
c proste przesuniÄcie wskaÅŗnika (co przesunie wskaÅŗnik master na koniec).
$ git checkout master
$ git merge experiment

Teraz migawka wskazywana przez C4'
jest dokÅadnie taka sama jak ta, na którÄ
wskazuje C5
w przykÅadzie ze scalaniem.
Nie ma różnicy w produkcie koÅcowym integracji. Zmiana bazy tworzy jednak czystszÄ
historiÄ.
JeÅli przejrzysz historiÄ gaÅÄzi po operacji rebase
, wyglÄ
da ona na liniowÄ
: wyglÄ
da jakby caÅa praca byÅa wykonywana stopniowo, nawet jeÅli oryginalnie odbywaÅa siÄ równolegle.
Warto korzystaÄ z tej funkcji, by mieÄ pewnoÅÄ, że rewizje zaaplikujÄ
siÄ w bezproblemowy sposób do zdalnej gaÅÄzi ā byÄ może w projekcie w którym próbujesz siÄ udzielaÄ, a którym nie zarzÄ
dzasz.
W takim wypadku bÄdziesz wykonywaÅ swojÄ
pracÄ we wÅasnej gaÅÄzi, a nastÄpnie zmieniaÅ jej bazÄ na origin/master
, jak tylko bÄdziesz gotowy do przesÅania wÅasnych poprawek do gÅównego projektu.
W ten sposób osoba utrzymujÄ
ca projekt nie bÄdzie musiaÅa dodatkowo wykonywaÄ integracji ā jedynie prostolinijne scalenie lub czyste zastosowanie zmian.
Zauważ, że migawka wskazywana przez wynikowÄ rewizjÄ bez wzglÄdu na to, czy jest to ostatnia rewizja po zmianie bazy lub ostatnia rewizja scalajÄ ca po operacji scalania, to taka sama migawka ā różnica istnieje jedynie w historii. Zmiana bazy nanosi zmiany z jednej linii pracy do innej w kolejnoÅci, w jakiej byÅy one wprowadzane, w odróżnieniu od scalania, które bierze dwie koÅcówki i integruje je ze sobÄ .
Ciekawsze operacje zmiany bazy
Poleceniem rebase
możesz także zastosowaÄ zmiany na innej gaÅÄzi niż ta, której zmieniasz bazÄ
Dla przykÅadu ā weÅŗ historiÄ takÄ
jak w przykÅadzie Historia z gaÅÄziÄ
tematycznÄ
utworzonÄ
na podstawie innej gaÅÄzi tematycznej..
UtworzyÅeÅ gaÅÄ
Åŗ tematycznÄ
(server
), żeby dodaÄ nowe funkcje do kodu serwerowego, po czym utworzyÅeÅ rewizjÄ.
NastÄpnie utworzyÅeÅ gaÅÄ
Åŗ, żeby wykonaÄ zmiany w kliencie (client
) i kilkukrotnie zatwierdziÅeÅ zmiany.
Ostatecznie wróciÅeÅ do gaÅÄzi server
i wykonaÅeÅ kilka kolejnych rewizji.

ZaÅóżmy, że zdecydowaÅeÅ siÄ scaliÄ zmiany w kliencie do kodu gÅównego, ale chcesz siÄ jeszcze wstrzymaÄ ze zmianami po stronie serwera, dopóki nie zostanÄ
one dokÅadniej przetestowane.
Możesz wziÄ
Ä zmiany w kodzie klienta, których nie ma w kodzie serwera (C8
i C9
) i zastosowaÄ je na gaÅÄzi gÅównej używajÄ
c opcji --onto
polecenia git rebase
:
$ git rebase --onto master server client
Oznacza to mniej wiÄcej "PrzeÅÄ
cz siÄ do gaÅÄzi klienta, okreÅl zmiany wprowadzone od wspólnego przodka gaÅÄzi client
i server
, a nastÄpnie nanieÅ te zmiany na gaÅÄ
Åŗ gÅównÄ
master
".
Jest to nieco skomplikowane, ale wynik jest caÅkiem niezÅy.

Teraz możesz zwyczajnie przesunÄ Ä wskaÅŗnik gaÅÄzi gÅównej do przodu (por. PrzesuniÄcie do przodu gaÅÄzi master w celu uwzglÄdnienia zmian z gaÅÄzi client):
$ git checkout master
$ git merge client

Powiedzmy, że zdecydujesz siÄ pobraÄ i scaliÄ zmiany z gaÅÄzi server
.
Możesz zmieniÄ bazÄ gaÅÄzi server
na wskazywanÄ
przez master
bez koniecznoÅci przeÅÄ
czania siÄ do gaÅÄzi server
używajÄ
c git rebase [gaÅÄ
Åŗ bazowa] [gaÅÄ
Åŗ tematyczna]
ā w ten sposób zmiany z gaÅÄzi server
zostanÄ
zaaplikowane do gaÅÄzi bazowej master
:
$ git rebase master server
Polecenie odtwarza zmiany z gaÅÄzi server
na gaÅÄzi master
tak, jak pokazuje to Zmiana bazy gaÅÄzi server
na koniec gaÅÄzi master
.

server
na koniec gaÅÄzi master
NastÄpnie możesz przesunÄ
Ä gaÅÄ
Åŗ bazowÄ
(master
):
$ git checkout master
$ git merge server
Możesz teraz usunÄ
Ä gaÅÄzie client
i server
, ponieważ caÅa praca jest już zintegrowana i wiÄcej ich nie potrzebujesz, pozostawiajÄ
c historiÄ w stanie takim, jaki obrazuje Ostateczna historia rewizji:
$ git branch -d client
$ git branch -d server

Zagrożenia operacji zmiany bazy
BÅogosÅawieÅstwo, jakie daje możliwoÅÄ zmiany bazy, ma swojÄ mrocznÄ stronÄ. Można jÄ podsumowaÄ jednym zdaniem:
Nie zmieniaj bazy rewizji, które wypchnÄ ÅeÅ już do publicznego repozytorium.
JeÅli bÄdziesz siÄ stosowaÅ do tej reguÅy, wszystko bÄdzie dobrze. W przeciwnym razie ludzie ciÄ znienawidzÄ , a rodzina i przyjaciele zacznÄ omijaÄ szerokim Åukiem.
StosujÄ
c operacjÄ zmiany bazy porzucasz istniejÄ
ce rewizje i tworzysz nowe, które sÄ
podobne, ale inne.
Wypychasz gdzieÅ swoje zmiany, inni je pobierajÄ
, scalajÄ
i pracujÄ
na nich, a nastÄpnie nadpisujesz te zmiany poleceniem git rebase
i wypychasz ponownie na serwer. Twoi wspóÅpracownicy bÄdÄ
musieli scaliÄ swojÄ
pracÄ raz jeszcze i zrobi siÄ baÅagan, kiedy spróbujesz pobraÄ i scaliÄ ich zmiany z powrotem z twoimi.
Spójrzmy na przykÅad obrazujÄ cy, jak operacja zmiany bazy może spowodowaÄ problemy. ZaÅóżmy, że sklonujesz repozytorium z centralnego serwera, a nastÄpnie wykonasz bazujÄ c na tym nowe zmiany. Twoja historia rewizji wyglÄ da nastÄpujÄ co:

Teraz ktoÅ inny wykonuje innÄ pracÄ, która obejmuje scalenie, i wypycha jÄ na centralny serwer. Pobierasz zmiany, scalasz nowÄ , zdalnÄ gaÅÄ Åŗ z wÅasnÄ pracÄ , w wyniku czego historia wyglÄ da mniej wiÄcej tak:

NastÄpnie osoba, która wypchnÄÅa scalone zmiany, rozmyÅliÅa siÄ i zdecydowaÅa zamiast scalenia zmieniÄ bazÄ swoich zmian; wykonuje git push --force
, żeby zastÄ
piÄ historiÄ na serwerze.
NastÄpnie ty pobierasz dane z serwera ÅciÄ
gajÄ
c nowe rewizje.

Teraz obaj znaleÅŗliÅcie siÄ w trudnej sytuacji.
JeÅli wykonasz git pull
, utworzysz rewizjÄ scalajÄ
cÄ
, która bÄdzie zawieraÅa obie linie historii, a twoje repozytorium bÄdzie wyglÄ
daÅo tak:

JeÅli uruchomisz git log
dla takiej historii, zobaczysz dwie rewizje majÄ
ce tego samego autora, datÄ oraz komentarz, co bÄdzie mylÄ
ce.
Co wiÄcej, jeÅli wypchniesz tÄ historiÄ z powrotem na serwer, raz jeszcze wprowadzisz wszystkie rewizje powstaÅe w wyniku operacji zmiany bazy na serwer centralny, co może dalej myliÄ i denerwowaÄ ludzi.
Można bezpiecznie przyjÄ
Ä, że drugi deweloper nie chce, aby C4
i C6
byÅy w historii; z tego wÅaÅnie powodu w pierwszej kolejnoÅci dokonaÅ zmiany bazy.
Rebase When You Rebase
If you do find yourself in a situation like this, Git has some further magic that might help you out. If someone on your team force pushes changes that overwrite work that youāve based work on, your challenge is to figure out what is yours and what theyāve rewritten.
It turns out that in addition to the commit SHA-1 checksum, Git also calculates a checksum that is based just on the patch introduced with the commit. This is called a āpatch-idā.
If you pull down work that was rewritten and rebase it on top of the new commits from your partner, Git can often successfully figure out what is uniquely yours and apply them back on top of the new branch.
For instance, in the previous scenario, if instead of doing a merge when weāre at KtoÅ wypycha rewizje po operacji zmiany bazy, porzucajÄ
c rewizje, na których ty oparÅeÅ swoje zmiany we run git rebase teamone/master
, Git will:
-
Determine what work is unique to our branch (C2, C3, C4, C6, C7)
-
Determine which are not merge commits (C2, C3, C4)
-
Determine which have not been rewritten into the target branch (just C2 and C3, since C4 is the same patch as C4')
-
Apply those commits to the top of
teamone/master
So instead of the result we see in Scalasz tÄ samÄ pracÄ raz jeszcze tworzÄ c nowÄ rewizjÄ scalajÄ cÄ , we would end up with something more like Rebase on top of force-pushed rebase work..

This only works if C4 and C4' that your partner made are almost exactly the same patch. Otherwise the rebase wonāt be able to tell that itās a duplicate and will add another C4-like patch (which will probably fail to apply cleanly, since the changes would already be at least somewhat there).
You can also simplify this by running a git pull --rebase
instead of a normal git pull
. Or you could do it manually with a git fetch
followed by a git rebase teamone/master
in this case.
If you are using git pull
and want to make --rebase
the default, you can set the pull.rebase
config value with something like git config --global pull.rebase true
.
If you treat rebasing as a way to clean up and work with commits before you push them, and if you only rebase commits that have never been available publicly, then youāll be fine. If you rebase commits that have already been pushed publicly, and people may have based work on those commits, then you may be in for some frustrating trouble, and the scorn of your teammates.
If you or a partner does find it necessary at some point, make sure everyone knows to run git pull --rebase
to try to make the pain after it happens a little bit simpler.
Rebase vs. Merge
Now that youāve seen rebasing and merging in action, you may be wondering which one is better. Before we can answer this, letās step back a bit and talk about what history means.
One point of view on this is that your repositoryās commit history is a record of what actually happened. Itās a historical document, valuable in its own right, and shouldnāt be tampered with. From this angle, changing the commit history is almost blasphemous; youāre lying about what actually transpired. So what if there was a messy series of merge commits? Thatās how it happened, and the repository should preserve that for posterity.
The opposing point of view is that the commit history is the story of how your project was made. You wouldnāt publish the first draft of a book, and the manual for how to maintain your software deserves careful editing. This is the camp that uses tools like rebase and filter-branch to tell the story in the way thatās best for future readers.
Now, to the question of whether merging or rebasing is better: hopefully youāll see that itās not that simple. Git is a powerful tool, and allows you to do many things to and with your history, but every team and every project is different. Now that you know how both of these things work, itās up to you to decide which one is best for your particular situation.
In general the way to get the best of both worlds is to rebase local changes youāve made but havenāt shared yet before you push them in order to clean up your story, but never rebase anything youāve pushed somewhere.