Połączenie Git z wymuszonym nadpisaniem

107

Mam gałąź o nazwie, demoktórą muszę połączyć z mastergałęzią. Mogę uzyskać pożądany efekt za pomocą następujących poleceń:

git pull origin demo
git checkout master
git pull origin master
git merge demo
git push origin master

Moim jedynym zmartwieniem jest to, że jeśli są jakieś problemy z scalaniem , chcę powiedzieć, gitaby nadpisać zmiany w mastergałęzi bez monitowania o scalenie. Więc zasadniczo zmiany w demogałęzi powinny automatycznie nadpisywać zmiany w mastergałęzi.

Rozejrzałem się, istnieje wiele opcji, ale nie chcę ryzykować z połączeniem.

OpenStack
źródło
git push -f origin master
MD XF
4
@MDXF: Może się mylę, ale nie powinienem używać -fopcji z mergepoleceniem, a nie z pushpoleceniem
OpenStack
Możesz spróbować obu i zobaczyć, co działa w Twoim przypadku
MD XF
Zobacz poniższe łącze, aby
zapoznać się

Odpowiedzi:

107

Niezupełnie związane z tą odpowiedzią, ale porzuciłbym git pull, co po prostu biegnie git fetchza nim git merge. Wykonujesz trzy scalenia, co spowoduje, że Twój Git uruchomi trzy operacje pobierania, podczas gdy jedno pobieranie jest wszystkim, czego potrzebujesz. W związku z tym:

git fetch origin   # update all our origin/* remote-tracking branches

git checkout demo         # if needed -- your example assumes you're on it
git merge origin/demo     # if needed -- see below

git checkout master
git merge origin/master

git merge -X theirs demo   # but see below

git push origin master     # again, see below

Kontrolowanie najtrudniejszych połączeń

Najciekawsza część tutaj jest git merge -X theirs. Jak zauważył root545 , -Xopcje są przekazywane do strategii scalania, a zarówno recursivestrategia domyślna , jak i resolvestrategia alternatywna przyjmują -X ourslub -X theirs(jedna lub druga, ale nie obie). Aby jednak zrozumieć, co robią, musisz wiedzieć, w jaki sposób Git wyszukuje i leczy konflikty .

Konflikt scalania może wystąpić w pewnym pliku 1, gdy wersja podstawowa różni się zarówno od wersji bieżącej (nazywanej również --ourswersją lokalną, HEAD lub ), jak i innej (nazywanej również zdalną lub --theirs) wersją tego samego pliku. Oznacza to, że podczas scalania zidentyfikowano trzy wersje (trzy zatwierdzenia): podstawową, naszą i ich. Wersja „podstawowa” pochodzi z podstawy scalania między naszym zatwierdzeniem a ich zatwierdzeniem, jak można znaleźć na wykresie zatwierdzeń (aby uzyskać więcej informacji na ten temat, zobacz inne wpisy StackOverflow). Git znalazł wtedy dwa zestawy zmian: „co zrobiliśmy” i „co zrobili”. Te zmiany są (na ogół) widoczne wiersz po wierszu, czysto tekstowe . Git nie ma prawdziwego zrozumienia zawartości plików; jest to po prostu porównanie każdego wiersza tekstu.

Te zmiany są tym, co widzisz w git diffwynikach i jak zawsze mają również kontekst . Możliwe, że rzeczy, które zmieniliśmy, znajdują się w innych wierszach niż te, które zmieniły, więc zmiany wydają się nie kolidować, ale kontekst również się zmienił (np. Z powodu naszej zmiany znajdującej się blisko góry lub dołu pliku, aby plik skończył się w naszej wersji, ale w ich dodali też więcej tekstu na górze lub na dole).

Jeśli zmiany się zdarzyć na różnych liniach, na przykład, zmieniamy colorsię colourna linii 17 i zmieniają fredsię barneyna linii 71-to nie ma konfliktu: Git prostu wykonuje zarówno zmian. Jeśli zmiany zachodzą w tych samych wierszach, ale są identyczne , Git pobiera jedną kopię zmiany. Tylko jeśli zmiany są w tych samych wierszach, ale są to różne zmiany lub ten specjalny przypadek interferencji kontekstu, pojawia się konflikt modyfikacji / modyfikacji.

-X oursI -X theirsopcje powiedzieć Git jak rozwiązać ten konflikt, poprzez wskazanie tylko jednego z dwóch zmian: nasza, czy ich. Ponieważ powiedziałeś, że scalasz demo(ich) z master(naszymi) i chcesz zmiany z demo, na pewno chcesz -X theirs.

-XJednak ślepe stosowanie jest niebezpieczne. Tylko dlatego, że nasze zmiany nie kolidowały z linią po linii, nie oznacza, że ​​nasze zmiany w rzeczywistości nie powodują konfliktu! Jeden klasyczny przykład występuje w językach z deklaracjami zmiennych. Podstawowa wersja może zadeklarować nieużywaną zmienną:

int i;

W naszej wersji usuwamy nieużywaną zmienną, aby ostrzeżenie kompilatora zniknęło - aw swojej wersji dodają pętlę kilka wierszy później, używając ijako licznika pętli. Jeśli połączymy te dwie zmiany, wynikowy kod nie będzie się już kompilować. Ta -Xopcja nie jest tutaj pomocna, ponieważ zmiany są w różnych wierszach .

Jeśli masz zestaw testów automatycznych, najważniejszą rzeczą do zrobienia jest uruchomienie testów po scaleniu. Możesz to zrobić po zatwierdzeniu i naprawić później, jeśli to konieczne; lub możesz to zrobić przed--no-commit zatwierdzeniem , dodając do git mergepolecenia. Szczegóły tego wszystkiego pozostawimy innym postom.


1 Możesz również uzyskać konflikty w odniesieniu do operacji „całego pliku”, np. Być może poprawiamy pisownię słowa w pliku (tak, że mamy zmianę), a oni usuwają cały plik (tak, że mają usunąć). Git nie rozwiąże samodzielnie tych konfliktów, niezależnie od -Xargumentów.


Wykonywanie mniejszej liczby połączeń i / lub inteligentniejszych połączeń i / lub używanie rebase

W obu naszych sekwencjach poleceń występują trzy połączenia. Pierwszym jest przeniesienie origin/demodo lokalnego demo(twoje zastosowania, git pullktóre, jeśli twój Git jest bardzo stary, nie zaktualizują się, origin/demoale dadzą ten sam wynik końcowy). Drugi jest doprowadzenie origin/masterdo master.

Nie jest dla mnie jasne, kto aktualizuje demoi / lub master. Jeśli napiszesz swój własny kod we własnej demogałęzi, a inni piszą kod i wypychają go do demogałęzi origin, to scalanie w pierwszym kroku może powodować konflikty lub powodować rzeczywiste scalanie. Najczęściej lepiej jest użyć rebase niż merge, aby połączyć pracę (trzeba przyznać, że to kwestia gustu i opinii). Jeśli tak, możesz użyć git rebasezamiast tego. Z drugiej strony, jeśli nigdy nie zrobić żadnej z własnymi zobowiązuje on demo, nawet nie trzeba się demooddział. Alternatywnie, jeśli chcesz zautomatyzować wiele z tych czynności, ale możesz dokładnie sprawdzić, czy są zatwierdzenia, które ty i inni zrobiliście, możesz użyćgit merge --ff-only origin/demo: to przyspieszy, jeśli to możliwe, i po prostu zakończy się niepowodzeniem, jeśli nie (w którym to momencie możesz sprawdzić dwa zestawy zmian i wybrać rzeczywiste scalenie lub ponowne ustawienie, jeśli to konieczne).demo aby dopasować zaktualizowany,origin/demo

Ta sama logika dotyczy master, choć robisz scalanie na master , więc na pewno nie potrzebujemy master. Jest jednak jeszcze bardziej prawdopodobne, że chciałbyś, aby scalanie nie powiodło się, jeśli nie można go wykonać jako szybkiego przewijania do przodu bez scalania, więc prawdopodobnie powinno tak być git merge --ff-only origin/master.

Powiedzmy, że nigdy nie wykonujesz własnych zatwierdzeń demo. W tym przypadku możemy democałkowicie porzucić nazwę :

git fetch origin   # update origin/*

git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"

git merge -X theirs origin/demo || die "complex merge conflict"

git push origin master

Jeśli robi własne demozobowiązuje gałąź, to nie jest pomocne; równie dobrze możesz zachować istniejące scalanie (ale może dodać w --ff-onlyzależności od tego, jakie zachowanie chcesz) lub przełączyć na wykonanie rebase. Zwróć uwagę, że wszystkie trzy metody mogą się nie powieść: scalanie może się nie powieść z powodu konfliktu, scalanie z --ff-onlymoże nie być w stanie przewinąć do przodu, a ponowne bazowanie może się nie powieść z konfliktem (rebase działa w zasadzie poprzez wybieranie najlepszych zatwierdzeń, które wykorzystuje scalanie maszyneria i stąd może wystąpić konflikt scalania).

torek
źródło
28

Miałem podobny problem, w którym musiałem skutecznie zastąpić dowolny plik, który miał zmiany / konflikty z inną gałęzią.

Rozwiązaniem, które znalazłem, było użycie git merge -s ours branch.

Zwróć uwagę, że opcja jest -si nie -X. -soznacza użycie oursjako strategii łączenia najwyższego poziomu, -Xbyłoby zastosowanie tej oursopcji do recursivestrategii łączenia, co nie jest tym, czego ja (lub my) chcemy w tym przypadku.

Kroki, gdzie oldbranchjest gałąź, którą chcesz nadpisać newbranch.

  • git checkout newbranch sprawdza gałąź, którą chcesz zachować
  • git merge -s ours oldbranch scala w starej gałęzi, ale zachowuje wszystkie nasze pliki.
  • git checkout oldbranch sprawdza gałąź, którą chcesz nadpisać
  • get merge newbranch scala się w nowej gałęzi, nadpisując starą gałąź
Niall Mccormack
źródło
Uwaga: żadne z innych rozwiązań tutaj nie zadziałało, dlatego zamieszczam swoją odpowiedź.
Niall Mccormack
1
Dla mnie to nie zadziałało. Rozwiązał konflikt (rozwiązał konfliktowe pliki), ale plik nie został scalony. I nie można też łączyć.
c-an
Jeśli komuś zdarzy się, że utknie w miejscu, w którym pojawi się monit „Wprowadź wiadomość o zatwierdzeniu, aby wyjaśnić, dlaczego to scalanie jest konieczne”: Wpisz swoją wiadomość, a następnie naciśnij klawisz ESC na klawiaturze, wpisz: wq i naciśnij ENTER, aby wyjść z zachęty.
topherPedersen
dziękuję @NiallMccormack za opublikowanie tej odpowiedzi, bardzo nam to pomogło!
Raphael_S
20

To podejście scalające doda jedno zatwierdzenie, do masterktórego wkleja się wszystko, co jest feature, bez narzekania na konflikty lub inne bzdury.

wprowadź opis obrazu tutaj

Zanim cokolwiek dotkniesz

git stash
git status # if anything shows up here, move it to your desktop

Teraz przygotuj mistrza

git checkout master
git pull # if there is a problem in this step, it is outside the scope of this answer

Ubierzcie się featurewszyscy

git checkout feature
git merge --strategy=ours master

Idź do zabicia

git checkout master
git merge --no-ff feature
William Entriken
źródło
1
git push to finish
Kochchy
git-scm.com/docs/git-merge#Documentation/git-merge.txt-ours. Działało, gdy zatwierdzenia nie łączyły się w czysty sposób. Ale to podejście nie zawsze będzie działać, cytując źródło Changes from the other tree that do not conflict with our side are reflected in the merge result. Nie zadziałało to dla mnie, ponieważ miałem czyste łączenie zatwierdzeń w mojej gałęzi, która była nadpisywana. Czy jest inny sposób?
NiharGht
11

Możesz wypróbować opcję „nasza” w git merge,

git merge branch -X nasz

Ta opcja wymusza automatyczne rozwiązywanie konfliktów porcji poprzez faworyzowanie naszej wersji. Zmiany z innego drzewa, które nie kolidują z naszą stroną, są odzwierciedlane w wyniku scalenia. W przypadku pliku binarnego cała zawartość jest pobierana z naszej strony.

korzeń
źródło
3
Byłoby to cofnięte, ponieważ OP powiedział, że chce, aby demowersja była preferowana, mimo że się z nią łączy master. Jest -X theirsteż, ale nie jest jasne, czy właśnie tego chce OP.
torek
Nie przeczytałeś całej drogi. Twoja notatka opisuje, coours robi się, gdy używasz na zapleczu z -sopcją. Podczas używania oursz -X: odrzuca wszystko, co zrobiło inne drzewo, deklarując, że nasza historia zawiera wszystko, co się w nim wydarzyło. źródło
jimasun
2

Kiedy próbowałem używać -X theirsi innych powiązanych przełączników poleceń, otrzymywałem zatwierdzenie scalania. Prawdopodobnie nie rozumiałem tego poprawnie. Łatwą do zrozumienia alternatywą jest po prostu usunięcie gałęzi, a następnie ponowne jej śledzenie.

git branch -D <branch-name>
git branch --track <branch-name> origin/<branch-name>

To nie jest dokładnie „scalanie”, ale właśnie tego szukałem, kiedy natknąłem się na to pytanie. W moim przypadku chciałem wyciągnąć zmiany ze zdalnej gałęzi, które zostały wymuszone.

Matt
źródło