Częściowo wybranie zatwierdzenia z Git

501

Pracuję nad 2 różnymi gałęziami: wydanie i rozwój .

Zauważyłem, że nadal muszę zintegrować niektóre zmiany, które zostały wprowadzone w gałęzi wydania z powrotem do gałęzi programistycznej .

Problem polega na tym, że nie potrzebuję całego zatwierdzenia, tylko kilka kawałków w niektórych plikach, więc to proste

git cherry-pick bc66559

nie załatwia sprawy.

Kiedy zrobię

git show bc66559

Widzę różnicę, ale tak naprawdę nie znam dobrego sposobu na zastosowanie tego częściowo do mojego bieżącego drzewa roboczego.

oliver
źródło

Odpowiedzi:

792

Najważniejsze, czego tu chcesz, to git add -p( -pto synonim --patch). Zapewnia to interaktywny sposób sprawdzania zawartości, pozwalając ci zdecydować, czy każdy przystojniak powinien wejść, a nawet umożliwiając ręczną edycję łatki, jeśli to konieczne.

Aby użyć go w połączeniu z cherry-pick:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(Podziękowania dla Tima Henigana za przypomnienie mi, że git-cherry-pick ma opcję - no-commit, oraz podziękowania dla Felixa Rabe'a za wskazanie, że musisz zresetować! Jeśli chcesz tylko pozostawić kilka rzeczy poza zatwierdzeniem , możesz użyć git reset <path>...do rozpakowania tylko tych plików).

W add -prazie potrzeby możesz oczywiście podać konkretne ścieżki . Jeśli zaczynasz z plaster można wymienić cherry-pickz apply.


Jeśli naprawdę chcesz git cherry-pick -p <commit>(ta opcja nie istnieje), możesz użyć

git checkout -p <commit>

Spowoduje to różnicowanie bieżącego zatwierdzenia w stosunku do określonego przez Ciebie zatwierdzenia i pozwoli na indywidualne zastosowanie fragmentów z tego pliku różnic. Ta opcja może być bardziej przydatna, jeśli zatwierdzenie, które pobierasz, powoduje konflikty scalania w części zatwierdzenia, którego nie jesteś zainteresowany. (Pamiętaj jednak, że checkoutróżni się od cherry-pick: checkoutstara się <commit>całkowicie cherry-pickzastosować zawartość, stosuje różnicę określony zatwierdzenie od jego rodzica. Oznacza to, że checkoutmoże zastosować więcej niż tylko to zatwierdzenie, które może być więcej niż chcesz.)

Cascabel
źródło
Właściwie, jeśli zastosuję się do porady w git 1.7.5.4, „git add -p” mówi „Bez zmian”, ponieważ wszystko jest już w indeksie. Muszę zrobić „Git Reset HEAD” przed „Git Add” - w jakikolwiek sposób, aby uniknąć tego resetu z jakąś opcją?
Felix Rabe
@FelixRabe: Właściwie jestem zaskoczony, że w pewnym momencie cherry-pick -nnajwyraźniej nie zostawiłem zmian wprowadzonych etapami - konwencja jest na pewno taka, że --no-commitopcje zatrzymują się tuż przed zatwierdzeniem, tj. Ze wszystkimi wprowadzonymi zmianami. Dodam reset do odpowiedzi.
Cascabel
1
@Blixt Oznacza to, że jeśli commity które są na innym oddziale, ale nie swoją aktualną gałąź są ABCDEF, git checkout -p <F>ma nie tylko ci zmiany z F, to dostaje ABCDEF wszystko puree razem i pozwala uporządkować jaka część, którą chcesz . Sprowadzenie tego do niektórych zmian z F jest uciążliwe. Z drugiej strony, git cherry-pick -n <F>dostajesz tylko zmiany z F - a jeśli niektóre z tych konfliktów są sprzeczne, to pomaga ci powiedzieć, abyś mógł dowiedzieć się, jak poprawnie połączyć.
Cascabel
Miałem z tym problemy, gdy zmiana była dodatkiem do pliku. git resetUsunie wystawił pliki i add -ppo prostu powiedzieć „nic do dodania”.
Ian Grainger
36

Zakładając, że zmiany, które chcesz, znajdują się na czele gałęzi, od której chcesz zmiany, użyj polecenia git checkout

dla jednego pliku:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

dla wielu plików po prostu łańcuch:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb
Jay Swain
źródło
5
To kopiuje cały plik: nie tylko zmiany.
Jeremy List
1
Ta odpowiedź, podobnie jak jakakolwiek inna do tej pory tutaj, ma dużą wadę: nie zachowuje oryginalnego autora zmiany, a zamiast tego popełnia twoje nazwisko. Jeśli zmiana jest dobra, kradniesz czyjś kredyt, jeśli zmiana jest zła, podpalasz się. Wygląda na to, że nie ma mowy o git-pick-p, a szkoda, że ​​wciąż go nie ma.
pfalcon,
15

Opierając się na odpowiedzi Mike'a Monkiewicza , możesz również określić jeden lub więcej plików do kasy z dostarczonego sha1 / branch.

git checkout -p bc66559 -- path/to/file.java 

Umożliwi to interaktywne wybieranie zmian, które chcesz zastosować do bieżącej wersji pliku.

Christian.D
źródło
5
Jest to problematyczne, jeśli bieżąca wersja pliku różni się znacznie od tej wersji. Zostaniesz poproszony o wiele nieistotnych zmian (które nie pojawiają się w zatwierdzeniu). Ponadto zmiany, które naprawdę chcesz, mogą pojawić się w „ukrytej formie” jako różnica w stosunku do prądu, a nie pierwotna różnica. Możliwe, że pierwotna różnica, którą chcesz, jest sprzeczna i musi zostać odpowiednio scalona; nie będziesz miał takiej okazji tutaj.
Kaz
1

Jeśli chcesz podać listę plików w wierszu poleceń i załatwić całą sprawę za pomocą pojedynczego polecenia atomowego, spróbuj:

git apply --3way <(git show -- list-of-files)

--3way: Jeśli łatka nie zostanie zastosowana w sposób czysty, Git utworzy konflikt scalania, abyś mógł uruchomić git mergetool. Pominięcie --3wayspowoduje, że Git zrezygnuje z łatek, które nie mają czystego zastosowania.

nyanpasu64
źródło
0

Jeśli „częściowe wybieranie” oznacza „w plikach, wybieranie niektórych zmian, ale odrzucanie innych”, można tego dokonać, wprowadzając git stash:

  1. Zrób pełny wybór wiśni.
  2. git reset HEAD^ aby przekonwertować całe wybrane przez siebie zatwierdzenie na nieskalowane zmiany robocze.
  3. Teraz git stash save --patch: interaktywnie wybierz niechciany materiał do schowania.
  4. Git wycofuje ukryte zmiany z kopii roboczej.
  5. git commit
  6. Wyrzucić zapas niepożądanymi zmianami: git stash drop.

Wskazówka: jeśli nadasz skrytce niechcianych zmian nazwę: git stash save --patch junkto jeśli teraz zapomnisz zrobić (6), później rozpoznasz skrytkę taką, jaka jest.

Kaz
źródło
Jeśli po prostu wybierzesz, a następnie zresetujesz ... nie będziesz miał nic do ukrycia. Jak powiedziano powyżej, musisz albo wykonać wybieranie z opcją --no-commit, albo zresetować - hard HEAD ~. Pamiętaj, że postępujesz negatywnie (wybierając to, czego nie chcesz). Przyjęte powyżej rozwiązanie pozwala na podejście pozytywne lub negatywne.
Pierre-Olivier Vares
0

Służy git format-patchdo wycinania części zatwierdzenia, na których Ci zależy, i git amdo zastosowania w innej gałęzi

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
adrock20
źródło