Jak scalić określone zatwierdzenie w Git

1034

Rozwidliłem gałąź z repozytorium w GitHub i popełniłem coś konkretnego dla mnie. Teraz stwierdziłem, że oryginalne repozytorium miało dobrą funkcję, która była dostępna HEAD.

Chcę scalić to tylko bez wcześniejszych zatwierdzeń. Co powinienem zrobić? Wiem, jak scalić wszystkie zatwierdzenia:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push
woda netto
źródło
Jeśli próbujesz to zrobić w odniesieniu do github, ten artykuł przeprowadzi Cię przez ten proces. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Odpowiedzi:

1173

git cherry-pick” Powinna być odpowiedź tutaj.

Zastosuj zmianę wprowadzoną przez istniejące zatwierdzenie.

Nie zapomnij przeczytać odpowiedzi Bdonlana na temat konsekwencji wybrania cherry w tym poście:
„Wyciągnij wszystkie zatwierdzenia z gałęzi, wypchnij określone zatwierdzenia do innego” , gdzie:

A-----B------C
 \
  \
   D

staje się:

A-----B------C
 \
  \
   D-----C'

Problem z tym zatwierdzeniem polega na tym, że git uważa, że ​​commity uwzględniają całą historię przed nimi

Gdzie C 'ma inny SHA-1identyfikator.
Podobnie, wybranie zatwierdzenia z jednej gałęzi do drugiej w zasadzie polega na wygenerowaniu łatki, a następnie nałożeniu jej, w ten sposób również tracąc historię.

Ta zmiana identyfikatorów zatwierdzania psuje między innymi funkcjonalność gita (chociaż jeśli jest używana oszczędnie, istnieją heurystyki, które to opisują).
Co ważniejsze, ignoruje zależności funkcjonalne - jeśli C faktycznie użył funkcji zdefiniowanej w B, nigdy się nie dowiesz .

VonC
źródło
1
@ openid000: „bardziej drobnoziarniste gałęzie”: to właśnie dokładnie sugerował Bdonlan w swojej odpowiedzi.
VonC
8
Uwaga: „git rebase” zmienia również SHA-1. Zobacz także „git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) i„ git workflow ”( stackoverflow.com/questions/457927/... ) w przypadkach, w których„ git rebase „jest uzasadniony.
VonC
1
Pomiędzy „drobnoziarnistymi gałęziami”, „cherry-pick” i „rebase” będziesz mieć wszystkie możliwości zarządzania kodem w gałęziach za pomocą git.
VonC
@VonC „drobnoziarniste gałęzie” tak. Miałem dwie gałęzie i dokonałem aktualizacji do konkretnego modułu wizualizacji w jednej gałęzi. Ten moduł był niezależny od wszystkich innych kodów, więc cholernie wygodne było zastosowanie tych zmian również w innych gałęziach
Cheeku
1
Ale zauważ, że kiedy się połączysz, zarówno zatwierdzenia C 'jak i C będą obowiązywały w historii zatwierdzeń.
Rahul Shah,
738

Możesz użyć polecenia git cherry-pick, aby zastosować pojedynczy zatwierdzenie do bieżącej gałęzi.

Przykład: git cherry-pick d42c389f

bdonlan
źródło
68
+1 za twój poprzedni post dotyczący zbierania wiśni ( stackoverflow.com/questions/880957/… ). Pozwoliłem sobie skopiować ten fragment we własnej odpowiedzi powyżej.
VonC
4
Prawdopodobnie git cherry-pick d42club git cherry-pick d42c3 zadziała. Git jest mądry. ;)
guneysus
2
fatal: zła wersja
Ievgen Naida
2
Właśnie wykonałem to polecenie i wydaje się, że zadziałało, ale kiedy wykonuję status git, mówi „nic do popełnienia, czyste drzewo pracy”. Kiedy odświeżam stronę Commits na mojej stronie bitbucket, nie pojawia się. Ale pojawia się, gdy uruchamiam git log. I widzę zmodyfikowany kod. Czy ktoś może wyjaśnić, czy muszę wykonać dodatkowe czynności?
Ray
1
Niestety nie odpowiada to na pytanie: W rzeczywistości nie tworzy scalenia. Nie ma przodków wskazujących na d42c389f. Być może OP nie dbał o samo połączenie , ale czasami różnica ma znaczenie.
LarsH
29

Spróbujmy wziąć przykład i zrozumieć:

Mam gałąź, powiedzmy master , wskazującą na X <commit-id> i mam nową gałąź wskazującą na Y <sha1>.

Gdzie Y <commit-id> = <master> oddział zatwierdza - niewiele zatwierdzeń

Powiedzmy teraz, że dla gałęzi Y muszę zamknąć odstępy między gałęzią główną i nową gałęzią. Poniżej znajduje się procedura, którą możemy wykonać:

Krok 1:

