Jak skopiować tylko niektóre commity z feature brancha do mastera? – polecenie git cherry-pick

Opublikowane przez Tomasz Prasołek w dniu

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:

Historia projektu.

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.

git cherry-pick – Skopiowanie commita z innego brancha

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.

Automatycznie wygenerowany dodatkowy komentarz.

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:unsplash-logofreestocks.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/ […]

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *