Git
Chapters ▾ 2nd Edition

5.3 Rozproszony Git - Utrzymywanie projektu

Utrzymywanie projektu

Oprócz wiedzy, jak efektywnie przyczynić się do rozwoju projektu, prawdopodobnie będziesz musiał wiedzieć, jak go utrzymać. Składa się na to akceptowanie i nakładanie łat wygenerowanych przez format-patch i wysłanych do Ciebie, lub łączenie zmian z zewnętrznych repozytoriów które dodałeś w projekcie. Nieważne czy prowadzisz zwykłe repozytorium, lub chcesz pomóc przy weryfikacji i integrowaniu łat, musisz wiedzieć w jaki sposób akceptować zmiany innych w taki sposób, który będzie przejrzysty dla innych i spójny w dłuższym okresie.

Praca z gałęziami tematycznymi

Jeżeli zamierzasz włączyć nowe zmiany, dobrym pomysłem jest stworzenie do tego nowej tymczasowej gałęzi – specjalnie przygotowanej do tego, aby przetestować te zmiany. W ten sposób najłatwiej dostosować pojedyncze zmiany, lub zostawić je jeżeli nie działają, do czasu aż będziesz mógł się tym ponownie zająć. Jeżeli stworzysz nową gałąź bazując na głównym motywie wprowadzanych zmian które chcesz przetestować, np. ruby_client lub coś podobnego, możesz łatwo zapamiętać czy musiałeś ją zostawić aby później do niej wrócić. Opiekun projektu Git często tworzy oddzielną przestrzeń nazw dla nich – np. sc/ruby_client, gdzie sc jest skrótem od osoby która udostępniła zmianę. Jak pamiętasz, możesz stworzyć nową gałąź bazując na swojej gałęzi master, w taki sposób:

$ git branch sc/ruby_client master

Ewentualnie, jeżeli chcesz się od razu na nią przełączyć, możesz użyć komendy checkout -b:

$ git checkout -b sc/ruby_client master

Teraz jesteś gotowy do tego, aby dodać do niej udostępnione zmiany i zdecydować czy chcesz je włączyć do jednej ze swoich gałęzi.

Wprowadzanie poprawek z wiadomości e-mail

Jeżeli otrzymasz poprawkę poprzez wiadomość e-mail, którą musisz włączyć do swojego projektu, musisz wprowadzić ją do gałęzi tematycznej w celu przetestowania. Istnieją dwa sposoby aby włączyć takie zmiany: przy użyciu git apply lub git am.

Wprowadzanie poprawki za pomocą apply

Jeżeli otrzymałeś poprawkę od kogoś, kto wygenerował ją za pomocą komendy git diff lub uniksowej diff, możesz zaaplikować ją za pomocą komendy git apply. Zakładając, że zapisałeś plik w /tmp/patch-ruby-client.patch, możesz go nałożyć w taki sposób:

$ git apply /tmp/patch-ruby-client.patch

Ta komenda zmodyfikuje pliki znajdujące się w obecnym katalogu. Jest ona prawie identyczna do komendy patch -p1 w celu nałożenia poprawki, ale jest bardziej restrykcyjna pod względem akceptowanych zmian. Obsługuje również dodawanie plików, usuwanie, oraz zmiany nazw jeżeli zostały zapisane w formacie git diff, czego komenda patch nie zrobi. Wreszcie, git apply ma zasadę "zaakceptuj lub odrzuć wszystko", gdzie albo wszystko jest zaakceptowane albo nic, a patch może częściowo nałożyć zmiany zostawiając projekt z niespójnym stanem. Komenda git apply jest z zasady bardziej restrykcyjna niż patch. Nie stworzy za Ciebie commita – po uruchomieniu, musisz zatwierdzić wprowadzone zmiany ręcznie.

Możesz również użyć git apply aby zobaczyć, czy poprawka nałoży się czysto zanim ją zaaplikujesz – jeżeli uruchomisz git apply --check z poprawką:

$ git apply --check 0001-seeing-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply

Jeżeli nie zostanie wygenerowany żaden komunikat, to poprawka wdroży się poprawnie. Ta komenda również kończy działanie z niezerowym statusem w przypadku błędu, możesz więc jeśli chcesz, możesz użyć jej w skryptach.

Wprowadzanie poprawki za pomocą am

Jeżeli otrzymałeś poprawkę wygenerowaną przez użytkownika używającego Gita, który stworzył go za pomocą format-patch, Twoja praca będzie prostsza ponieważ poprawka zawiera już informacje o autorze oraz komentarz do zmiany. Jeżeli możesz, namawiaj swoich współpracowników aby używali format-patch zamiast diff do generowania dla Ciebie poprawek. Powinieneś móc użyć jedynie git apply dla takich poprawek.

Aby zaaplikować poprawkę wygenerowaną przez format-patch, użyj git am. Technicznie rzecz biorąc, git am został stworzony, aby odczytywać plik w formacie mbox, który jest prostym, tekstowym formatem zawierającym jedną lub więcej wiadomości e-mail w jednym pliku. Wygląda on podobnie do:

From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function

Limit log functionality to the first 20

To są pierwsze linie z wyniku komendy format-patch, którą zobaczyłeś w poprzedniej sekcji. Jest to również poprawny plik w formacie mbox. Jeżeli ktoś poprawnie przesłał do Ciebie poprawkę za pomocą git send-email, możesz ją zapisać w formacie mbox, następnie wskazać git am ten plik, a Git zacznie aplikować wszystkie znalezione poprawki. Jeżeli używasz klienta pocztowego, który potrafi zapisać kilka wiadomości e-mail w formacie mbox, możesz zapisać serię poprawek do pliku i użyć git am aby jest nałożyć wszystkie poprawki za jednym zamachem.

Również, jeżeli ktoś wgrał poprawkę wygenerowaną poprzez format-patch do systemu zgłoszeń błędów lub czegoś podobnego, możesz zapisać lokalnie ten plik i potem przekazać go do git am, tak aby zaaplikować go:

$ git am 0001-limit-log-function.patch
Applying: add limit to log function

Możesz zauważyć, że poprawka została zaaplikowana bez problemu i została automatycznie zatwierdzona. Informacje o autorze zostały pobrane z wiadomości e-mail z nagłówków From i Date, a treść komentarz została pobrana z tematu i treści (przed poprawką) e-maila. Na przykład, jeżeli ta poprawka została zaaplikowana z pliku mbox, który przed chwilą pokazaliśmy, to wygenerowany commit będzie wyglądał mniej więcej tak:

$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   add limit to log function

   Limit log functionality to the first 20

Linie zaczynające się od Commit pokazują osobę która zaaplikowała poprawkę oraz czas kiedy to zrobiła. Linie rozpoczynające się od Author pokazują osobę która stworzyła poprawkę wraz z dokładną datą.

Jednak możliwa jest również sytuacja, w której poprawka nie zostanie bez problemów nałożona. Być może Twoja gałąź zbyt mocno się zmieniła, w stosunku do gałęzi, na której poprawka została stworzona, albo zależna jest ona od innej poprawki, której jeszcze nie nałożyłeś. W takiej sytuacji komenda git am zakończy się błędem i zapyta co robić dalej:

$ git am 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

Ta komenda zaznacza pliku z którymi miała problemy, podobnie do konfliktów występujących podczas komend merge lub rebase. Rozwiązujesz takie sytuacja również analogicznie – zmień plik w celu rozwiązania konfliktu, dodaj do przechowalni nowe pliki i następnie uruchom git am --resolved aby kontynuować działanie do następnej poprawki:

$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: seeing if this helps the gem

Jeżeli chcesz aby Git spróbował w bardziej inteligentny sposób rozwiązać konflikty, dodaj opcję -3 do komendy, która daje Gitowi możliwość spróbowania trójstronnego łączenia. Opcja ta nie jest domyślnie włączona, ponieważ nie działa poprawnie w sytuacji gdy w twoim repozytorium nie ma commitu, na którym bazuje poprawka. Jeżeli go masz – jeżeli poprawka bazowała na publicznym commicie – to dodanie -3 zazwyczaj pozwala na dużo mądrzejsze zaaplikowanie konfliktującej poprawki:

$ git am -3 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.

W tym przypadku, ta poprawka została już zastosowana. Bez podanej opcji -3 wyglądało to na konflikt.

Jeżeli włączasz większą liczbę poprawek z pliku mbox, możesz użyć komendy am w trybie interaktywnym, który zatrzymuje się na każdej poprawce, którą znajdzie, i pyta czy chcesz ją zastosować:

$ git am -3 -i mbox
Commit Body is:
--------------------------
seeing if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all

Jest to całkiem dobre jeżeli masz zapisaną większą liczbę poprawek, ponieważ możesz najpierw zobaczyć poprawkę jeżeli nie pamiętasz do czego była, lub nie aplikować jej jeżeli już to zrobiłeś.

Kiedy wszystkie poprawki zostaną wgrane i commitnięte w Twojej gałęzi, możesz zastanowić się w jaki sposób i czy chcesz integrować je do jednej z głównych gałęzi.

Checking Out Remote Branches

Jeżeli zmiana przyszła od użytkownika Gita który ma skonfigurowane własne repozytorium, wgrał do niego już jakąś liczbę zmian i następnie wysłał do Ciebie adres URL repozytorium oraz nazwę zdalnej gałęzi zawierającej zmiany, możesz ją dodać jako zdalną i połączyć zmiany lokalnie.

Na przykład, jeżeli Jessica wysyła Ci wiadomość e-mail w której pisze, że ma nową funkcjonalność w gałęzi ruby-client w swoim repozytorium, możesz je przetestować dodając zdalne repozytorium i sprawdzając tą gałąź lokalnie:

$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client

Jeżeli napisze do Ciebie ponownie z nową gałęzią która zawiera kolejną funkcjonalność, możesz ją pobrać i sprawdzić ponieważ masz już dodane zdalne repozytorium.

Jest to bardzo pomocne w sytuacji, w której współpracujesz z jakąś osobą na stałe. Jeżeli ktoś ma tylko pojedyncze łatki które udostępnia raz na jakiś czas, to akceptowanie ich poprzez e-mail może być szybsze, niż zmuszanie wszystkich do tego aby mieli własny serwer, jak również dodawanie i usuwanie zdalnych repozytoriów aby otrzymać jedną lub dwie łatki. Jednakże, skrypty oraz usługi udostępniane mogą uczynić to prostszym – zależy od tego w taki sposób pracujesz, oraz jak pracują Twoi współpracownicy.

Kolejną zaletą takiego podejścia jest to, że otrzymujesz również całą historię zmian. Chociaż mogą zdarzyć się uzasadnione problemy ze scalaniem zmian, wiesz na którym etapie historii ich praca bazowała; prawidłowe trójstronne scalenie jest domyślne, nie musisz więc podawać -3 i mieć nadzieję że łatka została wygenerowana z publicznie dostępnego commitu/zmiany.

Jeżeli nie współpracujesz z jakąś osobą na stałe, ale mimo wszystko chcesz pobrać od niej zmiany w ten sposób, możesz podać URL repozytorium do komendy git pull. Wykona ona jednokrotne zaciągnięcie zmian i nie zapisze URL repozytorium jako zdalnego:

$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
 * branch            HEAD       -> FETCH_HEAD
Merge made by recursive.

Ustalenie co zostało wprowadzone

Teraz posiadać gałąź tematyczną która zawiera otrzymane zmiany. W tym momencie możesz zdecydować co chcesz z nimi zrobić. Ta sekcja przywołuje kilka komend, tak abyś mógł zobaczyć w jaki sposób ich użyć, aby przejrzeć dokładnie co będziesz włączał do głównej gałęzi.

Często pomocne jest przejrzenie wszystkich zmian które są w tej gałęzi, ale nie ma ich w gałęzi master. Możesz wyłączyć zmiany z gałęzi master poprzez dodanie opcji --not przed jej nazwą. Działa to tak samo co format master..contrib, którego używaliśmy wcześniej. Na przykład, jeżeli Twój współpracownik prześle ci dwie poprawki, a Ty stworzysz nową gałąź contrib i włączysz te łatki tam, możesz uruchomić:

$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Oct 24 09:53:59 2008 -0700

    seeing if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Oct 22 19:38:36 2008 -0700

    updated the gemspec to hopefully work better

