Jak skopiować tylko niektóre commity z feature brancha do mastera? – polecenie git cherry-pick
Czy zdarzyła Ci się kiedyś taka sytuacja, że siedzisz sobie spokojnie w biurze, poprawiasz różne bugi w projekcie, dodajesz nowe funkcjonalności. Nagle, wchodzi Twój Szef / kierownik / project manager / ktoś inny (niepotrzebne skreślić 🙂 ) i mówi, że zaraz wydajemy wersję i chcemy w niej mieć poprawione bugi X, Y, Z oraz ten feature A.
Okazuje się, że masz to zrobione, ale znajduje się to na Twoim branchu wraz z innymi rzeczami, których jeszcze nie skończyłeś. Co robić? No otwierasz IDE i kopiujesz niezbędny kod na brancha produkcyjnego (develop lub master) i już 🙂 Skopiowanie jest w miarę szybkie, ale jeszcze musisz sprawdzić czy na pewno wszystko skopiowałeś, czy coś nie zostało.
Zajmuje Ci to ok. 30 minut. Wszystko działa, wersja może zostać wystawiona.
Jednak da się to zrobić w sposób szybszy i dużo łatwiejszy. Jak? Polecenie git cherry-pick nam pomoże.
git cherry-pick
To polecenie pozwala Nam skopiować commita (bądź kilka commitów) z jednego brancha na drugi. Nie musimy robić żadnego merge’a, nie musimy robić żadnego rebase’a. Po prostu wskazujemy ID commita i już.
Jawnie żadnego merge’a nie musimy robić, ale w praktyce cherry-pick zachowuje się jak polecenie merge. Jeśli git nie może nałożyć zmian z kopiowanego commita, to dostaniemy oczywiście konflikt, który trzeba rozwiązać samemu.
Przykład
Przygotowałem sobie repozytorium z przykładowym projektem. Projekt zawiera tylko jedną klasę User, jest on bardzo prosty, bo chcę Ci tylko pokazać działanie polecenia cherry-pick.
Mamy klasę User, która wygląda tak na branchu master:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Email Email { get; set; }
}
Mam też drugi branch – feature – na którym robię różne poprawki, poprawiam bugi i zrobiłem również zmianę w klasie User. Klasa ta na branchu feature wygląda następująco:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Email Email { get; set; }
}
Name
zamieniłem na FirstName
i dodałem właściwość LastName
.
A całe repozytorium tak:
Jak to działa?
Teraz następuje podobna sytuacja co we wstępie. Przychodzi kierownik i mówi Nam, że chce wydać wersję aplikacji i chcę w niej mieć już poprawioną klasę User, aby tam były właściwości FirstName
i LastName
. Widać, że to jest środkowy commit na branchu feature. Co zrobić?
git checkout master
git cherry-pick b2864db
Muszę przejść na branch gdzie chcemy skopiować nasz commit i wywołać polecenie cherry-pick z ID tego commita.
I już, commit skopiowany, można wydawać wersję 🙂
Konflikt
Powyższy przypadek był bardzo prosty, obyło się bez żadnego konfliktu. Zobaczmy teraz jak cherry-pick zadziała, gdy podczas kopiowania commita będzie konflikt.
Najpierw cofnę repozytorium do momentu z przed skopiowania commita. Zmodyfikuję klasę User na branchu master. Zmieniłem właściwość Name
na Login
.
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public Email Email { get; set; }
}
Zobaczmy co teraz się stanie przy próbie skopiowania tego samego commita co poprzednio:
$ git cherry-pick b2864db
error: could not apply b2864db... User - add LastName, change Name to FirstName
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
Konflikt powstał dlatego, że zmieniliśmy te samą linię zarówno na branchu master, jak i feature. Klasa User teraz wygląda tak:
public class User
{
public int Id { get; set; }
<<<<<<< HEAD
public string Login { get; set; }
=======
public string FirstName { get; set; }
public string LastName { get; set; }
>>>>>>> b2864db... User - add LastName, change Name to FirstName
public Email Email { get; set; }
}
Git status w bardzo ładny sposób informuje Nas co się dzieje:
You are currently cherry-picking commit b2864db.
(fix conflicts and run "git cherry-pick --continue")
(use "git cherry-pick --abort" to cancel the cherry-pick operation)
Widać, że jest konflikt do rozwiązania 🙂 Po ręcznym rozwiązaniu konfliktu musimy wpisać:
git cherry-pick --continue
Jeśli chcemy przerwać wykonywanie polecenia cherry-pick – bo np. nasz konflikt jest duży i go nie rozwiążemy – wpisujemy:
git cherry-pick --abort
Skopiowanie kilku commitów
W przypadku jeśli musimy wziąć z innego brancha więcej niż jeden commit, nie musimy wywoływać kilka razy polecenia cherry-pick. Można podać mu zakres commitów do wzięcia. UWAGA! Commity muszą zrobione jeden po drugim.
git cherry-pick [commit_id]..[commit_id]
UWAGA! Zakres commitów podajemy od jednego commita wcześniej, niż ten od którego chcemy skopiować.
Przykład
Załóżmy, że chcemy skopiować z brancha feature 3 zaznaczone commity:
Nie wykonujemy polecenia:
git cherry-pick 119a04c..208775b
To Nam skopiuje tylko 2 commity. Aby przenieść 3 zaznaczone musimy wykonać polecenie:
git cherry-pick 4ce909f..208775b
Oczywiście wcześniej musimy przejść na brancha docelowy. Po rozwiązaniu konfliktów i zatwierdzeniu polecenia, branch master wygląda następująco:
Przeniosły się tylko te 3 commity, które wybraliśmy. Commit o hashu 4ce909f
został na branchu feature. Wszystko zadziałało tak jak chcieliśmy 🙂
Dodatkowe opcje
Jeśli chcemy wiedzieć skąd był skopiowany nasz commit, możemy wywołać polecenie
z opcją -x:
git cherry-pick -x 119a04c
Wtedy do commit message zostanie dodana informacja z jakiego commita został stworzony ten commit.
Wady
Jedyną wadą jaką widzę tego rozwiązania jest to, że będę 2 identyczne commity na różnych branchach. Commity będą oczywiście miały różny hash, identyczne będą pod względem zmian w kodzie.
Podsumowanie
Jeśli musimy scalić nasz kod na branch deweloperski, a nie możemy zrobić standardowego merge’a (lub rebase – zależy co kto woli 🙂 ), wtedy trzeba posłużyć się git cherry-pick. Pozwala to na skopiowanie commita z innego brancha w miejsce, gdzie aktualnie się znajdujemy w repozytorium.
To polecenie jest stosowane bardzo rzadko, ale czasem może się bardzo przydać 🙂 Ja do tej pory używałem go ze 2-3 razy w ciągu ostatnich 2 lat pracy.
Źródła:
https://stackoverflow.com/questions/9339429/what-does-cherry-picking-a-commit-with-git-mean
Zdjęcie wykorzystane we wpisie pochodzi z portalu unsplash.com i zostało zrobione przez:freestocks.org
4 Komentarze
Edek · 19 kwietnia 2020 o 23 h 46 min
Fajny artykuł, czytelny, ale brakuje mi jednego – wyjaśnienia, co się stanie w przypadku merge do mastera. Jeśli hash będzie różny to zakładam, że operacja merge z feature brancha do mastera nie powiedzie się, ponieważ w obu branchach będzie ten sam kod pod różnym hashem? Cherry picki psują standardową procedurę mergowania z feature brancha do mastera?
Tomasz Prasołek · 20 kwietnia 2020 o 6 h 25 min
Cherry-pick nic nie zepsuje. Mergowanie do mastera normalnie zadziałała, tylko w historii będziesz miał 2 commity o tym samym commit message i tym samym kodzie 🙂
_haidee_ · 26 czerwca 2023 o 11 h 14 min
dziekuje za rozjaśnienie 😀
URL · 20 października 2020 o 22 h 37 min
… [Trackback]
[…] Read More: poznajgita.pl/jak-skopiowac-tylko-niektore-commity-z-feature-brancha-do-mastera-git-cherry-pick/ […]