Korzystam z git w nowym projekcie, który ma dwie równoległe - ale obecnie eksperymentalne - gałęzie programistyczne:
master
: import istniejącej bazy kodu oraz kilka modów, których jestem ogólnie pewienexp1
: gałąź eksperymentalna # 1exp2
: gałąź eksperymentalna # 2
exp1
i exp2
reprezentują dwa bardzo różne podejścia architektoniczne. Dopóki nie przejdę dalej, nie mam możliwości dowiedzieć się, który z nich (jeśli którykolwiek) zadziała. Gdy robię postępy w jednej gałęzi, czasami mam zmiany, które byłyby przydatne w drugiej gałęzi i chciałbym scalić tylko te.
Jaki jest najlepszy sposób łączenia selektywnych zmian z jednej gałęzi rozwoju do drugiej, pozostawiając za sobą wszystko inne?
Podejścia, które rozważałem:
git merge --no-commit
po czym następuje ręczne odstawianie dużej liczby zmian, których nie chcę robić wspólnie między gałęziami.Ręczne kopiowanie wspólnych plików do katalogu tymczasowego, a następnie
git checkout
przejście do drugiej gałęzi, a następnie dalsze ręczne kopiowanie z katalogu tymczasowego do drzewa roboczego.Odmiana powyższego. Na razie porzuć
exp
gałęzie i użyj dwóch dodatkowych lokalnych repozytoriów do eksperymentów. Dzięki temu ręczne kopiowanie plików jest znacznie prostsze.
Wszystkie trzy podejścia wydają się nużące i podatne na błędy. Mam nadzieję, że istnieje lepsze podejście; coś podobnego do parametru ścieżki filtru, który uczyniłby git-merge
bardziej selektywnym.
źródło
git merge -s ours --no-commit
następujących po nich niegit read-tree
byłoby dobrym rozwiązaniem? Zobacz stackoverflow.com/questions/1214906/…Odpowiedzi:
Używasz polecenia cherry-pick, aby uzyskać indywidualne zatwierdzenia z jednej gałęzi.
Jeśli żądane zmiany nie dotyczą pojedynczych zatwierdzeń, użyj metody pokazanej tutaj, aby podzielić zatwierdzenia na poszczególne zatwierdzenia . Z grubsza mówiąc, używasz,
git rebase -i
aby uzyskać oryginalne zatwierdzenie do edycji, a następniegit reset HEAD^
selektywnie przywrócić zmiany, a następniegit commit
zatwierdzić ten bit jako nowy zatwierdzenie w historii.W Red Hat Magazine jest jeszcze jedna fajna metoda, w której używają
git add --patch
lub ewentualniegit add --interactive
pozwalają na dodanie tylko części przystawki, jeśli chcesz podzielić różne zmiany na pojedynczy plik (wyszukaj na tej stronie hasło „split”).Po podzieleniu zmian możesz teraz wybrać tylko te, które chcesz.
źródło
Miałem dokładnie taki sam problem jak wspomniany powyżej. Ale znalazłem to wyraźniej w wyjaśnianiu odpowiedź.
Podsumowanie:
Sprawdź ścieżki z gałęzi, którą chcesz scalić,
Wskazówka: Działa to również bez
--
widocznego w połączonym poście.lub do selektywnego łączenia kawałków
Alternatywnie użyj resetowania, a następnie dodaj z opcją
-p
,Wreszcie dokonaj
źródło
foo.c
zrób to,git reset HEAD foo.c
aby rozproszyć ten plik, a następnie możesz go różnicować. Dowiedziałem się tego po wypróbowaniu go igit diff --cached
git checkout -p <revision> -- <path>
będzie to samo, co wydawanie pierwszych trzech poleceń, które opisałeś :)Aby selektywnie scalić pliki z jednej gałęzi do drugiej, uruchom
gdzie
branchX
jest gałąź, z której chcesz się połączyć w bieżącą gałąź.Ta
--no-commit
opcja spowoduje wyodrębnienie plików, które zostały scalone przez Git, bez faktycznego ich zatwierdzania. Dzięki temu będziesz mógł modyfikować scalone pliki w dowolny sposób, a następnie samodzielnie je zatwierdzić.W zależności od sposobu scalenia plików istnieją cztery przypadki:
1) Chcesz prawdziwego scalenia.
W takim przypadku akceptujesz scalone pliki tak, jak Git scalił je automatycznie, a następnie zatwierdzasz.
2) Istnieje kilka plików, których nie chcesz scalać.
Na przykład chcesz zachować wersję w bieżącej gałęzi i zignorować wersję w gałęzi, z której się łączysz.
Aby wybrać wersję w bieżącym oddziale, uruchom:
Spowoduje to pobranie wersji z
file1
bieżącej gałęzi i zastąpieniefile1
automatyzowanego przez Git.3) Jeśli chcesz wersję w branchX (a nie prawdziwe scalenie).
Biegać:
Będzie to pobrać wersję
file1
wbranchX
i nadpisywaniafile1
automatycznie połączone przez Git.4) Ostatni przypadek dotyczy wybrania tylko określonych połączeń
file1
.W takim przypadku możesz edytować zmodyfikowany plik
file1
bezpośrednio, zaktualizować go do dowolnej wersjifile1
, a następnie zatwierdzić.Jeśli Git nie może automatycznie scalić pliku, zgłosi plik jako „nie połączony ” i utworzy kopię, w której będziesz musiał ręcznie rozwiązać konflikty.
Aby wyjaśnić dalej przykładem, powiedzmy, że chcesz połączyć
branchX
się z bieżącą gałęzią:Następnie uruchom
git status
polecenie, aby wyświetlić status zmodyfikowanych plików.Na przykład:
Gdzie
file1
,file2
ifile3
są pliki git mają pomyślnie auto-scalone.Co to oznacza to, że zmiany
master
ibranchX
dla wszystkich tych trzech plików zostały połączone razem bez żadnych konfliktów.Możesz sprawdzić, jak zostało wykonane scalenie, uruchamiając
git diff --cached
;Jeśli uznasz, że scalenie jest niepożądane, możesz to zrobić
git commit
Jeśli nie chcesz scalać
file1
i chcesz zachować wersję w bieżącym oddzialeBiegać
Jeśli nie chcesz scalić
file2
i chcesz tylko wersjębranchX
Biegać
Jeśli chcesz
file3
zostać scalony automatycznie, nie rób nic.W tym momencie Git już to połączył.
file4
powyżej jest nieudane scalenie przez Git. Oznacza to, że nastąpiły zmiany w obu gałęziach, które występują w tej samej linii. W tym miejscu będziesz musiał ręcznie rozwiązać konflikty. Możesz odrzucić scalone wykonane, edytując plik bezpośrednio lub uruchamiając polecenie kasy dla wersji w gałęzi, którą chceszfile4
zostać.Wreszcie, nie zapomnij
git commit
.źródło
git merge --no-commit branchX
jest to tylko przewijanie do przodu, wskaźnik zostanie zaktualizowany, a więc--no-ff
aby zapobiec takiemu zachowaniu?Nie podoba mi się powyższe podejście. Korzystanie z pick-pick jest świetne do wybierania pojedynczej zmiany, ale jest to ból, jeśli chcesz wprowadzić wszystkie zmiany z wyjątkiem niektórych złych. Oto moje podejście.
Nie ma
--interactive
argumentu, który można przekazać, aby połączyć git.Oto alternatywa:
Masz pewne zmiany w „funkcji” gałęzi i chcesz wprowadzić niektóre, ale nie wszystkie z nich, do „master” w nie niechlujny sposób (tzn. Nie chcesz wybierać i zatwierdzać każdego z nich)
Więc po prostu zawiń to w skrypcie powłoki, zmień master na $ na i zmień funkcję na $ z i możesz zacząć:
źródło
git rebase -i $to
nagit rebase -i $to || $SHELL
, aby użytkownik mógł zadzwonićgit --skip
itp., Jeśli to konieczne, jeśli rebase nie powiedzie się. Warto również połączyć łańcuchy razem z&&
zamiast nowych linii.Jest jeszcze jedna droga:
Jest to połączenie pomiędzy
git checkout
igit add -p
może być dokładnie tym, czego szukasz:źródło
Chociaż niektóre z tych odpowiedzi są całkiem dobre, wydaje mi się, że żadna z nich nie odpowiedziała na oryginalne ograniczenie OP: wybieranie określonych plików z poszczególnych gałęzi. To rozwiązanie to robi, ale może być uciążliwe, jeśli istnieje wiele plików.
Powiedzmy, że masz
master
,exp1
iexp2
gałęzie. Chcesz scalić jeden plik z każdej gałęzi eksperymentalnej w master. Zrobiłbym coś takiego:To da ci różnice w plikach dla każdego pliku, który chcesz. Nic więcej. Nic mniej. Przydaje się, że masz radykalnie różne zmiany plików między wersjami - w moim przypadku zmieniając aplikację z Rails 2 na Rails 3.
EDYCJA : spowoduje to scalenie plików, ale nastąpi inteligentne scalenie. Nie byłem w stanie wymyślić, jak użyć tej metody, aby uzyskać informacje o różnicach w pliku (być może nadal będą występować w przypadku skrajnych różnic. Irytujące małe rzeczy, takie jak białe znaki, zostaną ponownie włączone, chyba że skorzystasz z
-s recursive -X ignore-all-space
opcji)źródło
git checkout exp1 path/to/file_a path/to/file_x
git checkout feature <path>/*
aby uzyskać grupy plików.1800 INFORMACJA odpowiedź jest całkowicie poprawna. Jednak jako git noob „użycie git cherry-pick” nie wystarczyło mi, aby to rozgryźć bez większego kopania w Internecie, więc pomyślałem, że opublikuję bardziej szczegółowy przewodnik na wypadek, gdyby ktoś inny podobna łódź.
Moim przypadkiem użycia było wybiórcze ściąganie zmian z czyjejś gałęzi github do mojej. Jeśli masz już oddział lokalny ze zmianami, musisz wykonać tylko kroki 2 i 5-7.
Utwórz (jeśli nie utworzono) lokalny oddział ze zmianami, które chcesz wprowadzić.
$ git branch mybranch <base branch>
Przełącz się na to.
$ git checkout mybranch
Rozwiń potrzebne zmiany z konta drugiej osoby. Jeśli jeszcze tego nie zrobiłeś, będziesz chciał dodać je jako pilota.
$ git remote add repos-w-changes <git url>
Ściągnij wszystko ze swojej gałęzi.
$ git pull repos-w-changes branch-i-want
Przeglądaj dzienniki zatwierdzeń, aby zobaczyć, jakie zmiany chcesz:
$ git log
Wróć do gałęzi, w której chcesz przenieść zmiany.
$ git checkout originalbranch
Cherry wybieraj swoje zobowiązania, jeden po drugim, za pomocą skrótów.
$ git cherry-pick -x hash-of-commit
Porada: http://www.sourcemage.org/Git_Guide
źródło
git cherry
polecenia (najpierw instrukcja), aby zidentyfikować zatwierdzenia, które nie zostały jeszcze scalone.Oto jak możesz zamienić
Myclass.java
plik wmaster
gałęzi naMyclass.java
wfeature1
gałęzi. Będzie działać, nawet jeśliMyclass.java
nie istniejemaster
.Zauważ, że to zastąpi - nie scali - i zignoruje lokalne zmiany w gałęzi master.
źródło
theirs
zastąpienieours
=> +1 Pozdrawiam;)2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Prosty sposób, aby faktycznie połączyć określone pliki z dwóch gałęzi, a nie tylko zastąpić określone pliki plikami z innej gałęzi.
Krok pierwszy: Zróżnicuj gałęzie
git diff branch_b > my_patch_file.patch
Tworzy plik poprawki różnicy między bieżącą gałęzią a gałęzią_b
Krok drugi: Zastosuj poprawkę do plików pasujących do wzorca
git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch
przydatne uwagi na temat opcji
Można użyć
*
jako symbolu wieloznacznego we wzorcu dołączania.Cięcia nie wymagają ucieczki.
Możesz także użyć opcji --exclude i zastosować ją do wszystkiego oprócz plików pasujących do wzorca lub odwrócić łatkę za pomocą opcji -R
Opcja -p1 to pozostałość po komendzie łatki * unix i fakt, że zawartość pliku łatki poprzedza każdą nazwę pliku za pomocą
a/
lubb/
(lub więcej w zależności od tego, jak wygenerowano plik łatki), którą należy usunąć, aby mógł się zorientować prawdziwy plik do ścieżki do pliku, do którego należy zastosować łatkę.Sprawdź stronę podręcznika dla git-Apply, aby uzyskać więcej opcji.
Krok trzeci: nie ma kroku trzeciego
Oczywiście chcesz zatwierdzić swoje zmiany, ale kto powiedział, że nie masz innych powiązanych poprawek, które chcesz zrobić przed dokonaniem zatwierdzenia.
źródło
Oto, w jaki sposób możesz sprawić, aby historia śledziła tylko kilka plików z innej gałęzi przy minimalnym wysiłku, nawet jeśli bardziej „prosta” fuzja przyniosłaby o wiele więcej zmian, których nie chcesz.
Po pierwsze, podejmiesz niezwykły krok, by z góry zadeklarować, że to, co zamierzasz wykonać, to scalenie, bez robienia czegokolwiek z plikami w twoim katalogu roboczym:
. . . gdzie „nazwa gałęzi” oznacza to, z czego chcesz się połączyć. Jeśli miałbyś dokonać od razu, nie wprowadziłby żadnych zmian, ale nadal pokazywałby pochodzenie z drugiej gałęzi. Możesz dodać więcej oddziałów / tagów / itp. do linii poleceń, jeśli potrzebujesz. W tym momencie jednak nie ma żadnych zmian do zatwierdzenia, więc pobierz pliki z innych wersji, następnie.
Jeśli łączysz się z więcej niż jednym innym oddziałem, powtórz w razie potrzeby.
Teraz pliki z drugiej gałęzi są w indeksie, gotowe do zatwierdzenia, z historią.
i będziesz musiał wiele wyjaśnić w tym komunikacie zatwierdzenia.
Pamiętaj jednak, że w przypadku, gdy nie było to jasne, jest to zrujnowane. Nie jest to zgodne z duchem tego, do czego służy „gałąź”, a wybieranie wiśni jest tutaj bardziej uczciwym sposobem na robienie tego, co robisz. Jeśli chcesz wykonać kolejne „scalenie” innych plików w tym samym oddziale, którego nie przyniosłeś ostatnim razem, zatrzyma Cię komunikat „już aktualne”. Jest to objaw braku rozgałęzienia, kiedy powinniśmy mieć, w gałęzi „od” powinna znajdować się więcej niż jedna inna gałąź.
źródło
git merge --no-ff --no-commit -s outs branchname1
) jest dokładnie tym, czego szukałem! Dzięki!Wiem, że jestem trochę spóźniony, ale to jest mój proces roboczy do łączenia wybranych plików.
źródło
Najprościej jest ustawić repo w gałęzi, z którą chcesz się połączyć, a następnie uruchomić,
Jeśli uciekniesz
zobaczysz plik już wyreżyserowany ...
Następnie uruchomić
Prosty.
źródło
Znalazłem ten post zawierający najprostszą odpowiedź. Wystarczy zrobić:
Przykład:
Zobacz post, aby uzyskać więcej informacji.
źródło
Dziwne, że git wciąż nie ma tak wygodnego narzędzia „po wyjęciu z pudełka”. Bardzo go używam, gdy aktualizuję starą gałąź wersji (która wciąż ma wielu użytkowników oprogramowania) tylko przez kilka poprawek z aktualnej gałęzi wersji. W takim przypadku często potrzebne jest szybkie pobranie tylko niektórych wierszy kodu z pliku w bagażniku, ignorując wiele innych zmian (które nie powinny wchodzić w starą wersję) ... I oczywiście interaktywne scalanie trójstronne jest potrzebny w tym przypadku,
git checkout --patch <branch> <file path>
nie nadaje się do tego celu selektywnego scalania.Możesz to zrobić łatwo:
Wystarczy dodać ten wiersz do
[alias]
sekcji w pliku globalnym.gitconfig
lub lokalnym.git/config
:Oznacza to, że używasz Beyond Compare. W razie potrzeby zmień oprogramowanie na wybrane. Możesz też zmienić to na automatyczne łączenie trójstronne, jeśli nie potrzebujesz interaktywnego scalania selektywnego:
Następnie użyj takiego:
To da ci prawdziwą możliwość selektywnego łączenia drzewa dowolnego pliku w innej gałęzi.
źródło
Nie jest to dokładnie to, czego szukałeś, ale było dla mnie przydatne:
Jest to mieszanka niektórych odpowiedzi.
źródło
git checkout HEAD file1
aby zachować bieżącą wersję i rozłączyć plikfile1
, można użyć-p
opcji, aby wybrać część pliku do scalenia. dzięki za podstęp!Zrobiłbym
W ten sposób możesz ograniczyć zakres zatwierdzeń dla pliku z gałęzi.
Skradzione z: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html
źródło
Miałem dokładnie taki sam problem jak wspomniany powyżej. Ale ten blog git wyjaśnił mi odpowiedź.
Polecenie z powyższego linku:
źródło
Podoba mi się odpowiedź „git-interactive-merge” powyżej, ale jest jedna łatwiejsza. Pozwól, aby git zrobił to za Ciebie, używając kombinacji interaktywnej rebase i na:
Tak więc przypadek polega na tym, że chcesz C1 i C2 z gałęzi „cecha” (punkt rozgałęzienia „A”), ale na razie nie ma reszty.
Który, jak wyżej, przenosi cię do interaktywnego edytora, w którym wybierasz linie „pick” dla C1 i C2 (jak wyżej). Zapisz i wyjdź, a następnie przejdzie do rebase i da ci gałąź „temp”, a także HEAD w master + C1 + C2:
Następnie możesz po prostu zaktualizować master do HEAD i usunąć gałąź temp i możesz już iść:
źródło
Co
git reset --soft branch
? Dziwi mnie, że nikt jeszcze o tym nie wspominał.Dla mnie to najłatwiejszy sposób wybiórczego wybierania zmian z innej gałęzi, ponieważ to polecenie umieszcza w moim drzewie roboczym wszystkie zmiany różnic i mogę łatwo wybrać lub przywrócić to, którego potrzebuję. W ten sposób mam pełną kontrolę nad zatwierdzonymi plikami.
źródło
Wiem, że to pytanie jest stare i istnieje wiele innych odpowiedzi, ale napisałem własny skrypt o nazwie „pmerge”, aby częściowo scalić katalogi. To praca w toku i wciąż uczę się skryptów git i bash.
To polecenie używa,
git merge --no-commit
a następnie usuwa zmiany, które nie pasują do podanej ścieżki.Zastosowanie:
git pmerge branch path
Przykład:
git merge develop src/
Nie testowałem tego szeroko. Katalog roboczy powinien być wolny od wszelkich niezatwierdzonych zmian i nieśledzonych plików.
źródło
Możesz użyć
read-tree
do odczytu lub scalenia danego zdalnego drzewa z bieżącym indeksem, na przykład:Aby wykonać scalenie, użyj
-m
zamiast tego.Zobacz także: Jak scalić podkatalog w git?
źródło
Proste podejście do selektywnego łączenia / zatwierdzania według pliku:
git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes
źródło
Jeśli nie masz zbyt wielu plików, które się zmieniły, nie będziesz mieć dodatkowych zobowiązań.
1. Tymczasowo zduplikuj oddział
$ git checkout -b temp_branch
2. Zresetuj do ostatnio poszukiwanego zatwierdzenia
$ git reset --hard HEAD~n
, gdzien
jest liczba zatwierdzeń, które musisz cofnąć3. Pobierz każdy plik z oryginalnego oddziału
$ git checkout origin/original_branch filename.ext
Teraz możesz zatwierdzać i wymuszać push (aby zastąpić zdalnie), jeśli to konieczne.
źródło
Jeśli tylko trzeba scalić konkretny katalog i zostawić wszystko w nienaruszonym stanie i jeszcze zachować historię, można ewentualnie spróbować ... utworzyć nową
target-branch
off zmaster
przed eksperymentem.Poniższe kroki zakładają, że masz dwie gałęzie
target-branch
isource-branch
, a katalogdir-to-merge
, który chcesz scalić, znajduje się wsource-branch
. Załóżmy również, że masz inne katalogi, takie jakdir-to-retain
w celu, których nie chcesz zmieniać i przechowywać historię. Zakłada się również, że występują konflikty scalania wdir-to-merge
.źródło
Kiedy tylko kilka plików zmieniło się między bieżącymi zatwierdzeniami dwóch gałęzi, ręcznie scalam zmiany, przeglądając różne pliki.
git difftoll <branch-1>..<branch-2>
źródło