Aby zobaczyć jakie zmiany każdy z commitów wniósł, zapamiętaj że możesz dodać opcję -p do git log, a otrzymasz również w wyniku różnice w kodzie.

Aby zobaczyć różnice tego co się stanie, jeżeli chciałbyś połączyć tą gałąź z inną, będziesz musiał użyć całkiem ciekawych sztuczek aby otrzymać poprawne wyniki. Możesz pomyśleć, aby uruchomić:

$ git diff master

Ta komenda pokaże ci różnice w kodzie, ale może to być mylące. Jeżeli Twoja gałąź master zmieniła się od czasu stworzenia gałęzi tematycznej, otrzymasz dziwne wyniki. Tak dzieje się dlatego, ponieważ Git porównuje bezpośrednio ostatnią migawkę z gałęzi tematycznej, z ostatnią migawkę w gałęzi master. Na przykład, jeżeli dodasz linię w pliku w gałęzi master, bezpośrednie porównanie pokaże, że gałąź tematyczna zamierza usunąć tą linię.

Jeżeli master jest bezpośrednim przodkiem Twojej gałęzi tematycznej, nie stanowi to problemu; jeżeli jednak obie linie się rozjechały, wynik diff pokaże dodawane wszystkie zmiany z gałęzi tematycznej, a usuwane wszystkie unikalne z master.

Wynik którego naprawdę oczekujesz, to ten, pokazujący zmiany będące w gałęzi tematycznej – zmiany które wprowadzisz jeżeli scalisz tą gałąź z master. Możesz to zrobić, poprzez porównanie ostatniego commitu z gałęzi tematycznej, z pierwszym wspólnym przodkiem z gałęzi master.

Technicznie rzecz ujmując, możesz to zrobić poprzez wskazanie wspólnego przodka i uruchomienie na nim diff:

$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

Jednak to nie jest wygodne rozwiązanie, dlatego Git udostępnia krótszą metodę aby to osiągnąć: składnie z potrójną kropką. W kontekście komendy diff, możesz wstawić trzy kropki po nazwie gałęzi z którą chcesz porównać, aby otrzymać różnice z ostatniej zmiany z gałęzi na której się znajdujesz a wspólnym przodkiem tej drugiej:

$ git diff master...contrib

Ta komenda pokaże zmiany wprowadzone tylko w gałęzi tematycznej, od czasu jej stworzenia. Jest to bardzo użyteczna składnia warta zapamiętania.

Integrowanie otrzymanych zmian

Kiedy zakończysz prace nad zmianami w gałęzi tematycznej i będą one gotowe do włączenia do głównej, pozostaje pytanie w jaki sposób to zrobić. Ponadto, jaki rodzaj przepływu pracy chcesz stosować w swoim projekcie? Masz różne możliwości, opiszemy więc kilka z nich.

Przepływ pracy podczas scalania zmian

Jednym z prostszych przepływów pracy jest scalenie zmian z twoją gałęzią master. W tym scenariuszu, posiadasz gałąź master, która zawiera stabilny kod. Kiedy masz zmiany w jednej z gałęzi tematycznych które wykonałeś, lub ktoś Ci przesłał a Ty je zweryfikowałeś, scalasz je z gałęzią master, usuwasz gałąź i kontynuujesz pracę. Jeżeli mielibyśmy repozytorium ze zmianami w dwóch gałęziach ruby_client oraz php_client (zob. Historia zmian z kilkoma gałęziami tematycznymi.) i mielibyśmy scalić najpierw ruby_client, a w następnej kolejności php_client, to Twoja historia zmian wyglądała by podobnie do Po scaleniu gałęzi tematycznej..

Historia zmian z kilkoma gałęziami tematycznymi.
Figure 73. Historia zmian z kilkoma gałęziami tematycznymi.
Po scaleniu gałęzi tematycznej.
Figure 74. Po scaleniu gałęzi tematycznej.

To jest prawdopodobnie najprostszy schemat pracy, ale jest on również problematyczny jeżeli masz do czynienia z dużymi repozytoriami lub projektami.

