GIT i patche: tworzenie i ładowanie

Zdarza się czasem, że potrzebujemy do aplikacji z użyciem GITa dodać jakąś funkcjonalność, jednak z pewnych powodów nie możemy posłużyć się normalnym commitem. Być może nie chcemy komuś przyznać prawa zapisu w repozytorium, lub nakładamy poprawkę na zewnętrzną aplikację, będącą pod kontrolą systemu wersjonowania. W takim przypadku pomocne będzie stworzenie patcha, a następnie załadowanie go do aplikacji.

Tworzenie patcha

Tworzenie patcha w GIT jest niezwykle proste. Powiedzmy, że chcemy wprowadzić pewną zmianę w działającą, zewnętrzną aplikację, a następnie przesłać te zmiany innemu programiście. Na początek powinniśmy stworzyć osobny branch na nasze zmiany:

git checkout -b fix-example master

Powyższe polecenie stworzy nowy branch o nazwie fix-example na podstawie gałęzi master i przełączy się na nią.

Następnie wprowadzamy wymagane poprawki i wykonujemy commit:

git add .
git commit -m "My fixes"

Teraz pozostaje już tylko wygenerowanie patcha z naszymi zmianami:

git format-patch master --stdout > fix-example.patch

Powyższe polecenie wygeneruje plik z wszystkimi zmianami w stosunku do gałęzi master.

Tworzenie patcha dla pojedynczego commita

Jeśli chcemy stworzyć osobne patche dla każdego commita, możemy to zrobić w następujący sposób:

git format-patch -2 0c4b265151e8de7416d16d447d7ac89043c75481

Pierwszy parametr oznacza, ile patchy chcemy stworzyć, a drugi początkowego commita,. Poszczególne pliki zostaną nazwane na podstawie opisu commita, np:

0001-My-fix-example.patch
0002-My-fix-examlple-2.patch

Załadowanie patcha

Ładowanie patcha jest równie proste, co jego tworzenie. Na początek możemy sprawdzić, co nowego wnosi przygotowana poprawka:

git apply --stat fix-example.patch

Wynikiem tego działania będzie lista wszystkich plików wraz z podsumowaniem zmian:

myfile1.php | 4
myfile2.php | 2
2 files changed, 3 insertions(+), 3 deletions(-)

W następnym kroku warto sprawdzić, czy załadowanie patcha uda się przeprowadzić bez konfliktów. W końcu w aplikacji mogły się już pojawić jakieś zmiany dokonane przez inne osoby:

git apply --check fix-example.patch

W przypadku konfliktów, zostaną one wyświetlone na liście, na przykład:

error: patch failed: myfile1.php:29
error: myfile1.php: patch does not apply

Natomiast w przypadku, gdy wszystko będzie możliwe do załadowania, nie powinniśmy zobaczyć żadnego komunikatu. W takim przypadku pozostaje już tylko faktyczne załadowanie naszej poprawki. Można to zrobić przy pomocy dwóch komend: git apply oraz git am. Druga z nich będzie jednak lepsza, gdyż umożliwia podpisanie patcha przez osobę, która go zaaplikowała:

git am --signoff < fix-example.patch

Po zakończeniu operacji zostanie wyświetlona lista commitów, jakie zostały wprowadzone w patchu:

Applying: my fix example

Przeglądając historię, będziemy mogli łatwo odnaleźć informację zarówno o autorze jak i osobie, która ją załadowała do repozytorium:

$ git log
commit 01ed141f907ffafea6f3f0c9947c9b78dba1bb45
Author: Marcin Fliszta <marcin.fliszta@...>
Date: Sat Nov 30 13:13:40 2013 +0100

my fix example

Signed-off-by: Marcin Fliszta <marcin.fliszta@...>

Wycofanie patcha

Gdy podczas ładowania patcha coś pójdzie nie tak jak się spodziewaliśmy, możemy łatwo go wycofać. Tryb rozwiązywania problemów jest podobny do tego, z którym mamy do czynienia podczas konfliktów.

$ git am --signoff < 0001-My-fix-example.patch
Applying: myfile1.php update
error: patch failed: myfile1.php:1
error: myfile1.php: patch does not apply
Patch failed at 0001 myfile1 update
The copy of the patch that failed is found in:
/var/www/patchexample/.git/rebase-apply/patch
When you have resolved this problem, run "git am --resolved".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

W celu wycofania naszego patcha wystarczy zastosować komendę wymienioną na końcu informacji o niepowodzeniu:

git am --abort

Przenoszenie commitów pomiędzy branchami

Jeśli widzisz dodatkowe zastosowanie opisanych tutaj rozwiązań w przypadku przenoszenia wybranych commitów pomiedzy lokalnymi branchami, to nie warto zawracać sobie tym głowy. Do tego celu GIT posiada znacznie lepsze polecenie, jakim jest cherry-pick.

Jego składnia i działanie są banalnie proste i może wyglądać następująco:

git cherry-pick 01ed141f907ffafea6f3f0c9947c9b78dba1bb45

Powyższe wywołanie spowoduje wstawienie wybranego po hashu commita do bieżącego brancha. Nie musimy oczywiście wskazywać z jakiego brancha ma być on załadowany, gdyż hash jest unikalny dla całego repozytorium. Dostępne są oczywiście różne opcje tego polecenia, można zapoznać się z nimi w dokumentacji GITa.

Dodaj komentarz