git checkout -b local origin/new

gdzie local to nazwa oddziału. Można podać dowolne imię.

Krok 2:

  git merge origin/master --no-ff --stat -v --log=300

Scal zatwierdzenia z gałęzi głównej do nowej gałęzi, a także utwórz zatwierdzenie scalania komunikatu dziennika z opisami jednowierszowymi co najwyżej <n> rzeczywistych zatwierdzeń, które są scalane.

Aby uzyskać więcej informacji i parametrów dotyczących scalania Git, zobacz:

git merge --help

Również jeśli chcesz scalić określone zatwierdzenie, możesz użyć:

git cherry-pick <commit-id>
Spyder
źródło
czy zmieniłeś definicję Yzdania 3d? „Mam nową gałąź wskazującą na Y” vs „Teraz powiedzmy, że dla gałęzi Y”, brzmi to tak, jakby Y było zatwierdzeniem, a potem stało się gałęzią
Purefan
jak mogę to zrobić z pewnego punktu zatwierdzenia oddziału, który chcę scalić
Kulbhushan Singh
3

W moim przypadku mieliśmy podobne zapotrzebowanie na CI CD. Użyliśmy git flow z gałęziami developerskimi i master. Programiści mogą łączyć tam zmiany bezpośrednio w celu opracowania lub poprzez żądanie ściągnięcia z gałęzi funkcji. Jednak, aby opanować, łączymy tylko stabilne zatwierdzenia z gałęzi programistycznej w sposób zautomatyzowany za pośrednictwem Jenkins.

W takim przypadku robienie czereśni nie jest dobrym rozwiązaniem. Jednak tworzymy gałąź lokalną z identyfikatora zatwierdzenia, a następnie łączymy gałąź lokalną z master i wykonujemy mvn clean Verify (używamy maven). Jeśli sukces się powiedzie, wypuść artefakt wersji produkcyjnej do nexusa za pomocą wtyczki maven release z opcją localCheckout = true i pushChanges = false. Wreszcie, gdy wszystko się powiedzie, przesuń zmiany i oznacz do źródła.

Przykładowy fragment kodu:

Zakładając, że jesteś mistrzem, jeśli wykonujesz to ręcznie. Jednak w przypadku Jenkinsa, gdy kasujesz repozytorium, będziesz w domyślnej gałęzi (master, jeśli skonfigurowano).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

To da ci pełną kontrolę dzięki nieustraszonemu połączeniu piekła lub konfliktu.

Nie wahaj się doradzić, jeśli jest jakaś lepsza opcja.

nrkkalyan
źródło
3

Wiodące odpowiedzi opisują, jak zastosować zmiany z konkretnego zatwierdzenia do bieżącej gałęzi. Jeśli to rozumiesz przez „jak połączyć”, po prostu użyj cherry-pick, jak sugerują.

Ale jeśli naprawdę chcesz scalenia , tj. Chcesz nowego zatwierdzenia z dwoma rodzicami - istniejącego zatwierdzenia w bieżącej gałęzi i zatwierdzenia, od którego chciałeś zastosować zmiany - wtedy nie da się tego osiągnąć.

Posiadanie prawdziwej historii scalania może być pożądane, na przykład, jeśli proces kompilacji korzysta z przodków git do automatycznego ustawiania ciągów wersji na podstawie najnowszego znacznika (za pomocą git describe).

Zamiast wybierania „wiśniowego” możesz wykonać rzeczywistą git merge --no-commit, a następnie ręcznie dostosować indeks, aby usunąć wszelkie niepożądane zmiany.

Załóżmy, że jesteś na gałęzi Ai chcesz scalić zatwierdzenie na końcu gałęzi B:

git checkout A
git merge --no-commit B

Teraz masz skonfigurowane zatwierdzenie z dwojgiem rodziców, aktualny typ zatwierdzeń Ai B. Możesz jednak zastosować więcej zmian, niż chcesz, w tym zmiany wcześniejszych zatwierdzeń w gałęzi B. Musisz cofnąć te niechciane zmiany, a następnie zatwierdzić.

(Może istnieć prosty sposób na ustawienie stanu katalogu roboczego i indeksu z powrotem do stanu sprzed scalenia, dzięki czemu masz czyste konto, na którym możesz wybrać zatwierdzenie, które chciałeś.) Nie wiem, jak osiągnąć ten czysty plan. git checkout HEADI git reset HEADoba usunę stan scalenia, pokonując cel tej metody).

Więc ręcznie cofnij niechciane zmiany. Na przykład możesz

git revert --no-commit 012ea56

dla każdego niechcianego zatwierdzenia 012ea56.

Kiedy skończysz dostosowywać, utwórz zatwierdzenie:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Teraz masz tylko pożądaną zmianę, a drzewo przodków pokazuje, że technicznie połączyłeś się z B.

LarsH
źródło