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.