Jeżeli masz większą ilość programistów lub większy projekt, będziesz chciał pewnie używał przynajmniej dwufazowego cyklu scalania. W tym scenariuszu, posiadasz funkcjonujące już długo dwie gałęzie master oraz develop, z których master jest aktualizowana tylko z bardzo stabilnymi zmianami, a cały nowy kod jest włączany do gałęzi develop. Regularnie wysyłasz ("push") obie te gałęzie do publicznego repozytorium. Za każdym razem gdy masz nową gałąź tematyczną do zintegrowania (Przed scaleniem gałęzi tematycznej.), włączasz ją do develop (Po scaleniu gałęzi tematycznej.); a kiedy tagujesz kolejną wersję, przesuwasz master za pomocą fast-forward o punktu w którym jest gałąź develop (Po utworzeniu kolejnej wersji.).

Przed scaleniem gałęzi tematycznej.
Figure 75. Przed scaleniem gałęzi tematycznej.
Po scaleniu gałęzi tematycznej.
Figure 76. Po scaleniu gałęzi tematycznej.
Po utworzeniu kolejnej wersji.
Figure 77. Po utworzeniu kolejnej wersji.

W ten sposób, kiedy ludzie klonują Twoje repozytorium, mogą albo pobrać master aby zbudować najnowszą stabilną wersję i utrzymywać ją uaktualnioną, lub mogą pobrać develop która zawiera mniej stabilne zmiany. Możesz rozbudować tą koncepcję, poprzez dodanie gałęzi służącej do integracji. Wtedy jeżeli kod w znajdujący się w niej jest stabilny i przechodzi wszystkie testy, scalasz ją do gałęzi develop; a jeżeli ta okaże się również stabilna, przesuwasz master za pomocą fast-forward.

Przepływ pracy przy scalaniu dużych zmian

Projekt Gita ma cztery długo funkcjonujące już gałęzie: master, next, pu (z ang. proposed updates, czyli proponowane zmiany) dla nowych funkcjonalności, oraz maint do wprowadzania poprawek z nowszej wersji na starszą. Kiedy nowe zmiany są dostarczone przez deweloperów, zbierane są do gałęzi tematycznych w repozytorium opiekuna, w sposób podobny do tego który opisałem (zob. Zarządzanie złożoną serią równoczesnych zmian w gałęziach tematycznych.). W tym momencie, są one weryfikowane i sprawdzane czy mogą być użyte, lub czy nadal wymagają dalszych prac. Jeżeli są gotowe, są włączona do next, a ta gałąź jest wypychana dalej, tak aby każdy mógł wypróbować nowe funkcjonalności.

Zarządzanie złożoną serią równoczesnych zmian w gałęziach tematycznych.
Figure 78. Zarządzanie złożoną serią równoczesnych zmian w gałęziach tematycznych.

Jeżeli funkcjonalność potrzebuje jeszcze kolejnych zmian, są one włączane do gałęzi pu. Kiedy okaże się, że cały kod działa już poprawnie, zmiany są włączane do master oraz przebudowywane włącznie ze zmianami z gałęzi next, które nie znalazły się jeszcze w master. Oznacza to, że master praktycznie zawsze przesuwa się do przodu, next tylko czasami ma zmienianą bazę poprzez "rebase", a pu najczęściej z nich może się przesunąć w innym kierunku

Włączanie gałęzi tematycznych do długo funkcjonujących gałęzi.
Figure 79. Włączanie gałęzi tematycznych do długo funkcjonujących gałęzi.

Z chwilą, gdy gałąź tematycznie zostanie włączona do master, jest usuwana z repozytorium. Projekt Gita ma również gałąź maint, która jest tworzona z ostatniej wersji, w celu dostarczania zmian w sytuacji gdy trzeba wydać wersję poprawkową. Dlatego kopiując repozytorium Gita masz cztery gałęzie, w których możesz zobaczyć projekt w różnych stadiach rozwoju, w zależności od tego jak stabilny kod chcesz używać, lub nad którym pracować; a opiekun ma ułatwiony przepływ zmian pomagający panować nad nowymi zmianami.

Zmiana bazy oraz wybiórcze pobieranie zmian

Część opiekunów woli używać rebase lub cherry-pick w celu włączania zmian w gałęzi master, zamiast przy użyciu merge, aby zachować bardziej liniową historię. Kiedy masz zmiany w gałęzi tematycznej i decydujesz się zintegrować je, przenosisz gałąź i uruchamiasz rebase aby nałożyć zmiany na górze swojej gałęzi master (lub develop, czy innej). Jeżeli to zadziała poprawnie, możesz przesunąć swoją gałąź master i otrzymasz praktycznie liniową historię.

Drugim sposobem na przeniesienie zmian z jednej gałęzi do drugiej jest zrobienie tego za pomocą komendy cherry-pick. Komenda ta jest podobna do rebase, ale dla pojedynczej zmiany. Pobiera ona zmianę która została wprowadzona i próbuje ją ponownie nałożyć na gałąź na której obecnie pracujesz. Jest to całkiem przydatne, w sytuacji gdy masz większą ilość zmian w gałęzi tematycznej, a chcesz zintegrować tylko jedną z nich, lub jeżeli masz tylko jedną zmianę w gałęzi i wolisz używać cherry-pick zamiast rebase. Dla przykładu, załóżmy że masz projekt, który wygląda jak poniżej:

Przykładowa historia przez wybiórczym zaciąganiem zmian.
Figure 80. Przykładowa historia przez wybiórczym zaciąganiem zmian

Jeżeli chcesz pobrać zmianę e43a6 do swojej gałęzi master, możesz uruchomić:

$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

To pobierze tylko zmiany z commita e43a6, ale otrzyma nową sumę SHA-1, ze względu na nową datę nałożenia. Teraz Twoja historia zmian wygląda tak:

Historia po wybiórczym zaciągnięciu zmiany z gałęzi tematycznej.
Figure 81. Historia po wybiórczym zaciągnięciu zmiany z gałęzi tematycznej.

Teraz możesz usunąć swoją gałąź tematyczną oraz zmiany, których nie chciałeś pobierać.

Rerere

If you’re doing lots of merging and rebasing, or you’re maintaining a long-lived topic branch, Git has a feature called "rerere" that can help.

Rerere stands for "reuse recorded resolution" – it’s a way of shortcutting manual conflict resolution. When rerere is enabled, Git will keep a set of pre- and post-images from successful merges, and if it notices that there’s a conflict that looks exactly like one you’ve already fixed, it’ll just use the fix from last time, without bothering you with it.

This feature comes in two parts: a configuration setting and a command. The configuration setting is rerere.enabled, and it’s handy enough to put in your global config:

$ git config --global rerere.enabled true

Now, whenever you do a merge that resolves conflicts, the resolution will be recorded in the cache in case you need it in the future.

If you need to, you can interact with the rerere cache using the git rerere command. When it’s invoked alone, Git checks its database of resolutions and tries to find a match with any current merge conflicts and resolve them (although this is done automatically if rerere.enabled is set to true). There are also subcommands to see what will be recorded, to erase specific resolution from the cache, and to clear the entire cache. We will cover rerere in more detail in Rerere.

Etykietowanie Twoich wydań

Kiedy zdecydowałeś, że wydasz nową wersję, najprawdopodobniej będziesz chciał stworzyć etykietę, tak abyś mógł odtworzyć tą wersję w każdym momencie. Możesz stworzyć nową etykietę, tak jak zostało to opisane w rozdziale Podstawy Gita. Jeżeli zdecydujesz się na utworzenie etykiety jako opiekun, komenda powinna wyglądać podobnie do poniższej:

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

Jeżeli podpisujesz swoje etykiety, możesz mieć problem z dystrybucją swojego publicznego klucza PGP, który został użyty. Można rozwiązać ten problem poprzez dodanie obiektu binarnego (ang. blob) w repozytorium, a następnie stworzenie etykiety kierującej dokładnie na jego zawartość. Aby to zrobić, musisz wybrać klucz za pomocą komendy gpg --list-keys:

$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon <schacon@gmail.com>
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]

Następnie, możesz bezpośrednio zaimportować wybrany klucz do Gita, poprzez eksport i przekazanie go do git hash-object, który zapisuje nowy obiekt binarny i zwraca jego sumę SHA-1:

$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92

Teraz, gdy masz zawartość swojego klucza w Gitcie, możesz utworzyć etykietę wskazującą bezpośrednio na ten klucz, poprzez podanie sumy SHA-1 zwróconej przez hash-object:

$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

Po uruchomieniu git push --tags, etykieta maintainer-pgp-pub zostanie udostępniona dla wszystkich. Jeżeli ktoś chciałby zweryfikować etykietę, może bezpośrednio zaimportować Twój klucz PGP poprzez pobranie zawartości z bazy danych i import do GPG:

$ git show maintainer-pgp-pub | gpg --import

Możesz używać tego klucza do weryfikacji wszystkich podpisanych etykiet. Możesz również dodać do komentarza do etykiety dodatkowe informacje, które będą możliwe do odczytania po uruchomieniu git show <tag> i pozwolą na prostszą weryfikację.

Generowanie numeru wersji

Ponieważ Git nie zwiększa stale numerów, np. v123 lub w podobny sposób, jeżeli chcesz mieć łatwiejszą do używania nazwę dla konkretnej zmiany, możesz uruchomić git describe na commitcie. Git poda Ci nazwę najbliższej etykiety, wraz z ilością zmian, oraz skróconą sumą SHA-1:

$ git describe master
v1.6.2-rc1-20-g8c5b85c

W ten sposób, możesz udostępnić konkretną wersję lub kompilację pod nazwą łatwiejszą do użycia przez ludzi. W rzeczywistości, jeżeli masz Gita zbudowanego ze źródeł pobranych z jego repozytorium, komenda git --version pokaże wynik podobny do powyższego. Jeżeli zamierzasz opisać zmianę, której wprost nadałeś etykietę, pokaże ona nazwę etykiety.

Komenda git describe faworyzuje etykiety stworzone przy użyciu opcji -a lub -s, więc etykiety dotyczące konkretnych wersji powinny być tworzone w ten sposób, jeżeli używasz git describe w celu zapewnienia poprawnych nazw commitów. Możesz również używać tej nazwy do komend checkout lub show, choć polegają one na skróconej wartości SHA-1, mogą więc nie być wiecznie poprawne. Na przykład, projekt jądra Linuksa przeszedł ostatnio z 8 na 10 znaków aby zapewnić unikalność sum SHA-1, więc poprzednie nazwy wygenerowane za pomocą git describe zostały unieważnione.

Przygotowywanie wydania nowej wersji

Teraz chcesz stworzyć nową wersję. Jedną z rzeczy które będziesz musiał zrobić, jest przygotowanie spakowanego archiwum z ostatnią zawartością kodu, dla tych, którzy nie używają Gita. Komenda która to umożliwia to git archive:

$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz

Jeżeli ktoś otworzy spakowany plik, otrzyma ostatnią wersję kodu w podkatalogu z nazwą projektu. Możesz również stworzyć archiwum zip w podobny sposób, dodając parametr --format=zip do git archive:

$ git archive master --prefix='project/' --format=zip > `git describe master`.zip

Masz teraz spakowane pliki projektu w formatach tar i zip, które możesz łatwo wgrać na serwer lub wysłać do ludzi.

Komenda shortlog

Nadszedł czas na wysłanie e-maila do listy mailingowej osób, które chcą wiedzieć co się dzieje w Twoim projekcie. Fajnym sposobem na szybkie uzyskanie czegoś w rodzaju dziennika zmian, co zostało dodane do projektu od ostatniego wydania lub e-maila, jest użycie polecenia git shortlog. Podsumowuje ono wszystkie commity w podanym przez Ciebie zakresie; na przykład, poniższe polecenie daje Ci podsumowanie wszystkich commitów od ostatniego wydania, jeśli Twoje ostatnie wydanie nosiło nazwę v1.0.1:

$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (8):
      Add support for annotated tags to Grit::Tag
      Add packed-refs annotated tag support.
      Add Grit::Commit#to_patch
      Update version and History.txt
      Remove stray `puts`
      Make ls_tree ignore nils

Tom Preston-Werner (4):
      fix dates in history
      dynamic version method
      Version bump to 1.0.2
      Regenerated gemspec for version 1.0.2

Możesz pobrać podsumowanie wszystkich zmian począwszy od wersji v1.0.1 pogrupowanych po autorze, które jest gotowe do wysłania na listę.

scroll-to-top